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

时间:2013-04-29 20:40:08

标签: c# wpf data-binding dependency-properties

我几天前开始学习WPF,并且在我学习它们的过程中创建了一些测试项目以评估我对材料的理解。根据{{​​3}},“如果属性是依赖属性,则只能绑定属性。”为了测试这个,我使用了两种不同的模式:

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>

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

这也是一个不太相关的问题:我注意到几乎所有关于WPF的文章人都使用局部变量,然后为它定义一个getter和setter,即使我们都知道.NET 2.5(我相信?)以上,我们可以使用get; set;语法,而无需定义本地成员变量。那有什么特别的原因吗?

提前致谢。

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分: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 来创建可以绑定的属性。

您的其他问题已经解答Why use INotifyPropertyChanged with bindings in WPF?

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

Implementing INotifyPropertyChanged - does a better way exist?

答案 2 :(得分:0)

在帖子的最后收到第二个问题:

  

这也是一个不太相关的问题:我注意到几乎所有关于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添加更改通知。 / p>

有很多方法可以做到这一点,但在现代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的{​​{1}}所以当我们打电话时,我们不必传递任何东西。