MAUI MVVM模式的ViewModel 基类设计

目录

1.基类设计

2.页面框架


1.基类设计

基类定义-BaseModel  实现数据更新通知接口

   public class BaseModel : System.ComponentModel.INotifyPropertyChanged
   {

       public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
       public void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
       {
           if (PropertyChanged != null)
           {
               PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
           }
       }
   }

ViewModel基类实现-BaseViewModel

UIElement对象使用类型VisualElement,它是ContentPage/ContentView 等的基类,但Window不是,因为MAUI框架的原因,Window的定义跟桌面端的Window不一样,MAUI的Window作为唯一主界面使用,同时Window对象无法作为对话框的形式弹出,所以Window就可以单独处理。

一般的设计逻辑是 实例化Page,然后再绑定ViewModel实例

this.BindingContext = new ViewModels.VM_MainWindow(this);

基类的设计是先初始化ViewModel实例,传入Page的类型,在初始化时同时初始化UI,再互相绑定。

下面是实现的代码:

    public class BaseViewModel : BaseModel
    {

        //程序的主命名空间
        public string UINameSapce = "";
        //类名
        public string UIElementName = "";
        public VisualElement UIElement { get; set; }
        
        private System.Type UIType;

        /// <summary>
        /// ViewModel基类  需要传UI界面的全名(FullName)参数
        /// </summary>
        /// <param name="uiType">UI界面的类型</param>
        public BaseViewModel(System.Type uiType)
        {
            try
            {
                UIType = uiType;
                UINameSapce = uiType.Namespace;
                SetUIElement();
                UIElement.BindingContext = this;
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// ViewModel基类,直接传入UI界面对象
        /// </summary>
        /// <param name="ui"></param>
        public BaseViewModel(VisualElement ui)
        {
            try
            {
                UIType = ui.GetType();
                UINameSapce = UIType.Namespace;
                UIElementName = UIType.FullName;
                UIElement = ui;
                UIElement.Loaded += Page_Loaded;
                UIElement.Unloaded += Page_Unloaded;
                //UIElement.BindingContext = this;
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

        #region 通过反射创建对应的UI元素

        /// <summary>
        /// 页面加载时触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void Page_Loaded(object sender, EventArgs e)
        {
            LoadData();
        }

        /// <summary>
        /// 页面退出时触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void Page_Unloaded(object sender, EventArgs e)
        {
            UnLoadData();
        }

        /// <summary>
        /// 页面加载时初始化数据
        /// 会在PageLoad页面触发
        /// 重写后,静态页面按需调用
        /// </summary>
        public virtual void LoadData()
        {

        }

        /// <summary>
        /// 页面销毁时处理数据
        /// 会在PageUnLoad页面触发
        /// 重写后,静态页面按需调用
        /// </summary>
        public virtual void UnLoadData()
        {

        }

        /// <summary>
        /// 根据VM名称,反射对应类型的UI实例
        /// </summary>
        internal void SetUIElement()
        {
            System.Type childType = this.GetType();//获取子类的类型   
            UIElementName = this.GetType().FullName;
            UIElement = (VisualElement)GetElement();
            UIElement.Loaded += Page_Loaded;
            UIElement.Unloaded += Page_Unloaded;
        }

        /// <summary>
        /// 用UI全名称(FullName)进行反射UI实例
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="UIElementFullName"></param>
        /// <returns></returns>
        public object GetElement()
        {
            if (UIType != null)
            {
                return System.Activator.CreateInstance(UIType);
            }
            else
            {
                throw new System.Exception("UIType为空");
            }
        }

        #endregion

        #region   异步线程

        /// <summary>
        /// 异步方法执行事件
        /// </summary>
        public void AsyncLoad(System.Action action)
        {
            System.IAsyncResult result = action.BeginInvoke((iar) =>
            {
            }, null);
        }

        /// <summary>
        /// 异步方法执行事件 带回调方法
        /// </summary>
        public void AsyncLoad(System.Action action, System.Action callback)
        {
            System.IAsyncResult result = action.BeginInvoke((iar) =>
            {
                this.DoMenthodByDispatcher(callback);
            }, null);
        }

        /// <summary>
        /// 异步方法执行事件 带参数及带回调方法
        /// </summary>
        public void AsyncLoad<T>(System.Action<T> action, T para, System.Action callback)
        {
            System.IAsyncResult result = action.BeginInvoke(para, (iar) =>
            {
                this.DoMenthodByDispatcher(callback);
            }, null);
        }

        /// <summary>
        /// 异步方法执行事件 带参数及带回调方法
        /// </summary>
        public void AsyncLoad<T, R>(System.Func<T, R> action, T para, System.Action<R> callback)
        {
            System.IAsyncResult result = action.BeginInvoke(para, (iar) =>
            {
                var res = action.EndInvoke(iar);
                this.DoMenthodByDispatcher<R>(callback, res);
            }, null);
        }

        /// <summary>
        /// 异步方法执行事件 带回调方法
        /// </summary>
        /// <typeparam name="R"></typeparam>
        /// <param name="action"></param>
        /// <param name="callback"></param>
        public void AsyncLoad<R>(System.Func<R> action, System.Action<R> callback)
        {
            System.IAsyncResult result = action.BeginInvoke((iar) =>
            {
                var res = action.EndInvoke(iar);
                this.DoMenthodByDispatcher<R>(callback, res);
            }, null);
        }

        /// <summary>
        /// 使用UI线程执行Action 带参数  默认 异步(BeginInvoke)执行
        /// </summary>
        /// <typeparam name="T">泛型参数</typeparam>
        /// <param name="action">执行的方法</param>
        /// <param name="obj">参数</param>
        /// <param name="isAsync">是否异步  true-异步 false-同步</param>
        public void DoMenthodByDispatcher<T>(System.Action<T> action, T obj, bool isAsync = true)
        {
            if (isAsync)
            {
                UIElement?.Dispatcher.DispatchAsync(new System.Action(() =>
                {
                    action(obj);
                }));
            }
            else
            {
                UIElement?.Dispatcher.Dispatch(new System.Action(() =>
                {
                    action(obj);
                }));
            }
        }

        /// <summary>
        ///使用UI线程执行Action  默认 异步(BeginInvoke)执行
        /// </summary>
        /// <param name="action"></param>
        /// <param name="isAsync">是否异步  true-异步 false-同步</param>
        public void DoMenthodByDispatcher(System.Action action, bool isAsync = true)
        {
            if (isAsync)
            {
                UIElement?.Dispatcher.DispatchAsync(new System.Action(() =>
                {
                    action();
                }));
            }
            else
            {
                UIElement?.Dispatcher.Dispatch(new System.Action(() =>
                {
                    action();
                }));
            }
        }
        #endregion  
    }

定义ViewModel的类:

Page类(UI):PersonalPage

对应的VM:VM_PersonalPage

    public class VM_PersonalPage:BaseViewModel
    {
        public VM_PersonalPage() : base(typeof(PersonalPage)) { }
    }

2.页面框架

MAUI,我使用的是以Window作为主界面,然后各个页面使用ContentView 进行切换

缺点就是得自己做导航缓存

MAUI不能使用Frame作为导航切换的框架了,更改下Border

<ScrollView Grid.Row="0">
    <Border
        x:Name="MainFrame"
        Content="{Binding PageUri}"
        HorizontalOptions="Fill"
        VerticalOptions="Fill" />
</ScrollView>

对应的VM实现:

页面切换,切换前做个判断,如果是同一个界面,则不再处理

传入的是Type对象,则进行创建实例,再切换

传入的是Object对象,则直接使用对象进行切换

切换的使用示例:

PageJump(typeof(VM_HomePage));

以下是页面切换部分的实现代码:

        #region 页面跳转

        private ContentView _pageUri;
        /// <summary>
        /// 加载的页面属性
        /// </summary>
        public ContentView PageUri
        {
            get { return _pageUri; }
            set
            {
                _pageUri = value;
                OnPropertyChanged();
            }
        }

        /// <summary>
        /// 页面跳转锁对象
        /// </summary>
        private bool _lock_pagejump = false;

        /// <summary>
        /// 在跳转之前检查状态是否可以进行跳转界面
        /// </summary>
        /// <param name="obj"></param>
        private bool BeforeJumpCheck(object obj)
        {
            bool isPass = false;
            if (!_lock_pagejump)
            {
                try
                {
                    _lock_pagejump = true;

                    //如果同一页面则不刷新
                    if (obj is BaseViewModel && PageUri?.GetType().FullName == (((BaseViewModel)obj).UIElement as ContentView)?.GetType().FullName)
                    {
                        return false;
                    }

                    isPass = true;
                }
                catch (Exception ex)
                {
                    LogOperate.Error("BeforeJumpCheck 异常", ex);
                }
                finally
                {
                    _lock_pagejump = false;
                }
            }
            return isPass;
        }

        /// <summary>
        /// 页面跳转
        /// </summary>
        /// <param name="vmtype">ViewModel 的类型 typeof()</param>
        /// <param name="args">ViewModel 的构造函数入参</param>
        internal void PageJump(Type vmtype, object[] args = null)
        {
            MainWin?.Dispatcher.Dispatch(() =>
            {
                object obj = null;
                //根据类型进行反射生成实例
                if (args != null && args.Length > 0)
                {
                    obj = vmtype.Assembly.CreateInstance(vmtype.FullName, false, System.Reflection.BindingFlags.CreateInstance, null, args, null, null);
                }
                else
                {
                    obj = vmtype.Assembly.CreateInstance(vmtype.FullName);
                }
                if (BeforeJumpCheck(obj))
                {
                    PageUri = (obj as BaseViewModel).UIElement as ContentView;
                }
                else
                {
                    obj = null;
                }
            });
        }

        /// <summary>
        /// 页面跳转,加载缓存中的vm数据
        /// </summary>
        /// <param name="vmobject">vm对象</param>
        internal void PageJump(object vmobject)
        {
            MainWin?.Dispatcher.Dispatch(() =>
            {
                if (vmobject is BaseViewModel)
                {
                    if (BeforeJumpCheck(vmobject))
                    {
                        PageUri = (vmobject as BaseViewModel).UIElement as ContentView;
                    }
                    else
                    {
                        vmobject = null;
                    }
                }
            });
        }

        #endregion

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rotion_深

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值