MvvmLight
MvvmLight主要程序库
ViewModelLocator
在.netFramework环境下nuget安装MvvmLight
会自动安装依赖项MvvmLightLibs
库,如果在.net core环境下需要手动安装MvvmLightLibs
库。
.netFramework环境下安装完成后,在ViewModel目录下具有一个ViewModelLocator
类,该类的作用是提供依赖注入容器和相关属性。
publicclassViewModelLocator
{
publicViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() =>SimpleIoc.Default);
//判断是否为设计时
//if (ViewModelBase.IsInDesignModeStatic)
SimpleIoc.Default.Register<MainViewModel>();//注册MainViewModel
SimpleIoc.Default.Register<SubWindowVM>();//注册SubWindowVM
}
publicMainViewModelMain
{
get
{ //返回MainViewModel实例,必须在前面进行注册后才可以
returnServiceLocator.Current.GetInstance<MainViewModel>();
}
}
publicSubWindowVMSubWin
{
get
{
returnServiceLocator.Current.GetInstance<SubWindowVM>();
}
}
publicstaticvoidCleanup(){}
}
另外,在App.xaml中增加ViewModelLocator
资源
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator"/>
</ResourceDictionary>
</Application.Resources>
这样在MainWindow中就可以这样注册DataContext
<WindowDataContext="{Binding Source={StaticResource Locator}, Path=Main}">
ObservableObject
ObservableObject
继承了INotifyPropertyChanged
,主要是简化了属性改变事件的触发
publicclassMainModel : ObservableObject
{
privateintmyVar;
publicintMyProperty
{
get { returnmyVar; }
set
{
Set(refmyVar, value);
}
}
privateint_value;
publicintValue
{
get { return_value; }
set {
_value=value;
RaisePropertyChanged("Value");
}
}
}
ViewModelBase
ViewModelBase
继承了ObservableObject
和Icleanup
接口,自定义的ViewModel可以自己选择是否要继承ViewModelBase,该类中IsInDesignMode
可以用来判断是否属于设计状态中。该类中另一常用的方法是Cleanup虚方法,用于进行资源释放。
资源释放
有时在某个VM中开启一个线程,当关闭View时,对应的VM中的线程仍然不会释放,此时就需要对VM中的线程资源进行释放。
publicclassSubWindowVM : ViewModelBase
{
boolflag=true;
privateintmyVar;
publicintMyProperty
{
get { returnmyVar; }
set { Set(refmyVar, value); }//效果相同
//set { myVar = value; RaisePropertyChanged(); }
}
publicSubWindowVM()
{
Task.Run(() =>
{
inta=0;
while (flag)
{
Debug.WriteLine($"=========={a++}=========");
Task.Delay(1000).Wait();
}
});
}
//需要明确调用
//释放VM中的资源,如线程,如果不调用,线程无法停止
//瞬时模式,每次调用后需要清理
publicoverridevoidCleanup()
{
base.Cleanup();
flag=false;
}
}
MvvmLight框架不会自动调用Cleanup方法,需要手动调用。比如当窗体关闭的时候,调用Cleanup方法,当时在View中直接调用VM中的方法违反了MVVM原则。不过,可以利用ViewModelLocator中的静态的Cleanup进行统一的资源释放。
在ViewModelLocator
中定义Cleanup的泛型方法
publicstaticvoidCleanup<T>() whereT:ViewModelBase
{
ServiceLocator.Current.GetInstance<T>().Cleanup();//释放资源
SimpleIoc.Default.Unregister<T>();//释放对象
SimpleIoc.Default.Register<T>();//为下次打开可以用
}
这样在window的Closed事件中,可以直接调用ViewModelLocator中的Cleanup方法
privatevoidWindow_Closed(objectsender, EventArgse)
{
ViewModelLocator.Cleanup<SubWindowVM>();
}
RelayCommand
RelayCommand
继承自ICommand
,用法和RouteCommand类似
publicICommandBtnCommand
{
//get => new RelayCommand(() => { });
//带参数
get=>newRelayCommand<string>(obj=>
{ });
//如果泛型定义为int则会转换失效,这里的泛型一般是引用,不能是值类型,因为用的反射机制
}
//任意事件的处理
publicICommandMouseCommand
{
get=>newRelayCommand<object>(obj=>
{ });
}
Messenger
常规使用
Messenger使用非常灵活,可以在VM中出发,在View中执行逻辑。
在VM中发送
Messenger.Default.Send<int>(123);//广播方式
在View中注册
Messenger.Default.Register<int>(this,ReceiveMsg1);//广播方式
privatevoidReceiveMsg1(intmsg){}
如果不想以广播的方式进行发送,可以使用指定token的方式
- 发送
Messenger.Default.Send<int>(123, "M1");//指定Token
- 注册
Messenger.Default.Register<int>(this,"M1",ReceiveMsg2);//指定token
现在有如下需求,要在VM中触发,需要打开一个子窗口,并且还要得到子窗口是否打开的返回值。基于MVVM思想,子窗口属于View层,主窗口才能调用。
- 新建一个类MessageAction
publicclassMessageAction<TValue,MResult>
{
publicTValuevalue { set; get; }
publicAction<MResult>State { set; get; }
publicMessageAction(Action<MResult>state)
{
State=state;
}
}
- MainWindow
MessageAction<string, bool>ma=newMessageAction<string, bool>(GetReturn);
ma.value="123";
Messenger.Default.Send<MessageAction<string, bool>>(ma);
//可以得到返回值
privatevoidGetReturn(boolb)
{
}
- MainViewModel
Messenger.Default.Register<MessageAction<string,bool>>(this, ReceiveMsg);
privatevoidReceiveMsg(MessageAction<string,bool>msg)
{
bools= newSubWindow().ShowDialog() ==true;//此时需要得到ShowDialog的返回值,true、false
//使用委托来实现返回值给发送消息者
stringss=msg.value;
msg.State.Invoke(s);
//为什么这样做,因为动作的触发点在VM中,VM中做触发的时候需要进行状态判断,也就是需要有返回值
//需要View中进行窗口操作,并且返回状态
}
NotificationMessage
发送一个字符串给接受者
//触发
NotificationMessagenm=newNotificationMessage("hello");
Messenger.Default.Send<NotificationMessage>(nm);
//注册
Messenger.Default.Register<NotificationMessage>(this, ReceiveNM);
PropertyChangedMessage
传递一个字符串属性名和值发送给接收者,用于将PropertyChanged事件传播给收件人。
privatestring_value;
publicstringValue
{
get { return_value; }
set
{
_value=value;
//有两种方式发送PropertyChangedMessage
//1.broadcast:true
Set(ref_value, value,broadcast:true);
//2.Messenger.Default.Send
PropertyChangedMessage<string>ddd=newPropertyChangedMessage<string>(Value, _value, "Value");
Messenger.Default.Send<PropertyChangedMessage<string>>(ddd);
//3.使用Broadcast
Broadcast<string>(Value, _value, "Value")
}
}
DispatcherHelper
//多线程,先检查是否在UI线程,如果不在则把该线程挂上去
//不能直接使用,需要配套
DispatcherHelper.Initialize();//一般把这一句写在App对象中
DispatcherHelper.CheckBeginInvokeOnUI();//异步的
//这样也可以
Application.Current.Dispatcher.Invoke()