用依赖属性替换非平凡的getter

时间:2012-09-17 12:48:45

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

我最近开始尝试使用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中,我主要想显示文件名,它使用一些逻辑来提供其值。据我所知,这是我的选择:

  1. 我可以简单地为FileName和Extension创建DependencyProperties,并在构造函数中设置它们的值,但这是多余的;我已经在FilePath中拥有该信息,因此我想避免使用此选项。
  2. 创建ValueConverters,一个用于显示Filename,另一个用于显示Extension,并在我的绑定中使用它们。
  3. 我只是简单地遇到了ValueConverters,所以我不确定。我可以将它们用于此目的吗?或者,我刚刚遇到他们存在的主要原因之一? :)

    最后但并非最不重要的是,当ValueConverter不是正确的方式时,是否有人能想到类似的情况?我想避免直接进入它们,只是意识到它不起作用,因为“那个”属性不能用这种方式表达。

3 个答案:

答案 0 :(得分:1)

您不需要DependencyProperties。当您要使用DependencyProperty设置为属性时,您只需MarkupExtension,我怀疑您是否正在使用模型类(因为您赢了)不要在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扩展可以自动完成部分工作,并且不需要显式的后备属性。

答案 1 :(得分:0)

不要复制数据。

首选BindingIValueConverter,因为这样,每当FilePath更改时,ExtensionFileName也会在UI中更新。< / p>

当然,你也可以在PropertyChanged的setter中为他们提出FilePath,但这是不好的做法,因为FilePath不应该关心谁/什么使用它。< / p>

该课程如下:

public class VideoFile : INotifyPropertyChanged {

    string m_FilePath;
    public string FilePath 
    { 
       get { return m_FilePath; } 
       protected set
       {
          if(value != m_FilePath)
          {
             m_FilePath = value;
             RaisePropertyChanged(() => this.FilePath);
          }
       }
    }
    public uint ID { get; protected set; }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged<T>(Expression<Func<T>> _PropertyExpression)
    {
        RaisePropertyChanged(PropertySupport.ExtractPropertyName(_PropertyExpression));
    }

    protected void RaisePropertyChanged(String _Prop)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(_Prop));
        }
    }

    #endregion
}

请注意PropertySupport是Prism的一部分,但是你可以通过调用RaisePropertyChanged("FilePath")来解决这个问题,只需要安排类型安全,因为如果你更改了属性的名称,你将有一个编译 - 时间错误。

答案 2 :(得分:0)

据我所知,您只想在UI上显示文件名。然后,您可以考虑在更改FilePath依赖项属性时更新FileName属性(OnChangedFilePath方法)。您还可以在ValidateFilePath方法中检查FilePath是否正常。请注意,FileName也必须是依赖属性或支持IPropertyChanged,否则在更改时不会更新UI。您不需要为此目的使用转换器。

public string FilePath
{
    get { return (string)GetValue(FilePathProperty); }
    set { SetValue(FilePathProperty, value); }
}

private static object CoerceFilePath(DependencyObject d, object value)
{
    return value;
}

private static bool ValidateFilePath(object Value)
{
    return true;
}

private static void OnChangedFilePath(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}

public static readonly DependencyProperty FilePathProperty =
    DependencyProperty.Register("FilePath", typeof(string), typeof(ClassName),
    new PropertyMetadata(@"C:\File.avi", OnChangedFilePath, CoerceFilePath),
    new ValidateValueCallback(ClassName.ValidateFilePath));