65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 依赖属性:兼容 XAML 绑定和代码后台绑定。

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.29/5 (7投票s)

2010 年 12 月 13 日

CDDL

2分钟阅读

viewsIcon

46019

一份易于理解的指南,用于编写与 XAML 绑定和代码后台绑定兼容的 WPF 依赖属性。

引言

WPF 和依赖属性是不可或缺的部分。

在 WPF 开发中,没有依赖属性和 MVVM,几乎寸步难行。但依赖属性的不当使用可能会造成噩梦。

让我们看下面的例子。

假设我们创建了一个自定义媒体播放器控件,并希望从 XAML 绑定 Source 属性。需要注意的是,一旦设置了 source 属性,我们需要执行一些额外的相关操作。

这是 Source 依赖属性的初始代码片段

public Uri Source
{
 get { return (Uri)GetValue(SourceProperty); }
 set
 {
       SetValue(SourceProperty, value);
       if (MediaElement != null)
       {
            SetMediaElementSource();
       }
 }
}

// Using a DependencyProperty as the backing store for Source. 
// This enables animation, styling, binding, etc...
   public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register("Source", typeof (Uri), typeof (MediaPlayer));

这是我们的 XAML

<media:MediaPlayer Source="{Binding Source}"   
x:Name="MainPlayer"></media:MediaPlayer>

现在如果我们运行应用程序,我们会看到 SetValue(SourceProperty, value); 之后编写的任何代码都不会执行。原因是,如果从 XAML 设置属性,则直接调用 SetValue() 方法。因此,如果从 XAML 绑定 source 属性,我们的播放器将无法工作!如果使用 DependencyProperty.Register 方法的以下重载版本,我们可以轻松克服这个问题。

// Using a DependencyProperty as the backing store for Source.  
// This enables animation, styling, binding, etc...
   public static readonly DependencyProperty SourceProperty =
        	DependencyProperty.Register("Source", typeof(Uri), 
	typeof(MediaPlayer), new FrameworkPropertyMetadata(OnMediaSourceChanged));

   private static void OnMediaSourceChanged
	(DependencyObject d, DependencyPropertyChangedEventArgs e)
   {
        var player = d as MediaPlayer;
        if (player != null) player.Source = e.NewValue as Uri;
   }

正如你所看到的,新的 FrameworkPropertyMetaData 只是一个方法,每当 Source 属性发生变化时都会被触发,无论它是从 XAML 还是代码后台更改的。由于我们正在 OnMediaSourceChanged 方法中设置 Source 属性,因此 source 的 Set 方法将执行所有业务逻辑!看起来我们的问题解决了!!但事实并非如此,它只是产生了一个新的问题。如果从代码后台设置 Source 属性,SourceSet 属性以及 Set 方法中编写的相关代码将执行两次,一次是从代码后台设置,另一次是从 OnMediaSourceChanged 方法。解决此问题的办法是,保持 Set 属性不包含任何业务逻辑(或任何代码!)。相反,任何需要执行的代码都应放在一个单独的方法中,并从 OnMediaSourceChanged 中调用该方法。以下是与 XAML 绑定和代码后台完全兼容的代码。

/// <summary>
/// Gets or sets the source.
/// </summary>
/// <value>The source.</value>
public Uri Source
{
    get { return (Uri)GetValue(SourceProperty); }
    set
    {
        SetValue(SourceProperty, value);
    }
}

// Using a DependencyProperty as the backing store for Source.  
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty SourceProperty =
    DependencyProperty.Register("Source", typeof(Uri), typeof(MediaPlayer), 
    new FrameworkPropertyMetadata(OnMediaSourceChanged));
    
/// <summary>
/// Called when [media source changed].
/// </summary>
/// The d.
/// The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> 
/// instance containing the event data.
private static void OnMediaSourceChanged
	(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var player = d as MediaPlayer;
    if (player != null)
    {
        player.Source = e.NewValue as Uri;
        player.PostSourceSetupWork();
    }
}

/// <summary>
/// Posts the source setup work.
/// </summary>
public void PostSourceSetupWork()
{
    if (MediaElement != null)
    {
        mediaElement.Pause();
        SetMediaElementSource();
    }
}

历史

  • 2010 年 12 月 13 日:初始发布
© . All rights reserved.