我的 UserControl 中的 DependencyProperty 无法更新 ViewModel 中的绑定属性

发布于 2024-10-08 10:42:31 字数 3974 浏览 2 评论 0原文

我制作了一个用户控件,其中包含具有一些自定义行为的 TextBox,并且我想将 Text 属性绑定到 ViewModel 中的属性。

我已将问题隔离到示例解决方案中,并设法使用 ViewModel 属性值更新 Text 属性,但是当我写入文本框并离开文本框时,我的 Person.Name 属性不会更新。

我的用户控件 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" />

代码隐藏代码:

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

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof (string)
                                                                                         , typeof (TextBoxReadOnlyLooksDisabled)
                                                                                         ,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 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>

示例值类:

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

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

提前致谢。 ;)

编辑:添加 INotifyPropertyChanged 并不能解决问题,因为更新我的自定义 TextBox 时不会访问名称的 set 方法。

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)
                                                                                         ,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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

老旧海报 2024-10-15 10:42:31

问题是 TextBoxReadOnlyLooksDisabled UserControl 中的 TextBox 没有与 Text 属性的双向绑定 - 当属性值更改时,您只能以编程方式更新 TextBox(在 OnTextChanged 处理程序中),但反之则不然。

为什么不完全删除更改的处理程序,然后添加一个绑定,如下所示:

<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}"/>

不要忘记相应地设置 DataContext:

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

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); }
    }
}
游魂 2024-10-15 10:42:31

那么您遇到的问题是由于自定义 TextBoxReadOnlyLooksDisabled 内部 TextBoxText 依赖属性实际上并未绑定到您的“ViewModel” “(Person 类),因此当您在 txtTextBoxBase 中写入内容时,其 Text dp 会发生更改,但更改不会传播回视图模型。
您可以做的是将嵌套 TextBoxText dp 连接到自定义控件的 Text dp:

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

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}}} />
燕归巢 2024-10-15 10:42:31
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"));
       }
    } 
} 

只需您的模型必须实现 INotifyPropertyChanged 并在设置属性时引发属性更改,以便 XAML 检测到更改并刷新其值。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文