-
Notifications
You must be signed in to change notification settings - Fork 693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
x:Bind outside data templates is extremely inconsistent #2508
Comments
@fabiant3 For FYI |
This almost looks like it shouldn't work at all. The I know with old-style |
{Binding} and ElementName will walk up the element tree at runtime. IIRC x:Bind will statically search up the naming scopes -- the DataTemplate then the x:Class. |
@jamesc I actually believe this was in fact supported, in particular because VS even has dedicated warnings for this. Like, if your
As in, it looks like the system is perfectly aware of the possibility of binding outside of a data template, as @MikeHillberg said. I'd also argue this feature is extremely useful in many cases. @MikeHillberg is it safe to say then that the issue is just that using anything other than standard |
Yes, it should work. I expect some of the code for the (1903) feature went into a not-common code path, so missed the function binding case. |
Thanks Mike for digging into this - yes, x:Bind'ing to named elements outside of the scope is an intentional feature. This is meant to be similar to the ElementName feature in {Binding}, allowing for binding to named elements outside of the current namescope. But unlike {Binding} which walks the visual tree at runtime to perform the name lookup, x:Bind walks the "markup tree" at compile time so it can perform compile time validation. You could see behavior differences when those don't align, like when using a Binding/x:Bind in a template from a ResourceDictionary. The Binding walks the visual tree from where the template is used, whereas x:Bind walks the markup tree from where the template is defined.
Correct, these should work |
I believe this is still an issue; is there any indication on when we can use Imagine I have a With Pseudocode, only to explain the use case of wanting to access data outside the <UserControl Class="local:CarsList">
<UserControl.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<DataTemplate x:Key="listItemTemplate" x:DataType:"models.Car">
<Grid>
<TextBlock Text="{x:Bind Brand}"
Visibility="{x:Bind IsEditing, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=True, Mode=OneWay}" />
<TextBox Text="{x:Bind Brand}"
Visibility="{x:Bind IsEditing, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
</Grid>
</DataTemplate x:Key="listItemTemplate">
</UserControl.Resources>
</UserControl> Doing the above will result in Workaround is giving the usercontrol an <UserControl Class="local:CarsList" x:Name="workaroundNameForControl">
<UserControl.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<DataTemplate x:Key="listItemTemplate" x:DataType:"models.Car">
<Grid>
<TextBlock Text="{x:Bind Brand}"
Visibility="{Binding Path=IsEditing, ElementName=workaroundNameForControl, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=True, Mode=OneWay}" />
<TextBox Text="{x:Bind Brand}"
Visibility="{Binding Path=IsEditing, ElementName=workaroundNameForControl, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
</Grid>
</DataTemplate x:Key="listItemTemplate">
</UserControl.Resources>
</UserControl> Other workarounds for this case would be using a |
Unfortunately, I don't believe we can investigate this before the official WinUI 3 release in 2021. For your scenario in particular, you could try adding an |
Thanks for thinking along! |
@hansmbakker does using |
@stevenbrix Considering that I'm also struggling with this, and this is very important. Now that we have functions in Conversion and other functions can be specified static and called as such (not nice but definitely a workaround) but properties of the control can't. Naming the control doesn't work, either, just as Hans pointed out above. And while the OP's solution might be OK for MVVMWHATEVERTHECURRENTBUZZWORDIS, it doesn't work for a plain user control: if we refer to the control itself this way, there will be complaints about IType_BindingsScopeConnector. Basically, with this trick, you can reach out to two data elements (template data context and control data context) but not the control itself as the second. |
any update? |
Hey all, we're still heads-down on major feature work/bug fixes for WinUI and Project Reunion, so there likely won't be any movement on this for several months. Thanks! |
@MikeHillberg @Sergio0694 what if in nested DataTemplate: |
@Sergio0694 not work either. |
not work, see above. |
after change the generated code, it compiles. @RealTommyKlein |
Seems related to #2237 as well? My latest trick is to do something like this: <Page x:Name="ThisPage"...>
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:MyType">
<Button Command="{Binding ViewModel.SaveCommand, ElementName=ThisPage}" CommandParameter="{x:Bind (local:MyType)}"/> But x:Bind should just let us break into the different scopes we need:
I think that'd cover the majority of scenarios I ever need when dealing with templating. The compiler should just be able to walk the scope up if it's not finding the reference path at each tier. |
@michael-hawker your latest trick doesn't work for ItemsRepeater, although it does for ListView/ItemsControl. That is mind blowing! I really want to have at least some workaround, but I can't, unless I switch from ItemsRepeater to ListView/ItemsControl and only then can switch to use Binding? Crazy! Something is wrong here even with Binding. Please look into it! |
For me it actually worked only by giving a name to my UserControl (which is used in a ListView), and then replacing x:Bind with Binding. They should really fix this. This works:
This works but it fails eventually to evaluate IsReady when it's true and the control remains hidden only for certain items in the list.
|
This also comes up for ItemsPanelTemplates, x:Bind isn't usable in those, which is frustrating. Sometimes you want to configure your layout panel change for ItemsControl based on other properties. Instead trying to bind to a page property gets you: Related SO post as well: https://stackoverflow.com/questions/11307531/binding-inside-the-itemspaneltemplate-from-parent-in-windows-phone |
Also found myself needing this the other day in the Store. I just couldn't for the love of me get this to work, as the XAML compiler would just refuse to compile code with a binding to something outside of a template, even if visible in XAML. Had to come up with some proxy object to bind to to, as I just couldn't get a normal binding to the control's property directly to work. A fix for this would really, really be welcome 😄 |
@Sergio0694 Find Ancestor extension in the Toolkit? https://docs.microsoft.com/windows/communitytoolkit/extensions/frameworkelementextensions#ancestortype |
I mean, yes, but I wanted a statically-typed and reflection-free solution, that's why |
Not sure whether this is a regression and if so, when it was introduced, but now I can't even get the only scenario that was working back when I first opened this issue to work at all anymore. Even just binding to the top viewmodel in a page fails to build. This is a pretty big issue because it blocks many scenarios where you need a backlink to a parent viewmodel for shared functionality (eg. for commands), and the only solution is to either use a static resource (which is only applicable in very few and specific cases), or having to add extra wrapping models just for that, which adds overhead. This is affecting other internal partners as well, and has been broken for years, with no ETA for a fix at all either. I'm pretty sure it's also broken on WinUI 3 too 😥 |
noone cares |
There is one workaround. You have to create a kinda display class. Just for the purpose of the example. Code behind [INotifyPropertyChanged]
public partial class FooViewModel {
[ObserveableProperty] private List<FixedFooItem> _fooItems = new();
private void Whenever() {
List<FooItem> items = ...;
for(FooItem item in items) {
_fooItems.Add(item, DoSomething)
}
}
[RelayCommand]
private void DoSomething(FooItem item) {
//..
}
}
[INotifyPropertyChanged]
public partial class FooItem {
[ObserveableProperty] private string _name { get; set; }
}
[INotifyPropertyChanged]
public partial class FixedFooItem {
[ObserveableProperty] private FooItem _data;
public IRelayCommand DoSomethingCommand { get;set; }
public FixedFooItem(FooItem item, IRelayCommand doSomethingCommand)
{
_data = item;
DoSomethingCommand = doSomethingCommand;
}
} View <ScrollViewer IsVerticalScrollChainingEnabled="True">
<ItemsRepeater ItemsSource="{x:Bind ViewModel.FooItems, Mode=OneWay}">
<ItemsRepeater.Layout>
<UniformGridLayout MinItemWidth="100" MinItemHeight="100" MinRowSpacing="12" MinColumnSpacing="12"/>
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="local:FixedFooItems">
<Button Content="{x:Bind Data.Name}" Command="{x:Bind DoSomethingCommand}" CommandParameter="{x:Bind Data}"/>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</ScrollViewer> I hope there is coming a fix anytime soon. |
Unrelated, but want to call this out: @LeonSpors I'd strongly recommended not to use |
Any updates so far? |
Bumping this (see #8638). |
@duncanmacmichael any update? |
They said they are working on x:Bind improvements for the 1.6 version of WinAppSdk, let's hope this will be part of the update. |
Will call out that this type of issue will prevent adoption of AoT as the fallback is to do things with regular |
Here's another scenario we have currently which relies on Binding, which means we can't migrate this to be AOT compatible: <DataTemplate x:DataType="local:TemplateInformation">
<StackPanel>
<TextBox Name="CodeValidator"
ui:TextBoxExtensions.Regex="{x:Bind Regex, Mode=OneWay}"
Header="{x:Bind Header, Mode=OneWay}"
PlaceholderText="{x:Bind PlaceholderText, Mode=OneWay}" />
<TextBlock Text="Thanks for entering a valid code!"
Visibility="{Binding (ui:TextBoxExtensions.IsValid), ElementName=CodeValidator}" />
</StackPanel>
</DataTemplate> We should be able to just do: <TextBlock Text="Thanks for entering a valid code!"
Visibility="{x:Bind CodeValidator.(ui:TextBoxExtensions.IsValid)}" /> Like we could if we were outside the data template, but within the template, CodeValidator can't be found. |
Does this not work? <TextBlock
Text="Thanks for entering a valid code!"
Visibility="{x:Bind ui:TextBoxExtensions.IsValid(CodeValidator), Mode=OneWay}" /> That should work fine, it's just a normal binding to function 🤔 |
|
Confirmed. |
Describe the bug
Apparently it is possible to use
x:Bind
to bind to a field in code behind outside a data template. Basically, escaping from the data template in use. Not entirely sure whether this is by design or just a lucky quirk of the XAML compiler and codegen (especially since this is not mentioned anywhere in the docs), but it works.As a code example:
You can see how the binding is escaping the data template and just targeting a field in code behind.
The problem with this is that it's extremely fragile and inconsistent:
x:Bind
to a property outside the data template works fine ✅x:Bind
to that same property but with function binding fails to build ❌x:Bind
to that same property from a visual state setter crashes at runtime ❌x:Bind
to that same property with a converter crashes at runtime ❌Steps to reproduce the bug
Steps to reproduce the behavior:
Expected behavior
🤷♂️ 🤷♂️ 🤷♂️
Actual behavior
The binding to the outside field (the view model) works just fine
Screenshots
cc. @MikeHillberg
The text was updated successfully, but these errors were encountered: