替换非平凡的getter方法为依赖属性。
替换非平凡的getter方法为依赖属性。
我最近开始尝试使用DataBinding并为我的自定义类实现DependencyProperties。一切都很顺利,可能性令人兴奋,然而,我遇到了一个问题,可能只能通过稍微修改整体类设计来解决。我想确保这是唯一的选择,而且我没有漏掉任何东西。
所以,我的类存储用户导入应用程序的视频文件的信息。除其他属性外,它包含:
public class VideoFile { public string FilePath { get; protected set; } public uint ID { get; protected set; } public string Extension { get { return Path.GetExtension(FilePath); } } public string FileName { get { return Path.GetFileName(FilePath); } } }
所以,我已成功地用DependencyProperty替换了FilePath。然而,在UI中,我主要想显示文件名,它使用一些逻辑来提供其值。据我所知,我有以下选择:
- 我可以简单地为FileName和Extension创建DependencyProperties,并在构造函数中设置它们的值,但这是多余的;我已经在FilePath中有了这些信息,所以我想避免这个选项。
- 创建ValueConverters,一个用于显示文件名,一个用于显示扩展名,并在我的绑定中使用它们。
我只是简单地了解了一下ValueConverters,所以我不确定。我能用它们来实现这个目的吗?或者,我只是遇到了它们存在的主要原因之一吗?:)
最后但并非最不重要的是,有人能想到类似这种情况的情形吗,当一个ValueConverter不是正确的选择时?我想避免直接使用它们,只是为了意识到它不起作用,因为“那个”属性无法以这种方式表达。
问题的出现原因:
问题是由于需要在UI上显示文件名,但是当FilePath属性改变时,FileName属性并没有更新,导致UI未能及时更新。
解决方法:
1. 将FileName属性改为依赖属性或支持IPropertyChanged接口,以便在更改时更新UI。
2. 在OnChangedFilePath方法中更新FileName属性,使其与FilePath保持一致。
3. 在ValidateFilePath方法中检查FilePath是否有效。
4. 在FilePath属性的注册中,通过PropertyMetadata的OnChangedFilePath参数指定OnChangedFilePath方法,通过CoerceFilePath参数指定CoerceFilePath方法,通过ValidateValueCallback参数指定ValidateFilePath方法。
问题出现的原因是作者想要使用依赖属性来自动更新UI,但是有人提出了用INotifyPropertyChanged来实现的更轻量级的方法。解决方法是使用INotifyPropertyChanged来自动更新UI,而不是使用依赖属性。
以下是用中文整理的文章:
替换非平凡的getter与依赖属性
在这种情况下,您不需要使用DependencyProperties。只有在您使用MarkupExtension设置属性时才需要使用DependencyProperty,而我怀疑您在模型类中进行这样的操作(因为您不会在Xaml中声明此类!)。
一种更轻量级的方法是使用INotifyPropertyChanged。以下是一个.NET 3.5风格的实现:
public class VideoFile : INotifyPropertyChanged { private string _filePath; public string FilePath { get { return _filePath; } protected set { _filePath = value; OnPropertyChanged("FilePath"); OnPropertyChanged("Extension"); OnPropertyChanged("FileName"); } } public uint ID { get; protected set; } public string Extension { get { return Path.GetExtension(FilePath); } } public string FileName { get { return Path.GetFileName(FilePath); } } protected void OnPropertyChanged(string propName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propName)); } public event PropertyChangedEventHandler PropertyChanged; }
(在.NET 4.5中,由于新的[CallerMemberName]属性,这可以简化一些。)
唯一的缺点是您需要为属性提供支持字段。然而,有一个名为NotifyPropertyWeaver的VS扩展可以自动化部分工作并消除对显式支持属性的需求。
这很好,直到您有几个属性引发了其他属性的PropertyChanged事件,这时您无法分辨出设置某些属性会产生什么效果,而且调试起来非常困难。这就是为什么我建议不要使用它的原因。
感谢您提供详细的实现,支持字段并不是问题。然而,我仍然认为我需要一个依赖属性。据我所了解,以这种方式实现将保持UI的自动更新,因此当我说Label1.Content = VideoFile1.FileName时,一旦属性更改,标签将会更新。但是我在XAML中声明了一个ListBox,在该ListBox中,我想为列表项编写自定义DataTemplate。在该模板中,我想创建标签和图像,并将它们绑定到这些属性...我认为这种方法在这种情况下无法帮助我。我错了吗?
你错了。INotifyPropertyChanged是您自动更新UI所需要的,包括在自定义的DataTemplate中。DependencyProperty也可以工作,但对于这种用途来说有点过头了。而且,PropertyChanged相对于DependencyProperty来说,怎么会更难调试呢?
我建议不要在单个setter中引发多个属性的PropertyChanged事件,我从未暗示过你所说的。
使用依赖属性有什么过头之处?在绑定方面,它们比INotifyPropertyChanged实现更快。参见这个SO线程。
替换非平凡的getter方法使用依赖属性的问题出现的原因是为了避免数据的重复。解决方法是使用绑定和一个IValueConverter,这样,每当FilePath发生变化时,UI中的Extension和FileName也会更新。此外,还可以在FilePath的setter中引发PropertyChanged事件,但这是不好的做法,因为FilePath不应该关心谁/什么在使用它。
解决方案中的代码示例是一个名为VideoFile的类。该类实现了INotifyPropertyChanged接口,其中FilePath属性的getter方法返回m_FilePath字段的值,setter方法在值发生变化时调用RaisePropertyChanged方法引发PropertyChanged事件。类中还有一个ID属性和两个受保护的RaisePropertyChanged方法。
在解决方案中提到了PropertySupport类,它是Prism框架的一部分,但通过调用RaisePropertyChanged("FilePath")也可以实现相同的效果,只是没有类型安全性。PropertySupport类提供了类型安全性,如果更改属性的名称,则会在编译时出现错误。
在解决方案中还提到了计算属性和转换器的使用。建议如果这个类是一个用于UI绑定的模型,使用PropertyChanged更有意义。特别是当FilePath发生变化时,如果不实现INotifyPropertyChanged或DependencyProperty,UI将不会重新绑定任何属性。在这两种方法中,NotifyPropertyChanged更加轻量级。
对于提到的计算属性,有人认为这是数据的重复,因为它是以不同的方式呈现相同的数据。而另一个人认为,这并不是数据的重复,因为计算属性和转换器实际上是相同的代码,只是封装方式不同。如果建议将FilePath设置为DependencyProperty,就应该在代码中包含实现。
总结一下,解决方案中提到了使用依赖属性和PropertyChanged事件来解决非平凡getter方法的问题。依赖属性提供了类型安全性和UI绑定的能力,PropertyChanged事件用于通知UI属性的变化。通过使用绑定和转换器,可以实现属性间的自动更新。计算属性被认为是数据的重复,但也有人认为它们只是以不同方式呈现相同的数据。最后,解决方案中提到了实现INotifyPropertyChanged接口和使用PropertySupport类的方法。