【问题标题】:Why binding doesn't work for TestClass, but setting the same property does work?为什么绑定对 TestClass 不起作用,但设置相同的属性却起作用?
【发布时间】:2019-10-16 17:13:56
【问题描述】:

所以我可能不太了解绑定,我想出了这个测试项目,我可以在其中以简单的方式展示我的问题。我需要在哪里更改项目以使绑定更新视图?

这是我的项目,如果你想下载它: https://1drv.ms/u/s!AqdZJMIRBGu7z1V8K1jXN9BQWFQ-?e=oxdlhL

这里有一些代码:

TestClass.cs:

 public class TestClass
 {
     public string Text { get; set; } = "Default";
 } 

TestClassView.xaml:

<UserControl x:Class="Test.TestClassView" ...>
   <Grid>
      <TextBlock Text="{Binding TestClass.Text}" />
   </Grid>
</UserControl>

TestClassView.xaml.cs:

public partial class TestClassView : UserControl
{
    public TestClass TestClass 
    {
        get { return (TestClass)GetValue(TestClassProperty); }
        set { SetValue(TestClassProperty, value); }
    }
    public static readonly DependencyProperty TestClassProperty = DependencyProperty.Register("TestClass", typeof(TestClass), typeof(TestClassView), new PropertyMetadata(new TestClass() { Text = "Default"}));

    public TestClassView()
    {
        InitializeComponent();

        DataContext = this;
    }

MainWindow.xaml:

<Window x:Class="Test.MainWindow" ...>
   <Grid>
      <WrapPanel Margin="10">
         <Button Content="Test" Click="Button_Click" />
         <local:TestClassView x:Name="TestClassView" TestClass="{Binding TestClass}" />
      </WrapPanel>
   </Grid>
</Window>

MainWindow.xaml.cs:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private TestClass testClass = new TestClass() { Text = "DefaultProperty" };
    public TestClass TestClass { get { return testClass; } set{ testClass = value; OnPropertyChanged(); } }
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName]string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        TestClass = new TestClass() { Text = "Test1" };
        MessageBox.Show("Test 1 Done");
        await Task.Delay(1000);
        TestClassView.TestClass = new TestClass() { Text = "Test2" };
        MessageBox.Show("Test 2 Done");
    }
}

所以当我点击按钮时,首先我设置了 MainWindow 的 TestClass 实例,它绑定到 TestClassView 的 DependencyProperty 调用 TestClass。我也调用了 PropertyChanged,但 TestClassView 属性没有更新。

一秒钟后,在点击事件处理程序的第二部分,实际上设置了TestClassView TestClass 属性,并且视图将被更新。

那么如何修改代码,让绑定生效呢?

【问题讨论】:

    标签: c# wpf data-binding


    【解决方案1】:

    我会说 TestClassView 不需要 TestClass 属性。其用法与DataContext属性相同。

    public partial class TestClassView : UserControl
    {
        public TestClassView()
        {
            InitializeComponent();
        }
    }
    
    <UserControl x:Class="Test.TestClassView" ...>
       <Grid>
          <TextBlock Text="{Binding Text}" />
       </Grid>
    </UserControl>
    

    和窗口:

    <Window x:Class="Test.MainWindow" ...>
       <Grid>
          <WrapPanel Margin="10">
             <Button Content="Test" Click="Button_Click" />
             <local:TestClassView x:Name="TestClassView" DataContext="{Binding TestClass}" />
          </WrapPanel>
       </Grid>
    </Window>
    
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private TestClass testClass = new TestClass() { Text = "DefaultProperty" };
        public TestClass TestClass { get { return testClass; } set { testClass = value; OnPropertyChanged(); } }
    
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName]string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            TestClass = new TestClass() { Text = "Test1" };
            MessageBox.Show("Test 1 Done");
            await Task.Delay(1000);
            TestClass = new TestClass() { Text = "Test2" };
            MessageBox.Show("Test 2 Done");
        }
    }
    

    在原始示例中绑定不起作用,因为TestClassView 控件通过在构造函数中设置DataContext = this; 来破坏DataContext 传播。这是一种不好的做法,导致的问题多于解决的问题。

    在 Window 中设置 DataContext = this;可接受练习,因为它允许快速设置,但通常 View 使用单独的 ViewModel 类用于 DataContext。

    【讨论】:

    • 我不知道为什么,但如果我在 xaml 文件中设置数据上下文,它会解决我的问题。我是这样实现的:DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}",不知道有没有副作用什么的。这算作不好的做法吗?
    【解决方案2】:

    在您的绑定中,您将绑定到 TestClass 的一个属性:

    <TextBlock Text="{Binding TestClass.Text}" />
    

    因此该属性需要引发 PropertyChanged。从其他物体上举起它是没有用的。

    private string text = "Default";
    public string Text 
    { 
        get => return this.text;
        set 
        { 
            this.text = value;
            this.NotifyPropertyChanged();
        } 
     } 
    

    【讨论】:

      猜你喜欢
      • 2016-07-05
      • 1970-01-01
      • 1970-01-01
      • 2017-11-24
      • 1970-01-01
      • 1970-01-01
      • 2021-04-29
      • 2010-09-23
      相关资源
      最近更新 更多