Home > other >  Bind view models' property to dependency property
Bind view models' property to dependency property

Time:04-04

I have created reusable components let's say a label and a textbox:

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>

Now as you can see I created dependency properties for the Text properties. Here is the code behind:

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

In my view I use this reusable component like this:

MyView.xaml

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

And my view model:

MyViewModel.cs

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

Question is, how can I bind this dependency property to my view model's property? As my approach doesn't work. I have more than one property so I must find a solution for the binding to be dynamic. Could it be that for this kind of problem, I should use a completely different approach?

CodePudding user response:

The internal elements must bind to the control's properties either by Binding.ElementName, where the the named UserControl is the binding source or by using Binding.RelativeSource.

HeaderAndTextBox.xaml

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

Next, make sure the DataContext of the parent element that hosts HeaderAndTextBox is correct:

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>

To make the HeaderAndTextBox.TextBoxContent property send data back to the view model automatically (when typing into the TextBox), you should configure the dependency property accordingly by using a FrameworkPropertyMetadata object instead of a PropertyMetadata and set the FrameworkPropertyMetadata.BindsTwoWayByDefault property:

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