WPF依赖属性问题

3

我是新手WPF,我试图完成一个我认为很简单的任务 - 在我的程序运行过程中显示业务对象字段的值随着变化而变化。我知道如何通过在C#中手动更改TextBox.Text属性来“强制”更改它,但是由于我正在学习WPF,我想用“正确”的方式做,这意味着使用数据绑定。

问题#1:据我所知,我的选择是使用DependencyProperty或在我的业务对象中实现INotifyPropertyChanged,对吗?

问题#2:这是我尝试使用DependencyProperty路线的通用代码版本。

标记:

Button x:Name="nextButton" Content="Click"  Grid.Row="2" Click="nextButton_Click" />
TextBox x:Name="myTextBox" Grid.Row="1" Text="{Binding Source=myTest, Path=Name, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>

代码后端:

namespace DependencyTest2
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        private int i;

        private TestSphere myTest;

        public MainWindow()
        {
            InitializeComponent();

            i = 0;

            myTest = new TestSphere();
        }

        private void nextButton_Click(object sender, RoutedEventArgs e)
        {
            switch (i)
            {
                case 0:
                    myTest.Name = "string1";
                    break;
                case 1:
                    myTest.Name = "string2";
                    break;
                case 2:
                    myTest.Name = "string3";
                    break;
            }

            i++;
        }
    }

    class TestSphere : DependencyObject
    {

        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }

        public static readonly DependencyProperty NameProperty =
            DependencyProperty.Register("Name", typeof(string), typeof(TestSphere));


        public TestSphere()
        {
            Name = "default";
        }
    }
}

当我运行程序时,文本框中没有任何内容显示,即使绑定属性有一个值 - 我需要做些什么来通知绑定源值已更改吗?我认为使用DependencyProperty作为源应该会处理这个问题,但再次说明,我是一个WPF新手。谢谢!
  • 史蒂夫
好的,我尝试使用我在codeproject上找到的包装类来实现INotifyPropertyChanged,如下所示:

    class TestSphere : NotifyProperyChangedBase
    {
        private string _Name;

        public string Name
        {
            get { return _Name; }
            set 
            {
                this.CheckPropertyChanged("Name", ref _Name, ref value);
            }
        }

        public TestSphere()
        {
            Name = "default";
        }
    }

    public abstract class NotifyProperyChangedBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
        #region methods
        protected bool CheckPropertyChanged(string propertyName, ref T oldValue, ref T newValue)
        {
            if (oldValue == null && newValue == null)
            {
                return false;
            }

            if ((oldValue == null && newValue != null) || !oldValue.Equals((T)newValue))
            {
                oldValue = newValue;
                FirePropertyChanged(propertyName);
                return true;
            }

            return false;
        }

        protected void FirePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

这是我的新标记语言:


    Grid Name="myGrid">
        Grid.RowDefinitions>
            RowDefinition Height="30"/>
            RowDefinition Height="30"/>
            RowDefinition Height="30"/>
            RowDefinition Height="*"/>
        /Grid.RowDefinitions>
        Label x:Name="myLabel" Grid.Row="0" Foreground="Black"  />
        Button x:Name="nextButton" Content="Click"  Grid.Row="2" Click="nextButton_Click" />
        TextBox x:Name="myTextBox" Grid.Row="1" Text="{Binding Path=myTest.Name}"/>
    /Grid>

在我实例化myTest之后,我还添加了一行代码myGrid.DataContext = myTest;到'public MainWindow()'中。当我逐步执行生成的程序时,this.PropertyChanged的值总是评估为null,因此PropertyChanged事件从未触发。提前抱歉,这一定是一个非常初级的问题。

  • 史蒂夫

在NotifyPropertyChangedBase中比较旧/新值可能不会带来任何性能提升,但是顺便说一下,看起来你每次更新时都调用了FirePropertyChanged()两次。 - Chris Wenham
另外,你能展示一下你现在的 {Binding} 是什么样子吗?如果 TextBox 的父容器是 <Grid Name="myGrid">,并且 myGrid.DataContext = myTest,那么绑定应该是 {Binding Path=Name}。 - Chris Wenham
谢谢你发现了这个问题!我忘记改回原来的了 - 请看上面的编辑。 - seveland
改为 Binding Path=Name 而不是 Binding Path=myTest.Name 就解决了!非常感谢您的帮助! - seveland
1个回答

1

你只需要在TestSphere类上实现INotifyPropertyChanged,而不是DependencyObject。当你更新值时,调用PropertyChanged(this, new PropertyChangedEventArgs("Name"))

其次,你需要在代码后台为窗口设置DataContext。假设你在XAML中使用了这个根网格元素:

<Grid Name="MainForm">

然后在你的代码后台,你会这样做:

MainForm.DataContext = this;

最后,将myTest属性更改为public,然后您的XAML中的绑定应该只需要这样:
Text="{Binding Path=myTest.Name}"

感谢您的快速回复 - 我编辑了原帖以添加新问题,这是您建议的结果。再次感谢! - seveland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接