MvvmLight框架入门

news2025/2/25 21:35:41

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()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/361713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

page cache设计及实现

你好&#xff0c;我是安然无虞。 page cache的设计及实现 page cache 本质上也是一个哈希桶, 它是按照页的数量进行映射的. 当 central cache 向 page cache 申请内存时, page cache 先检查对应位置是否有span, 如果没有则向更大页去寻找一个span, 如果找到则分裂成两个. 比如…

Java基础常见面试题(五)

异常 Java 异常类层次结构图概览 &#xff1a; Exception 和 Error 有什么区别&#xff1f; Java 中&#xff0c;所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类 Exception&#xff08;异常&#xff09;和 Error&#xff08;错误…

DAMA-CDGA/CDGP数据治理认证考试如何报考?

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

LocalDNS

目录 文章目录目录本节实战DNS优化1、dns 5s 超时问题解决办法2、NodeLocal DNSCache实验软件关于我最后本节实战 实战名称&#x1f498; 实战&#xff1a;NodeLocal DNSCache-2022.7.30(测试成功)&#x1f498; 实战&#xff1a;NodeLocal DNSCache-2023.2.21(测试成功) DNS优…

强大的内置数据结构_元组(6,)

如约而至&#xff0c;紧接着上一期文章&#xff0c;小编将会陆续把全套的Python笔记将依次发放给大家&#xff0c;便于大家学习Python、期末备考、巩固基础等(这几期是公众号小插曲&#xff0c;后期发放编程技术的话主要还是会围绕Java来展开~感谢大家支持)元组python内置数据结…

Vulnhub靶场----2、DC-2

文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 DC-2下载地址&#xff1a;https://download.vulnhub.com/dc/DC-2.zip kali&#xff1a;192.168.144.148 DC-2&#xff1a;192.168.144.150 添加hosts文件&#xff1a;192.168.144.150 DC-2 二、渗透流程 nmap -A -…

FLV-初学总结

FLV-初学总结 从零开始仅学习了一下午的总结&#xff0c;本文非常稚嫩… 本文为纯初学者的学习记录&#xff0c;为了方便理解&#xff0c;内容未必严谨&#xff0c;可以用作纯新手的入门了解篇。本文主要的参考链接如下⬇️ 详细了解FLV&#xff1a;FLV官方文档&#xff08;Ve…

面向对象之三大特性(二)

5. 面向对象三大特性 继承、多肽、封装 5.1 继承 类的继承与现实生活中的父、子继承关系一样。被继承的类称为父类、基类或超类&#xff0c;继承者称为子类。Python 继承分为&#xff1a;单继承和多继承&#xff08;继承多个类&#xff09;。 class Parent1:pass class Par…

分析称勒索攻击在非洲、中东与中国增长最快

Orange Cyberdefense&#xff08;OCD&#xff09;于 2022 年 12 月 1 日发布了最新的网络威胁年度报告。报告中指出&#xff0c;网络勒索仍然是头号威胁 &#xff0c;也逐渐泛滥到世界各地。 报告中的网络威胁指的是企业网络中的某些资产被包括勒索软件在内的攻击进行勒索&…

Qt中修改界面类的类名时需要注意的几个修改点

有些时候因为一些原因&#xff0c;需要修改Qt中创建的界面类&#xff0c;需要特别注意几个修改点。 比如将test类修改为test2类 修改test.h名称为test2.h文件&#xff1b;修改test.cpp名称为test2.cpp文件&#xff1b;修改test.ui名称为test2.ui文件&#xff1b;修改pro文件中…

前端Docker部署方案

一、Docker容器和镜像概念 首先明确镜像和容器的概念。我们可以用 docker 构建一个镜像&#xff0c;这个镜像可以导入导出&#xff0c;用于传输&#xff0c;重复利用。然后如果把他 run 起来&#xff0c;则称为一个容器。容器是运行时&#xff0c;会包括运行时上下文&#xff…

Vue基础14之TodoList组件自定义事件、全局事件总线、TodoList全局事件总线

Vue基础14TodoList-组件自定义事件先改Header和Footer子组件&#xff0c;List先不考虑App.vueMyHeader.vueMyFooter.vue全局事件总线实现思路正规写法main.jsApp.vueStudent.vueSchool.vue总结&#xff1a;全局事件总线&#xff08;GlobalEventBus&#xff09;TodoList案例&…

【Java闭关修炼】MyBatis-接口代理的方式实现Dao层

【Java闭关修炼】MyBatis-接口代理的方式实现Dao层实现规则代码实现代理对象分析接口代理方式小结实现规则 映射配置文件中的名称空间必须和Dao层接口的全类名相同映射配置文件的增删改查标签的id属性必须和Dao层接口方法的参数相同映射配置文件中的增删改查标签的parameterTyp…

【C进阶】数据的存储

文章目录:star:1. 数据类型:star:2. 整形在内存中的存储2.1 存储规则2.2 存储模式2.3 验证大小端模式:star:3. 数据范围3.1 整形溢出3.2 数据范围的求解3.3 练习:star:4. 浮点型在内存中的存储4.1 浮点数的存储规则4.2 练习5. :star::star:总结(思维导图)⭐️1. 数据类型 在了…

独立产品灵感周刊 DecoHack #048 - 优秀独立开发产品推荐

如果有关注我的 Twitter 的朋友应该看到了&#xff0c;我上周末研究了两天 AI 画图&#xff0c;现在用 Ai 做图太强了&#xff0c;上周又升级 Stable Diffusion 玩了一下&#xff0c;和我去年试的时候相比强大了好多&#xff0c;而且插件LoRA模型玩法都还在快速迭代&#xff0c…

超详细解读!数据库表分区技术全攻略

更多内容可以关注微信公众号&#xff1a;老程序员刘飞 分区的定义 分区是一种数据库优化技术&#xff0c;它可以将大表按照一定的规则分成多个小表&#xff0c;从而提高查询和维护的效率。在分区的过程中&#xff0c;数据库会将数据按照分区规则分配到不同的分区中&#xff0…

【Linux】调试工具gdb的使用

环境&#xff1a;centos7.6&#xff0c;腾讯云服务器Linux文章都放在了专栏&#xff1a;【Linux】欢迎支持订阅&#x1f339;前言在前文&#xff0c;我们已经讲解了vim工具以及gcc/g的使用&#xff0c;我们可以进行编写代码以及编译代码了&#xff0c;但是还没有学习如何在Linu…

贪心--跳跃问题/拼接问题

跳跃问题 判断能否从数轴的最左端跳跃到最右边 变形&#xff1a;最少跳跃次数 45.跳跃游戏 题目链接 给定一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。 示…

博客界的至高神:属于自己的WordPress网站,你值得拥有!

【如果暂时没时间安装&#xff0c;可以直接跳转到最后先看展示效果】 很多朋友都想有一个对外展示的窗口&#xff0c;在那里放一些个人的作品或者其他想对外分享的东西。大部分人选择了在微博、公众号等平台&#xff0c;毕竟这些平台流量大&#xff0c;我们可以很轻易地把自己…

Jectpack -- Navigation了解与简单使用

简介 Navigation是一个可简化的Android导航的库和插件&#xff0c;换句话说&#xff0c;Navigation是用来规范管理Fragment的切换的&#xff0c;并且是通过可视化的方式来进行管理的。 ![[Pasted image 20230210083913.png]] 正常的跳转方法(其实有kotlin以后&#xff0c;代…