MvvmLight入门教程

举报
步步为营 发表于 2023/02/21 14:26:54 2023/02/21
【摘要】 MvvmLight是一款应用广泛的MVVM框架

MvvmLight

MvvmLight主要程序库

image-20230220151724416

ViewModelLocator

在.netFramework环境下nuget安装MvvmLight会自动安装依赖项MvvmLightLibs库,如果在.net core环境下需要手动安装MvvmLightLibs库。

.netFramework环境下安装完成后,在ViewModel目录下具有一个ViewModelLocator类,该类的作用是提供依赖注入容器和相关属性。

public class ViewModelLocator
{
    public ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        //判断是否为设计时
        //if (ViewModelBase.IsInDesignModeStatic)
        SimpleIoc.Default.Register<MainViewModel>();//注册MainViewModel
        SimpleIoc.Default.Register<SubWindowVM>();//注册SubWindowVM
    }

    public MainViewModel Main
    {
        get
        {	//返回MainViewModel实例,必须在前面进行注册后才可以
            return ServiceLocator.Current.GetInstance<MainViewModel>();
        }
    }

    public SubWindowVM SubWin
    {
        get
        {
            return ServiceLocator.Current.GetInstance<SubWindowVM>();
        }
    }

    public static void Cleanup(){}
}

另外,在App.xaml中增加ViewModelLocator资源

<Application.Resources>
    <ResourceDictionary>
        <vm:ViewModelLocator  x:Key="Locator" />
    </ResourceDictionary>
</Application.Resources>

这样在MainWindow中就可以这样注册DataContext

<Window DataContext="{Binding Source={StaticResource Locator}, Path=Main}">

ObservableObject

ObservableObject继承了INotifyPropertyChanged,主要是简化了属性改变事件的触发

public class MainModel : ObservableObject
{
    private int myVar;

    public int MyProperty
    {
        get { return myVar; }
        set
        {
            Set(ref myVar, value);
        }
    }

    private int _value;

    public int Value
    {
        get { return _value; }
        set {
            _value = value;
            RaisePropertyChanged("Value");
        }
    }
}

ViewModelBase

ViewModelBase继承了ObservableObjectIcleanup接口,自定义的ViewModel可以自己选择是否要继承ViewModelBase,该类中IsInDesignMode可以用来判断是否属于设计状态中。该类中另一常用的方法是Cleanup虚方法,用于进行资源释放。

资源释放

有时在某个VM中开启一个线程,当关闭View时,对应的VM中的线程仍然不会释放,此时就需要对VM中的线程资源进行释放。

public class SubWindowVM : ViewModelBase
{
    bool flag = true;

    private int myVar;

    public int MyProperty
    {
        get { return myVar; }
        set { Set(ref myVar, value); }//效果相同
        //set { myVar = value; RaisePropertyChanged(); }
    }
    public SubWindowVM()
    {
        Task.Run(() =>
        {
            int a = 0;
            while (flag)
            {
                Debug.WriteLine($"=========={a++}=========");
                Task.Delay(1000).Wait();
            }
        });
    }
    //需要明确调用
    //释放VM中的资源,如线程,如果不调用,线程无法停止
    //瞬时模式,每次调用后需要清理
    public override void Cleanup()
    {
        base.Cleanup();
        flag = false;
    }
}

MvvmLight框架不会自动调用Cleanup方法,需要手动调用。比如当窗体关闭的时候,调用Cleanup方法,当时在View中直接调用VM中的方法违反了MVVM原则。不过,可以利用ViewModelLocator中的静态的Cleanup进行统一的资源释放。

ViewModelLocator中定义Cleanup的泛型方法

public static void Cleanup<T>() where T:ViewModelBase
{
    ServiceLocator.Current.GetInstance<T>().Cleanup();//释放资源

    SimpleIoc.Default.Unregister<T>();//释放对象
    SimpleIoc.Default.Register<T>();//为下次打开可以用
}

这样在window的Closed事件中,可以直接调用ViewModelLocator中的Cleanup方法

private void Window_Closed(object sender, EventArgs e)
{
    ViewModelLocator.Cleanup<SubWindowVM>();
}

RelayCommand

RelayCommand继承自ICommand,用法和RouteCommand类似

public ICommand BtnCommand
{
    //get => new RelayCommand(() => { });
    //带参数
    get => new RelayCommand<string>(obj =>
    { });
    //如果泛型定义为int则会转换失效,这里的泛型一般是引用,不能是值类型,因为用的反射机制
}
//任意事件的处理
public ICommand MouseCommand
{
    get => new RelayCommand<object>(obj=>
    { });
}

Messenger

常规使用

Messenger使用非常灵活,可以在VM中出发,在View中执行逻辑。

在VM中发送

Messenger.Default.Send<int>(123);//广播方式

在View中注册

Messenger.Default.Register<int>(this,ReceiveMsg1);//广播方式
private void ReceiveMsg1(int msg){}

如果不想以广播的方式进行发送,可以使用指定token的方式

  • 发送Messenger.Default.Send<int>(123, "M1");//指定Token
  • 注册Messenger.Default.Register<int>(this,"M1",ReceiveMsg2);//指定token

现在有如下需求,要在VM中触发,需要打开一个子窗口,并且还要得到子窗口是否打开的返回值。基于MVVM思想,子窗口属于View层,主窗口才能调用。

  • 新建一个类MessageAction
public class MessageAction<TValue,MResult>
{
     public TValue value { set; get; }
     public Action<MResult> State { set; get; }
     public MessageAction(Action<MResult> state)
     {
         State = state;
     }
}
  • MainWindow
MessageAction<string, bool> ma = new MessageAction<string, bool>(GetReturn);
ma.value = "123";
Messenger.Default.Send<MessageAction<string, bool>>(ma);

//可以得到返回值
private void GetReturn(bool b)
{

}
  • MainViewModel
Messenger.Default.Register<MessageAction<string,bool>>(this, ReceiveMsg);


private void ReceiveMsg(MessageAction<string,bool> msg)
{
    bool s=  new SubWindow().ShowDialog() ==true;//此时需要得到ShowDialog的返回值,true、false
    //使用委托来实现返回值给发送消息者
    string ss= msg.value;
    msg.State.Invoke(s);
    //为什么这样做,因为动作的触发点在VM中,VM中做触发的时候需要进行状态判断,也就是需要有返回值
    //需要View中进行窗口操作,并且返回状态
}

NotificationMessage

发送一个字符串给接受者

//触发
NotificationMessage nm = new NotificationMessage("hello");
Messenger.Default.Send<NotificationMessage>(nm);
//注册
Messenger.Default.Register<NotificationMessage>(this, ReceiveNM);

PropertyChangedMessage

传递一个字符串属性名和值发送给接收者,用于将PropertyChanged事件传播给收件人。

private string _value;

public string Value
{
    get { return _value; }
    set
    {
        _value = value;
        //有两种方式发送PropertyChangedMessage
        //1.broadcast:true
        Set(ref _value, value,broadcast:true);
        //2.Messenger.Default.Send
        PropertyChangedMessage<string> ddd = new PropertyChangedMessage<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()
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。