<UserControl x:Class="...>
<StackPanel>
<TextBlock Name="TextBlock1" />
<TextBlock Name="TextBlock2" />
<TextBlock Name="TextBlock3" />
...
<TextBlock Name="TextBlock10" />
</StackPanel>
</UserControl>
我们有这样定义的属性:
public string Text1 { get; set; }
public string Text2 { get; set; }
public string Text3 { get; set; }
...
public string Text10 { get; set; }
现在我想将所有这些TextBlock绑定到所有这些属性。显然有多种方法可以做到这一点,我想知道它们的优缺点。让我们列出一张清单:
My first approach was this:
<TextBlock Name="TextBlock1" Text="{Binding Path=Text1, RelativeSource={RelativeSource AncestorType=UserControl}}" />
This works and is fairly simple, but it is a lot of redundant code if I have to type it for all the TextBlocks. In this example we could just copy-paste, but the UserControl could be more complex.
When I googled the problem I found this solution:
<UserControl ... DataContext="{Binding RelativeSource={RelativeSource Self}}"> <TextBlock Name="TextBlock1" Text="{Binding Path=Text1}" />
This looks pretty clean in the xaml, however DataContext could be used otherwise. So if someone uses this UserControl and changes the DataContext, we are screwed.
Another common solution seems to be this:
<UserControl ... x:Name="MyUserControl"> <TextBlock Name="TextBlock1" Text="{Binding Path=Text1, ElementName=MyUserControl}" />
This has the same problem as 2. though, Name could be set from somewhere else.
What if we write our own MarkupExtension?
public class UserControlBindingExtension : MarkupExtension { public UserControlBindingExtension() { } public UserControlBindingExtension(string path) { this.Path = path; } private Binding binding = null; private string path; [ConstructorArgument("path")] public string Path { get { return path; } set { this.path = value; binding = new Binding(this.path); binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(UserControl), 1); } } public override object ProvideValue(IServiceProvider serviceProvider) { if(binding == null) return null; return binding.ProvideValue(serviceProvider); } }
Now we can do something like this:
<UserControl ... xmlns:self="clr-namespace:MyProject"> <TextBlock Name="TextBlock1" Text="{self:UserControlBinding Path=Text1}"
Neat! But I'm not conviced if my implementation is bulletproof and I would prefer not writing my own MarkupExtension.
Similar to 4. we could do this:
public class UserControlBindingHelper : MarkupExtension { public UserControlBindingHelper() { } public UserControlBindingHelper(Binding binding) { this.Binding = binding; } private Binding binding; [ConstructorArgument("binding")] public Binding Binding { get { return binding; } set { binding = value; binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(UserControl), 1); } } public override object ProvideValue(IServiceProvider serviceProvider) { if (binding == null) return null; return binding.ProvideValue(serviceProvider); } }
Which would result in code like this:
<TextBlock Name="TextBlock1" Text="{self:UserControlBindingHelper {Binding Text1}}" />
We could do it in code!
private void setBindingToUserControl(FrameworkElement element, DependencyProperty dp, string path) { Binding binding = new Binding(path); binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(StateBar), 1); element.SetBinding(dp, binding); }
And then we do this:
setBindingToUserControl(this.TextBlock1, TextBlock.Text, "Text1");
Fairly nice, but it makes the xaml harder to read, since the information about the bindings is missing now.
What other good/interesting options are there?
那么在什么情况下应该选择哪种方法?我是否犯了错误?
具体问题如下:
所有这些方法都正确吗?有些方法优于其他方法吗?
RelativeSource AncestorType
绑定。 - Clemens