绑定到 WPF 中的依赖项和常规属性

本文关键字:常规 属性 依赖 WPF 绑定 | 更新日期: 2023-09-27 18:11:37

我几天前开始学习 WPF,并一直在创建一些测试项目来评估我在学习材料时对材料的理解。根据这篇文章,"一个属性只有在它是依赖属性时才能被绑定。为了测试这一点,我使用了两种不同的模式:

1. 使用 MVVM 模式(某种(

我创建了Movie模型:

public class MovieModel
{
    private string _movieTitle;
    private string _rating;
    public string MovieTitle
    {
        get { return _movieTitle; }
        set { _movieTitle = value; }
    }
    public string Rating
    {
        get { return _rating; }
        set { _rating = value; }
    }
}

MovieViewModel视图模型:

public class MovieViewModel
{
    MovieModel _movie;
    public MovieViewModel()
    {
        _movie = new MovieModel { MovieTitle = "Inception (Default)" };
    }
    public MovieModel Movie
    {
        get { return _movie; }
        set { _movie = value; }
    }
    public string MovieTitle
    {
        get { return Movie.MovieTitle; }
        set { Movie.MovieTitle = value; }
    }
}

最后在我的主View中,我将 DataContext 设置为 MovieViewModel 类的一个实例:

<Window x:Class="MVVMTestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MVVMTestProject.ViewModels"
    Name="MainWindowElement"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MovieViewModel/>
</Window.DataContext>
<Grid>
    <StackPanel Orientation="Vertical">
        <TextBox Width="150" Text="{Binding Path=MovieTitle}"></TextBox>
    </StackPanel>
</Grid>

这工作正常,当我运行应用程序时,我在文本框中看到Inception (Default),即使ModelViewModel中的任何属性都没有DependencyProperties。现在第二次尝试是:

2. 在 MainView 的代码隐藏中使用属性

在我的主视图的代码后面,我把:

private string _myText;
public MainWindow()
{
    InitializeComponent();
    MyText = "Some Text";
}
public string MyText
{
    get { return _myText; }
    set { set _myText = value; }
}

然后我把我的观点改为:

<Window x:Class="MVVMTestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MVVMTestProject.ViewModels"
    Name="MainWindowElement"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MovieViewModel/>
</Window.DataContext>
<Grid>
    <StackPanel Orientation="Vertical">
        <TextBox Width="150" Text="{Binding ElementName=MainWindowElement, Path=MyText}"></TextBox>
    </StackPanel>
</Grid>

但这没有用。当我运行应用程序时,文本框为空。所以我将属性更改为依赖属性,奇怪的是,它起作用了。所以我不明白为什么模型和 ViewModel 上的属性可以是常规属性,但代码隐藏属性必须是依赖项属性才能绑定到?是因为视图模型设置为视图的 DataContext,并且解析器足够聪明以实现这一点,还是有其他原因?

还有一个不太相关的问题:我注意到在几乎每篇关于 WPF 的文章中,人们都使用局部变量,然后为其定义一个 getter 和 setter,即使我们都知道在 .NET 2.5(我相信?(及更高版本中,我们可以只使用 get; set; 语法而不必定义局部成员变量。那么这有什么具体的原因吗?

提前谢谢。

绑定到 WPF 中的依赖项和常规属性

  1. 仅在将用作绑定目标的FrameworkElement(依赖项对象(上需要DependencyProperty,这意味着您将通过 XAML 应用 {Binding} 扩展或通过代码设置绑定的属性。在您的示例中,只需要DependencyProperty TextBox.Text。在第二个示例中,绑定不起作用,因为您没有适当的机制来发送从绑定源发送属性值已更改的通知。通常,这是通过实现INotifyPropertyChanged接口并在属性setter中引发PropertyChanged事件来完成的。将属性更改为DependencyProperty时,还会获得通知机制,以便将属性更改传播到目标 (TextBox(。因此,您可以通过引发PropertyChanged事件来使其工作,而无需DependencyProperty。请注意,除了绑定之外,还有其他用途需要DependencyProperty(样式、动画...

  2. PropertyChanged事件可能是使用支持字段(而不是作为自动属性(实现属性的主要原因。因为您需要在属性设置器中PropertyChanged事件。

拿起帖子末尾的第二个问题:

还有一个不太相关的问题:我注意到在几乎每篇关于 WPF 的文章中,人们都使用局部变量,然后为它定义一个 getter 和 setter,即使我们都知道在 .NET 2.5(我相信?(及更高版本中,我们可以只使用 get; set; 语法而不必定义局部成员变量。那么这有什么具体的原因吗?

在您的代码中,您给出了如下示例:

private string _movieTitle;
public string MovieTitle
{
    get { return _movieTitle; }
    set { _movieTitle = value; }
}

该代码正是编译器在您编写此代码时生成的代码:

public string MovieTitle { get; set; }

您在 WPF 中看到类似第一个窗体的原因是,我们希望 WPF 绑定在属性值更改时做出反应,为了实现这一点,我们需要通过 INotifyPropertyChanged 添加更改通知。

有很多方法可以做到这一点,但在现代 C# 中,你可以编写如下内容:

public class MovieViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string _movieTitle;
    public string MovieTitle
    {
        get { return _movieTitle; }
        set
        {
            if (_movieTitle == value)
                return;
            _movieTitle = value;
            OnPropertyChanged();
        }
    }
    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

在 setter 中,我们使用属性的字符串名称引发 PropertyChanged 事件, MovieTitle ,编译器为我们传递该属性,因为 [CallerMemberName] 属性和默认值 null 因此我们在调用它时不必传递任何东西。

使用 mvvm 光,您可以绑定属性并引发事件。

    /// <summary>
    /// The <see cref="SelectedPriority" /> property's name.
    /// </summary>
    public const string SelectedPriorityPropertyName = "SelectedPriority";
    private string _selectedPriority = "normalny";
    /// <summary>
    /// Sets and gets the SelectedPriority property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public string SelectedPriority
    {
        get
        {
            return _selectedPriority;
        }
        set
        {
            if (_selectedPriority == value)
            {
                return;
            }
            RaisePropertyChanging(SelectedPriorityPropertyName);
            _selectedPriority = value;
            RaisePropertyChanged(SelectedPriorityPropertyName);
        }
    }

这个类扩展了ViewModelBase,它有方法RaisePropertyChanged,RaisePropertyChange。您可以从本网站 http://www.galasoft.ch/mvvm/下载框架。安装后,您可以使用代码片段到 mvvminpc 来创建可以绑定的属性。

您的另一个问题已得到解答 为什么在 WPF 中使用 INotifyPropertyChanged 与绑定?

最后,您需要实现 RaisePropertyChanged(( 方法并在 set 中使用此方法;get 以通知视图有关更改属性的信息。

实现 INotifyPropertyChanged - 是否存在更好的方法?