2012年9月16日星期日

.NET 4.5(C#): 利用CallerMemberName特性使MVVM Light属性定义更简洁

.NET 4.5(C#): 利用CallerMemberName特性使MVVM Light属性定义更简洁

我曾经写过一篇文章:.NET 4.5(C#):INotifyPropertyChanged执行的演变:从表达式树到调用方信息的BindableBase类型,讲述.NET 4.5中的调用者信息和WinRT工程中Visual Studio 2012利用调用者信息生成的BindableBase类型。不过当前的WinRT的MVVM Light(V4 RTM)并没有利用调用者信息来简化INotifyPropertyChanged属性的定义。

 

当前MVVM Light的两种RaisePropertyChanged调用方式:

protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression, T oldValue, T newValue, bool broadcast);

protected virtual void RaisePropertyChanged<T>(string propertyName, T oldValue, T newValue, bool broadcast);

 

那么INotifyPropertyChanged的属性定义要么使用原始字符串定义的样式:

#region MyProperty

public const string MyPropertyPropertyName = "MyProperty";

private string _MyProperty = null;

public string MyProperty

{

    get { return _MyProperty; }

    set

    {

        if (_MyProperty == value)

            return;

        _MyProperty = value;

        RaisePropertyChanged(MyPropertyPropertyName);

    }

}

#endregion

 

或者使用Expression Tree:

//使用Expression Tree

#region MyProperty

private string _MyProperty = null;

public string MyProperty

{

    get { return _MyProperty; }

    set

    {

        if (_MyProperty == value)

            return;

        _MyProperty = value;

        RaisePropertyChanged(() => _MyProperty);

    }

}

#endregion

 

 

不错,但是有很大的改进方式可以让代码更简洁,借鉴BindableBase类型,我们可以自定义一个类型继承MVVM Light的ViewModelBase,并加入一个RaisePropertyChangedEx方法,来使用调用者信息,同时对数据进行判断,这样判断代码就不用写在属性声明中了,如下代码:

//+ using GalaSoft.MvvmLight;

//+ using System.Runtime.CompilerServices;

class MyViewModelBase : ViewModelBase

{

    protected virtual bool RaisePropertyChangedEx<T>(ref T oldValue, T newValue, bool broadcast, [CallerMemberName] string prop = null)

    {

        if (!Object.Equals(oldValue, newValue))

        {

            oldValue = newValue;

            RaisePropertyChanged(prop, oldValue, newValue, broadcast);

            return true;

        }

        return false;

    }

}

 

 

同时我还写了个Code Snippets来快速定义这种类型的属性(snippet在文章下方),快捷方式是mnp。这样继承上面的MyViewModelBase,然后使用该Code Snippet就可以快速创建简洁的INotifyPropertyChanged属性了:

class MyViewModel : MyViewModelBase

{

    //使用Code Snippets快速创建更简洁的INotifyPropertyChanged属性定义

    //RaisePropertyChangedEx中的false参数代表非广播

    #region MyProperty

 

    private string _MyProperty = null;

    public string MyProperty

    {

        get { return _MyProperty; }

        set { RaisePropertyChangedEx(ref _MyProperty, value, false); }

    }

 

    #endregion

}

 

下面完整的Code Snippet内容(抱歉今天Skydrive登不上去,直接贴代码了,不过也不是很长)

<? version="1.0" encoding="utf-8"?>

<CodeSnippets ="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

  <CodeSnippet Format="1.0.0">

    <Header>

      <SnippetTypes>

        <SnippetType>Expansion</SnippetType>

      </SnippetTypes>

      <Title>MVVM Light中INPC属性定义</Title>

      <Author>Mgen</Author>

      <Shortcut>mnp</Shortcut>

    </Header>

    <Snippet>

      <Declarations>

        <Literal Editable="true">

          <ID>Type</ID>

          <ToolTip>属性类型</ToolTip>

          <Default>string</Default>

          <Function>

          </Function>

        </Literal>

        <Literal Editable="true">

          <ID>Value</ID>

          <ToolTip>默认值</ToolTip>

          <Default>null</Default>

          <Function>

          </Function>

        </Literal>

        <Literal Editable="true">

          <ID>Prop</ID>

          <ToolTip>属性名称</ToolTip>

          <Default>MyProperty</Default>

          <Function>

          </Function>

        </Literal>

      </Declarations>

      <Code Language="csharp">

          <![CDATA[#region $Prop$

 

private $Type$ _$Prop$ = $Value$;

public $Type$ $Prop$

{

    get { return _$Prop$;}

    set { RaisePropertyChangedEx(ref _$Prop$, value, false); }

}

 

#endregion

 

$end$]]></Code>

    </Snippet>

  </CodeSnippet>

</CodeSnippets>


TAG: