将视图模型 属性 绑定到依赖项 属性

Bind view models' property to dependency property

我已经创建了可重复使用的组件,比如一个标签和一个文本框:

HeaderAndTextBox.xaml

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>

        <TextBlock
            Margin="10,0,0,0"
            FontSize="16"
            FontWeight="DemiBold"
            Foreground="White"
            Text="{Binding Header, ElementName=root}" />

        <TextBox
            Grid.Row="1"
            MaxWidth="300"
            Margin="10"
            mah:TextBoxHelper.ClearTextButton="True"
            mah:TextBoxHelper.IsWaitingForData="True"
            FontSize="16"
            Text="{Binding TextBoxContent, ElementName=root}" />

    </Grid>

现在如您所见,我为文本属性创建了依赖属性。这是背后的代码:

HeaderAndTextBox.xaml.cs

public partial class HeaderAndTextBox : UserControl
    {
        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(string), typeof(HeaderAndTextBox), new PropertyMetadata(string.Empty));

        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty TextBoxContentProperty =
            DependencyProperty.Register("TextBoxContent", typeof(string), typeof(HeaderAndTextBox), new PropertyMetadata(string.Empty));

        public string TextBoxContent
        {
            get { return (string)GetValue(TextBoxContentProperty); }
            set { SetValue(TextBoxContentProperty, value); }
        }

        public HeaderAndTextBox()
        {
            InitializeComponent();
        }
    }

在我看来,我是这样使用这个可重用组件的:

MyView.xaml

<controls:HeaderAndTextBox
                            Grid.Row="1"
                            Margin="10,10,0,0"
                            Header="Last Name"
                            TextBoxContent="{Binding Path=LastName, UpdateSourceTrigger=PropertyChanged}" />

我的视图模型:

MyViewModel.cs

private string? _lastName;
        public string? LastName
        {
            get
            {
                return _lastName;
            }
            set
            {
                _lastName = value;
                OnPropertyChanged(nameof(LastName));
            }
        }

问题是,如何将此依赖项 属性 绑定到视图模型的 属性?因为我的方法不起作用。我有不止一个 属性 所以我必须找到一个动态绑定的解决方案。 难道对于这种问题,我应该使用完全不同的方法?

内部元素必须通过 Binding.ElementName 绑定到控件的属性,其中命名的 UserControl 是绑定源,或者通过使用 Binding.RelativeSource.

HeaderAndTextBox.xaml

<UserControl>
  <TextBox Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=TextBoxContent, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>

接下来,确保承载 HeaderAndTextBox 的父元素的 DataContext 是正确的:

MainWindow.xaml

<Window>
  <Window.DataContext>
    <MyViewModel />
  </Window.DataContext>
  
  <StackPanel>

    <!-- The HeaderAndTextBox inherits the parent's DataContext, 
         which is MyViewModel, automatically. -->
    <HeaderAndTextBox TextBoxContent="{Binding SomeMyViewModelTextProperty}" />

    <Grid DataContext="{Binding GridViewModel}">

      <!-- Same control, different instance, 
           binds to a different view model class (GridViewModel). -->
      <HeaderAndTextBox TextBoxContent="{Binding SomeGridViewModelTextProperty}" />
    </Grid>
  </StackPanel>
</Window>

要使 HeaderAndTextBox.TextBoxContent 属性 自动将数据发送回视图模型(当输入 TextBox 时),您应该相应地配置依赖项 属性使用 FrameworkPropertyMetadata 对象而不是 PropertyMetadata 并设置 FrameworkPropertyMetadata.BindsTwoWayByDefault 属性:

HeaderAndTextBox.xaml.cs

partial class HeaderAndTextBox : UserControl
{
  public static readonly DependencyProperty TextBoxContentProperty = DependencyProperty.Register(
    "TextBoxContent", 
    typeof(string), 
    typeof(HeaderAndTextBox), 
    new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

  public string TextBoxContent
  {  
    get => (string)GetValue(TextBoxContentProperty); 
    set => SetValue(TextBoxContentProperty, value); 
  }
}