首页 > 解决方案 > WPF UserControl DependencyProperty 在 XAML 中使用或通过绑定

问题描述

我想要一个通用的 UserControl,我可以通过直接在 XAML 中设置其值或将其绑定到某个模型属性来设置属性。

就像 TextBlock Text 属性一样。

现在我只有一个简单的 UserControl,它有一个 DependencyProperty TxT 和一个绑定到它的 TextBlock Text 属性。没有其他代码存在。

如果我在主窗口的 XAML 中设置 TxT,它将不起作用,绑定有效。

如果我将 PropertyChangedCallback 添加到该 DependencyProperty 它也适用于 XAML。

所以问题是,如果我希望能够直接在 XAML 中设置每个属性,是否必须为每个属性设置 PropertyChangedCallback?

我不清楚,大多数人都没有提到它,但这也迫使我添加内部控件名称以更改它们在 PropertyChangedCallback 中的值。

代码如下。

可以通过其他方式完成吗?

主窗口

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfAppDpBare" xmlns:Model="clr-namespace:WpfAppDpBare.Model" x:Class="WpfAppDpBare.MainWindow"
    Background="CadetBlue"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.DataContext>
    <Model:MainModel/>
</Window.DataContext>
<Grid>
    <local:UserControlSample TxT="DIRECT TXT" HorizontalAlignment="Center" VerticalAlignment="Center" Height="125" Width="125" Margin="10,34,659,262"/>
    <TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Direct" VerticalAlignment="Top" FontSize="14" FontWeight="Bold"/>
    <TextBlock HorizontalAlignment="Left" Margin="203,10,0,0" TextWrapping="Wrap" Text="Binding" VerticalAlignment="Top" FontSize="14" FontWeight="Bold"/>
    <local:UserControlSample DataContext="{Binding UCData}" HorizontalAlignment="Center" VerticalAlignment="Center" Height="125" Width="125" Margin="203,34,466,262"/>
</Grid>

public partial class MainWindow:Window {
    public MainWindow() {
        InitializeComponent();
        }
    }

用户控制

<UserControl x:Class="WpfAppDpBare.UserControlSample"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:WpfAppDpBare"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800" Background="White">
<Grid>
    <TextBlock TextWrapping="Wrap" Text="{Binding TxT,FallbackValue=...,TargetNullValue=...}" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20" FontWeight="Bold"/>

</Grid>

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

    public string TxT {
        get { return (string)GetValue(TxTProperty); }
        set { SetValue(TxTProperty, value); }
        }

    // Using a DependencyProperty as the backing store for TxT.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TxTProperty =
        DependencyProperty.Register("TxT", typeof(string), typeof(UserControlSample), new PropertyMetadata());

    }

楷模

  public class MainModel:ViewModelBase {

    /// <summary>
    /// The <see cref="UCData" /> property's name.
    /// </summary>
    public const string UCDataPropertyName = "UCData";

    private UCModel uCModel = null;

    /// <summary>
    /// Sets and gets the UCData property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public UCModel UCData {
        get {
            return uCModel;
            }

        set {
            if(uCModel == value) {
                return;
                }

            uCModel = value;
            RaisePropertyChanged(UCDataPropertyName);
            }
        }

    public MainModel() {
        UCData = new UCModel() { TxT = "BINDING TXT" };
        }

    }

public class UCModel:ViewModelBase {

    /// <summary>
    /// The <see cref="TxT" /> property's name.
    /// </summary>
    public const string TxTPropertyName = "TxT";

    private string _TxT = null;

    /// <summary>
    /// Sets and gets the TxT property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public string TxT {
        get {
            return _TxT;
            }

        set {
            if(_TxT == value) {
                return;
                }

            _TxT = value;
            RaisePropertyChanged(TxTPropertyName);
            }
        }

    }

完整的裸项目https://wetransfer.com/downloads/199f3db5d183e64cf9f20db4225d4c9820180702001102/f4f61b

样本

正如您在项目绑定作品中看到的那样,直接属性文本不是。

我希望它全部包含在用户控件中,所以我要么在 xaml 中设置用户控件属性值,要么绑定到它,而无需在主窗口 xaml 或代码中添加其他内容。

标签: c#wpfxaml

解决方案


您没有将 TextBlock 的Text属性绑定到TxTUserControl 的属性。

设置 Binding 的 RelativeSource

<TextBlock Text="{Binding TxT,
                  RelativeSource={RelativeSource AncestorType=UserControl}, ...}" .../>

或将 a 分配x:Name给 UserControl 并使用 ElementName 绑定。


然后,而不是通过设置 UserControl 的 DataContext

<local:UserControlSample DataContext="{Binding UCData}" .../>

绑定其 TxT 属性:

<local:UserControlSample TxT="{Binding UCData.TxT}" .../>

编辑:为了直接绑定到其 DataContext 中对象的属性,如预期的那样

<local:UserControlSample DataContext="{Binding UCData}" .../>

您根本不需要在 UserControl 中声明任何属性。删除 TxT 依赖属性声明,并直接绑定 UserControl 的 XAML 中的元素,就像您已经做的那样:

<TextBlock Text="{Binding TxT, ...}"/>

但是请注意,这不是 UserControl 通常的工作方式。您现在确实依赖于特定的视图模型类型,并且不能与其他视图模型重用。


推荐阅读