c# – 如何绑定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-).我们列一个清单:

>我的第一个方法是:

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

这工作并且相当简单,但如果我必须为所有TextBlocks键入它,则会有很多冗余代码.在这个例子中,我们可以只复制粘贴,但UserControl可能更复杂.
>当我用Google搜索问题时,我发现了这个解决方案:

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

这在xaml中看起来很干净,但是否则可以使用DataContext.因此,如果有人使用此UserControl并更改DataContext,我们就会搞砸了.
>另一个常见的解决方案似乎是这样的:

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

这与2的问题相同.但是,Name可以从其他地方设置.
>如果我们编写自己的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.
>类似于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}}" />

>我们可以在代码中完成它!

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更难阅读,因为现在缺少有关绑定的信息.
>还有哪些其他有趣/有趣的选择?

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

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

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

点赞