3

几天前我开始学习 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; }
    }
}

最后在我的 mainView中,我将 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)在文本框中看到的应用程序时,即使Modelor中的所有属性都不ViewModelDependencyProperties. 现在第二次尝试是:

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>

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

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

提前致谢。

4

3 回答 3

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

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

于 2013-04-29T20:55:01.867 回答
1

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

还有一个不太相关的问题:我注意到在几乎每一篇关于 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因此我们在调用它时不必传递任何内容。

于 2015-10-15T23:52:21.260 回答
0

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

    /// <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、RaisePropertyChanging 方法。您可以从http://www.galasoft.ch/mvvm/这个站点下载框架。安装后,您可以使用片段到mvvminpc来创建可以绑定的属性。

您的另一个问题已回答为什么在 WPF 中将 INotifyPropertyChanged 与绑定一起使用?

总而言之,您需要实现 RaisePropertyChanged() 方法并在 set;get 中使用此方法来通知视图有关更改属性的信息。

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

于 2013-04-29T20:44:09.007 回答