首页 > 解决方案 > 如何防止 UserControl 破坏 ElementName 绑定

问题描述

编辑:
创建控件的正确方法是如何避免ElementName 绑定出现以下问题:

    <TextBox x:Name="MyTextBox" Text="some Text"></TextBox>
    <Label>
        <!--Binding works-->
        <TextBlock Text="{Binding Path=Text, ElementName=MyTextBox, FallbackValue='Binding Failed'}"></TextBlock>
    </Label>
    <Button>
        <!--Binding works-->
        <TextBlock Text="{Binding Path=Text, ElementName=MyTextBox, FallbackValue='Binding Failed'}"></TextBlock>
    </Button>
    <local:MyUserControl>
        <!-- THIS BINDING FAILS !!!-->
        <TextBlock Text="{Binding Path=Text, ElementName=MyTextBox, FallbackValue='Binding Failed'}"></TextBlock>
    </local:MyUserControl>

MyUserControl.xaml:

<UserControl x:Class="Problem.MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" />

MyUserControl.xaml.cs

public partial class MyUserControl : UserControl
{
    public MyUserControl()
    {
        InitializeComponent();
    }
}

原文: 我还是 WPF 的新手,无法找到正确的方法。我基本上希望 UserControl 的子项保留能够绑定到 XAML 中根元素的 x:Name 的行为。

这是一个示例,显示了与 WPF 控件相比,由我的 UserControl 描述符引起的问题:

<Parent x:Name="_thisParent">
...
    <Label>
        <!--  Binding to _thisParent works  -->
        <TextBlock Text="{Binding Path=MyText, ElementName=_thisParent}" />
    </Label>
    <uc:Descriptor Text="description: ">
        <!--  Binding to _thisParent FAILS !!  -->
        <TextBlock Text="{Binding Path=MyText, ElementName=_thisParent}" />
    </uc:Descriptor>
    <Button>
        <!--  Binding to _thisParent works  -->
        <TextBlock Text="{Binding Path=MyText, ElementName=_thisParent}" />
    </Button>

这是我的用户控件的代码:

描述符.xaml

<UserControl
x:Class="EmbedContent.UserControls.Descriptor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="_thisDescriptor">
<UserControl.Template>
    <ControlTemplate>
        <DockPanel>
            <TextBlock DockPanel.Dock="Left" Text="{Binding Path=Text, ElementName=_thisDescriptor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, FallbackValue='Binding Failed'}" />
            <ContentPresenter Content="{Binding Path=Content, ElementName=_thisDescriptor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, FallbackValue='Binding Failed'}" />
        </DockPanel>
    </ControlTemplate>
</UserControl.Template>

描述符.xaml.cs

public partial class Descriptor : UserControl
{

    #region Ctor
    public Descriptor()
    {
        InitializeComponent();
    }
    #endregion



    #region Dependency-Properties
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(Descriptor), new PropertyMetadata("Descriptor's default Text"));
    #endregion

我将如何需要实现自定义 UserControl / ContentControl(如果需要没有 xaml)来保留 WPF 控件的行为?
无论如何,这是如何根据最佳实践完成的?我假设我只是遇到这个问题,因为我做错了什么。

标签: c#wpfxaml

解决方案


我终于找到了一个解决方案,但它看起来仍然有些难看,所以我希望有人能发布一个更好的解决方案。我在这里发布代码,包括一个再次突出显示问题的示例。

解决方案:
将 UserControl / ContentControl 拆分为继承自例如 ContentControl 的 .cs 类,并设置描述 App.xaml 资源中额外元素的模板。基本上将 .xaml 部分作为样式移动到资源中。

描述符.cs

namespace EmbedContent.UserControls
{
class Descriptor : ContentControl{
    #region Ctor
    public Descriptor() : base() {}
    #endregion

    #region Dependency-Properties
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(Descriptor), new PropertyMetadata("Descriptor's default Text"));
    #endregion
}
}

应用程序.xaml

<Application
x:Class="EmbedContent.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:EmbedContent"
xmlns:uc="clr-namespace:EmbedContent.UserControls"
StartupUri="MainWindow.xaml">
<Application.Resources>
    <Style TargetType="uc:Descriptor">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="uc:Descriptor">
                    <DockPanel>
                        <TextBlock DockPanel.Dock="Left" Text="{Binding Path=Text, RelativeSource={RelativeSource AncestorType=uc:Descriptor}}" />
                        <ContentPresenter />
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Application.Resources>

上面可以调用和使用描述符,因为我期望它来自 WPF 控件。这里是 UserControl 的代码,它在使用时会破坏绑定:

描述符二.xaml

<UserControl
x:Class="EmbedContent.UserControls.DescriptorTwo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="_thisDescriptorTwo">
<UserControl.Template>
    <ControlTemplate>
        <DockPanel>
            <TextBlock DockPanel.Dock="Left" Text="{Binding Path=Text, ElementName=_thisDescriptorTwo}" />
            <ContentPresenter Content="{Binding Path=Content, ElementName=_thisDescriptorTwo}" />
        </DockPanel>
    </ControlTemplate>
</UserControl.Template>

描述符Two.xaml.cs

namespace EmbedContent.UserControls
{
    public partial class DescriptorTwo : UserControl
    {

        #region Ctor
        public DescriptorTwo()
        {
            InitializeComponent();
        }
        #endregion


        #region Dependency-Properties
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(DescriptorTwo), new PropertyMetadata("Descriptor's default Text"));
        #endregion
    }
}

这里是如何从另一个 UserControl 调用两者的示例

Example.xaml

<UserControl
x:Class="EmbedContent.UserControls.EmbedContentExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:EmbedContent.UserControls"
x:Name="parent">
<StackPanel>
    <Label>
        <!--  Binding to parent works  -->
        <TextBlock Text="{Binding Path=MyText, ElementName=parent}" />
    </Label>
    <uc:Descriptor Text="description: ">
        <!--  Binding to parent works  -->
        <TextBlock Text="{Binding Path=MyText, ElementName=parent}" />
    </uc:Descriptor>
    <uc:DescriptorTwo Text="description: ">
        <!--  Binding to parent fails  -->
        <TextBlock Text="{Binding Path=MyText, ElementName=parent}" />
    </uc:DescriptorTwo>
</StackPanel>

和代码隐藏

示例.xaml.cs

namespace EmbedContent.UserControls
{
    public partial class EmbedContentExample : UserControl
    {
        public EmbedContentExample()
        {
            InitializeComponent();
        }

        public string MyText
        {
            get { return (string)GetValue(MyTextProperty); }
            set { SetValue(MyTextProperty, value); }
        }

        public static readonly DependencyProperty MyTextProperty =
            DependencyProperty.Register("MyText", typeof(string), typeof(EmbedContentExample), new PropertyMetadata("EmbedContentExample's MyText"));
    }
}

推荐阅读