繁体   English   中英

如何绑定UserControl中的多个属性

[英]How to bind multiple properties in a UserControl

假设我们有一个像这样的UserControl:

<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; }

并且知道我想将所有这些TextBlocks绑定到所有这些属性。 显然有多种方法可以做到这一点,我想知道不同方法的优点(dis-)。 我们列一个清单:

  1. 我的第一个方法是:

     <TextBlock Name="TextBlock1" Text="{Binding Path=Text1, RelativeSource={RelativeSource AncestorType=UserControl}}" /> 

    这工作并且相当简单,但如果我必须为所有TextBlocks键入它,则会有很多冗余代码。 在这个例子中,我们可以只复制粘贴,但UserControl可能更复杂。

  2. 当我用Google搜索问题时,我发现了这个解决方案:

     <UserControl ... DataContext="{Binding RelativeSource={RelativeSource Self}}"> <TextBlock Name="TextBlock1" Text="{Binding Path=Text1}" /> 

    这在xaml中看起来很干净,但是否则可以使用DataContext。 因此,如果有人使用此UserControl并更改DataContext,我们就会搞砸了。

  3. 另一个常见的解决方案似乎是:

     <UserControl ... x:Name="MyUserControl"> <TextBlock Name="TextBlock1" Text="{Binding Path=Text1, ElementName=MyUserControl}" /> 

    这与2的问题相同。但是,Name可以从其他地方设置。

  4. 如果我们编写自己的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); } } 

    现在我们可以这样做:

     <UserControl ... xmlns:self="clr-namespace:MyProject"> <TextBlock Name="TextBlock1" Text="{self:UserControlBinding Path=Text1}" 

    整齐! 但是,如果我的实现是防弹的,我不相信我不想写自己的MarkupExtension。

  5. 与4.类似,我们可以这样做:

     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); } } 

    这会产生如下代码:

     <TextBlock Name="TextBlock1" Text="{self:UserControlBindingHelper {Binding Text1}}" /> 
  6. 我们可以在代码中完成它!

     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); } 

    然后我们这样做:

     setBindingToUserControl(this.TextBlock1, TextBlock.Text, "Text1"); 

    相当不错,但它使得xaml更难阅读,因为现在缺少有关绑定的信息。

  7. 还有哪些好的/有趣的选择?

那么在哪种情况下走的路是什么? 我在某个地方犯了错误吗?
要指定问题:
这些都是正确的吗? 有些优于其他人吗?

看起来你已经在这里做了很多好的测试! 正如您可能已经注意到的那样,您的大部分工作方式都有效,但我建议您使用第一种方法。 虽然它可能看起来有点重复,但它非常明确且维护起来相当简单。 它还使任何不太熟悉xaml的人都可以轻松阅读您的代码。

您已经知道解决方案2和3的问题。创建自己的标记扩展有点矫枉过正(至少在这种情况下)并且使您的代码更难理解。 解决方案6工作正常但正如您所说,它使得无法知道文本块在xaml中绑定的内容。

只需使用#2,并假设DataContext是正确的。 这是标准。 使用用户控件的人员有责任确保正确设置DataContext。

其他任何东西只会增加不必要的复杂性。

请参阅ReSharper WPF错误:“由于未知的DataContext,无法解析符号”MyVariable“

对于任何使用用户控件将DataContext设置为RelativeSource Self人来说,这是常见的做法,请参阅如何绑定到RelativeSource Self?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM