本篇我們着重介紹ViewModelBase,演示Set和RaisePropertyChanged方法的使用,以及就Cleanup方法釋放資源展開討論。
- ICleanup 接口。
實現該接口的ViewModel需要在Cleanup方法裏釋放資源,特別是-= event - ObservableObject
該類實現了INotifyPropertyChanged接口,定義了一個可通知的對象基類,供ViewModelBase繼承 - ViewModelBase
繼承自ObservableObject, ICleanup。將作爲MvvmLight框架下使用的ViewModel的基類。主要提供Set和RaisePropertyChanged供外部使用。同時會在Cleanup方法裏,Unregister該實例的所有的MvvmLight Messenger(在GalaSoft.MvvmLight.Messaging命名空間定義)
以上是第一篇裏給出的表格,ViewModelBase是MvvmLight裏非常重要的一個基類,理論上使用MvvmLight你所有的ViewModel都需要繼承該類(當然你也可以不繼承,那你還用啥MvvmLight?啥?只用RelayCommand?給跪了……)
我們先看一下最基本的Set和RaisePropertyChanged方法的使用:
private string title;
public string Title
{
get { return title; }
set { Set(ref title , value); }
}
private string text;
public string Text
{
get { return text; }
set
{
text = value;
RaisePropertyChanged("Text");
RaisePropertyChanged("TitleAndText");
}
}
public string TitleAndText
{
get
{
return title + text;
}
}
Set方法會再屬性賦值時自動爲你調用RaisePropertyChanged進行通知。當然你也可以手動調用RaisePropertyChanged方法。
MvvmLight的源代碼如下,將可複用的邏輯提取封裝,減少了我們搬磚時的工作量:
protected bool Set<T>(
ref T field,
T newValue = default(T),
bool broadcast = false,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return false;
}
#if !PORTABLE
RaisePropertyChanging(propertyName);
#endif
var oldValue = field;
field = newValue;
RaisePropertyChanged(propertyName, oldValue, field, broadcast);
return true;
}
Cleanup是一個非常重要的方法。當前Page在OnNavigatedFrom時,應該要釋放不再需要的資源,特別是-= event,Unregister掉MvvmLight的Messenger。
在繼承ViewModelBase的子類ViewModel裏調用base.Cleanup();會自動釋放掉當前ViewModel註冊的Messenger
ViewModelBase裏的Cleanup方法:
public virtual void Cleanup()
{
MessengerInstance.Unregister(this);
}
所以一般ViewModel的OnNavigatedFrom方法看上去都是這個樣子:
public void OnNavigatedFrom(object obj)
{
base.Cleanup();
this.xxxxEvent -= xxxxHandler;
}
什麼什麼,你說ViewModel是沒有OnNavigatedFrom方法的?確實是沒有的,但是我們這裏給需要處理導航事件的ViewModel都實現了INavigable接口:
public interface INavigable
{
void OnNavigatedFrom(object obj);
void OnNavigatedTo(object obj);
}
然後呢,override需要處理導航事件的Page的相應方法,調用ViewModel裏的NavigatedFrom、NavigatedTo方法,傳遞參數,把處理的邏輯放到ViewModel中:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var navigable = DataContext as INavigable;
navigable.OnNavigatedTo(e.Parameter);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
var navigable = DataContext as INavigable;
navigable.OnNavigatedFrom(e.Parameter);
}
}
看到這裏,是不是覺得釋放資源什麼的也是一件非常的簡單的事情呢?但是騷年!Too young too simple, sometimes naive!僅僅這樣就夠了嗎?我們需要再回到Cleanup方法,既然ViewModel可以通過OnNavigatedFrom來釋放資源,但如果ViewModel並沒有,或者說不需要導航事件,又該如何處理呢。例如某個MainPage對應的MainViewModel中存在一個ContactViewModel的列表:
public class MainViewModel : ViewModelBase, INavigable
{
ObservableCollection<ContactViewModel> ContactList { get; set; }
}
而ContactViewModel僅僅是對Contact數據對象做的封裝,並不存在導航事件。這時候,如果不需要ContactList常駐內存,MainViewModel的OnNavigatedFrom的方法就長成這樣了:
public void OnNavigatedFrom(object obj)
{
base.Cleanup();
this.xxxxEvent -= xxxxHandler;
foreach (var contact in ContactList)
{
contact.Cleanup();
}
ContactList.Clear();
}
沒有錯哦,仍然是繼承了ViewModelBase的ContactViewModel自己來釋放內部的資源,但是Cleanup的調用是由外部引用ContactList的MainViewModel來發起的。
本篇就ViewModelBase的繼承使用展開了討論,介紹了一點俺平時使用的經驗,包括如何使用導航事件和釋放資源。還希望能給萌新們啓發,老司機們輕拍。
另外MvvmLight框架使用入門系列可能會暫停一下(反正也沒人看……),因爲俺接下來要開始搞Win10的Universal App了,挖咔咔!