开发者

DependencyProperty in my UserControl fails to update bound property in ViewModel

I have made an usercontrol that contains a TextBox with some custom behaviours and I want to bind the Text property to a property in my ViewModel.

I have isolated the problem into a sample solution and manage to update the Text property with the ViewModel property value, but when I write into the textbox and leaves the textbox my Person.Name property is not updated.

My usercontrol xaml:

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

<Control.Resources>
    <Style x:Key="readOnlyTextbox">
        <Style.Triggers>
            <Trigger Property="TextBoxBase.IsReadOnly" Value="True">
                <Setter Property="TextBoxBase.Background" Value="WhiteSmoke" />
                <Setter Property="TextBoxBase.Foreground" Value="#FF6D6D6D" />
                <Setter Property="TextBox.BorderBrush" Value="DarkGray" />
                <Setter Property="TextBoxBase.BorderThickness" Value="1,1,1,1" />
            </Trigger>
            <Trigger Property="TextBoxBase.IsReadOnly" Value="False">
                <Setter Property="TextBoxBase.Background" Value="White" />
                <Setter Property="TextBoxBase.Foreground" Value="Black" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Control.Resources>

<TextBox Style="{StaticResource readOnlyTextbox}" x:Name="txtTextBoxBase" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />

The codebehind code:

public partial class TextBoxReadOnlyLooksDisabled
{
    public TextBoxReadOnlyLooksDisabled()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof (string)
                                                                                         , typeof (TextBoxReadOnlyLooksDisabled)
                                                                     开发者_JAVA技巧                    ,new PropertyMetadata(OnTextChange));

    private static void OnTextChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textBoxReadOnlyLooksDisabled = (TextBoxReadOnlyLooksDisabled) d;
        textBoxReadOnlyLooksDisabled.txtTextBoxBase.Text = (string) e.NewValue;
    }

    public string Text
    {
        get { return (string) GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
}

Window where I try to get the sample to work:

<Window x:Class="WpfCustomUserControlBinding.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:src="clr-namespace:WpfCustomUserControlBinding" Title="MainWindow" Height="153" Width="525">
<Window.Resources>
    <src:Person x:Key="myDataSource"/>        
</Window.Resources>
<Grid >
    <Label Content="Plain vanilla" Height="26" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" Width="143" />
    <Label Content="Messed up version" Height="26" HorizontalAlignment="Left" Margin="12,61,0,0" Name="label2" VerticalAlignment="Top" Width="143" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="152,15,0,0" x:Name="txtVanlig" VerticalAlignment="Top" Width="251" Text="{Binding Source={StaticResource myDataSource}, Path=Name, Mode=TwoWay}"/>

    <src:TextBoxReadOnlyLooksDisabled Height="23" HorizontalAlignment="Left" Margin="152,61,0,0" x:Name="txtVrien" VerticalAlignment="Top" Width="251" Text="{Binding Source={StaticResource myDataSource}, Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>

The sample value class:

 public class Person
{
    private string _name = "King Chaos";

    public string Name{get{return _name;}set{_name = value;}}
}

Thanks in advance. ;)

Edit: Adding INotifyPropertyChanged does not do the trick since the set method of the Name is not accessed when updating my custom TextBox.


The problem is that the TextBox inside your TextBoxReadOnlyLooksDisabled UserControl has no two-way binding to the Text property - you only update the TextBox programmatically (in the OnTextChanged handler) when your property value changes, but not vice-versa.

Why not just drop the changed handler altogether, and add a binding instead, like this:

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

<Control.Resources>        
  //...
</Control.Resources>

<TextBox Style="{StaticResource readOnlyTextbox}"
         x:Name="txtTextBoxBase"
         HorizontalAlignment="Stretch"
         VerticalAlignment="Stretch"
         Text="{Binding Path=Text, Mode=TwoWay}"/>

Don't forget to also set the DataContext accordingly:

public partial class TextBoxReadOnlyLooksDisabled : UserControl
{
    public TextBoxReadOnlyLooksDisabled()
    {
        InitializeComponent();
        DataContext = this;
    }

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string),
                                                                                         typeof(TextBoxReadOnlyLooksDisabled));

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
}


Well the problem you are experiencing is caused because the Text dependency property of the TextBox inside of your custom TextBoxReadOnlyLooksDisabled is actually not bound to your "ViewModel" (the Person class) and so when you write something in that txtTextBoxBase its Text dp is changed, but the change is not propagated back to the ViewModel. What you can do is wire the Text dp of the nested TextBox to the Text dp of your custom control with:

<TextBox x:Name="txtTextBoxBase"
         Text={Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TextBoxReadOnlyLooksDisabled}}} />


public class Person : INotifyPropertyChanged
{ 
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name = "King Chaos"; 

    public string Name{
       get{
          return _name;
       }
       set{
          _name = value;
          if (PropertyChanged != null)
              PropertyChanged(this, new 
                 PropertyChangedEventArgs("Name"));
       }
    } 
} 

Simply your model must Implement INotifyPropertyChanged and raise property changed whenever your property is set, so that XAML will detect a change and refresh its value.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜