如何创建Mvvmlight WPF程序

创建步骤

创建Mvvm结构的开发环境很简单,首先在View层的App.xaml文件中引用ViewModel层,设置全局资源。

其次,给主窗体设置数据源并设置绑定

接着,在ViewModel层新建定位器和主窗体绑定的类

定位器中相关代码

如果是使用NuGet导入MvvmLight,就会自动创建好了这个ViewModelLocator类,在类的构造函数中会带有这个

ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

关于这个我也不清楚有什么作用,毕竟SimpleIoc.Default中就继承了IServiceLocator。

这里使用的SimpleIoc,可以去以下链接浏览,这里对mvvmLight做了详细的介绍:

https://www.cnblogs.com/maanshancss/p/5794780.html

简要介绍一下,就是使用了SimpleIoc容器,使用依赖注入的方式将ViewModel类注册进去容器中,也可将一些服务,公共全局变量注册进去,例如:

        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            SimpleIoc.Default.Register<IDataService, DataService>();//公共变量交换器
            SimpleIoc.Default.Register<MainWindowViewModel>();//主窗口ViewModel
            SimpleIoc.Default.Register<TextViewModel>();//测试窗口ViewModel
            //创建导航服务,并将导航服务注册进容器
            var navigationService = this.CreateNavigationService();
            SimpleIoc.Default.Register<INavigationService>(() => navigationService);
        }
        /// <summary>
        /// 主窗口
        /// </summary>
        public MainWindowViewModel MainWindowViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainWindowViewModel>();
            }
        }
        /// <summary>
        /// 测试窗口
        /// </summary>
        public TextViewModel TextViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<TextViewModel>();
            }
        }
        /// <summary>
        /// 创建导航服务
        /// </summary>
        /// <returns></returns>
        private INavigationService CreateNavigationService()
        {
            var navigationService = new NavigationService();
            //主窗口
            navigationService.Configure(ViewNames.MAIN_VIEW, new Uri("/MainWindow.xaml", UriKind.RelativeOrAbsolute));
            //测试窗口页
            navigationService.Configure(ViewNames.TEXT_VIEW, new Uri("/TextView.xaml", UriKind.RelativeOrAbsolute));
            return navigationService;
        }

在这里只是在ViewModelLocator构造函数中将ViewModel注册进 SimpleIoc容器,而创建导航服务的作用在于设置各个页面的地址使用Key值标识,后续可以通过Key值进行定位导航的是那个页面,则会跳转到这个key所指向的View。以下为封装的一个导航服务类,主要处理页面导航的一些记录以及实现界面跳转和回退等。

    /// <summary>
    /// 封装导航服务
    /// </summary>
    public class NavigationService : ViewModelBase, INavigationService
    {
        #region 成员变量
        /// <summary>
        /// 历史列表
        /// </summary>
        private readonly List<string> historic;
        /// <summary>
        /// 当前导航页
        /// </summary>
        private string currentPageKey;
        /// <summary>
        /// 是否正在导航
        /// </summary>
        private bool isNavigating;
        /// <summary>
        /// 导航页面地址Key
        /// </summary>
        private readonly Dictionary<string, Uri> pageUrlByKey;
        /// <summary>
        /// 导航页单例Key
        /// </summary>
        private readonly Dictionary<string, Page> pageInstancesByKey;
        /// <summary>
        /// 当前框架容器
        /// </summary>
        private Frame currentFrame = null;
        /// <summary>
        /// 导航参数
        /// </summary>
        public object Parameter { get; private set; }
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        public NavigationService()
        {
            pageUrlByKey = new Dictionary<string, Uri>();
            pageInstancesByKey = new Dictionary<string, Page>();
            historic = new List<string>();
        }

        /// <summary>
        /// 当前导航页面Key
        /// </summary>
        public string CurrentPageKey
        {
            get
            {
                return currentPageKey;
            }

            private set
            {
                Set(() => CurrentPageKey, ref currentPageKey, value);
            }
        }
        /// <summary>
        /// 回退
        /// </summary>
        public void GoBack()
        {
            if (historic.Count > 1)
            {
                historic.RemoveAt(historic.Count - 1);

                NavigateTo(historic.Last(), "Back");
            }
        }
        /// <summary>
        /// 跳转下一个页面
        /// </summary>
        /// <param name="pageKey"></param>
        public void NavigateTo(string pageKey)
        {
            NavigateTo(pageKey, "Next");
        }
        /// <summary>
        /// 页面跳转
        /// </summary>
        /// <param name="pageKey">页面配置的key</param>
        /// <param name="parameter">参数</param>
        public void NavigateTo(string pageKey, object parameter)
        {
            if (isNavigating)
            {
                return;
            }
            isNavigating = true;
            try
            {
                lock (pageUrlByKey)
                {
                    if (!pageUrlByKey.ContainsKey(pageKey))
                    {
                        throw new ArgumentException(string.Format("No such page: {0} ", pageKey), "pageKey");
                    }
                    if (currentFrame == null)
                    {
                        var frame = FindControlHelper.Instance.GetChildObject<Frame>(Application.Current.MainWindow, "MainFrame");
                        if (frame != null)
                        {
                            this.currentFrame = frame;
                            this.currentFrame.Navigated -= CurrentFrame_Navigated;
                            this.currentFrame.Navigated += CurrentFrame_Navigated;
                        }
                        else
                        {
                            throw new Exception("frame is null");
                        }
                    }
                    bool reloadPage = false;
                    if (parameter != null)
                    {
                        bool.TryParse(parameter.ToString(), out reloadPage);
                    }
                    if (this.currentFrame != null)
                    {
                        if (this.pageInstancesByKey.ContainsKey(pageKey) && reloadPage == false)
                        {
                            this.currentFrame.NavigationService.Navigate(this.pageInstancesByKey[pageKey]);
                        }
                        else
                        {
                            this.currentFrame.Source = pageUrlByKey[pageKey];
                            this.CurrentPageKey = pageKey;
                        }
                    }
                    Parameter = parameter;
                    if (parameter.ToString().Equals("Next"))//记录可以用于回退功能的页面跳转历史
                    {
                        historic.Add(pageKey);
                    }
                    CurrentPageKey = pageKey;
                }
            }
            finally
            {
                isNavigating = false;
            }
        }
        /// <summary>
        /// 配置导航页面地址与Key值字典
        /// </summary>
        /// <param name="key">Key值</param>
        /// <param name="pageType">页面地址</param>
        public void Configure(string key, Uri pageType)
        {
            lock (pageUrlByKey)
            {
                if (pageUrlByKey.ContainsKey(key))
                {
                    pageUrlByKey[key] = pageType;
                }
                else
                {
                    pageUrlByKey.Add(key, pageType);
                }
            }
        }
        #region 辅助方法
        /// <summary>
        /// 框架中执行页面跳转后
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CurrentFrame_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            Page page = e.Content as Page;
            this.AddPage(CurrentPageKey, page);
        }
        /// <summary>
        /// 添加实例了的页面以及key值
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="instance">页面</param>
        private void AddPage(string key, Page page)
        {
            lock (pageInstancesByKey)
            {
                if (!pageInstancesByKey.ContainsKey(key) && page != null)
                {
                    if (this.Parameter == null)
                    {
                        pageInstancesByKey.Add(key, page);
                    }
                    else
                    {
                        bool reloadPage = false;
                        bool.TryParse(this.Parameter.ToString(), out reloadPage);
                        if (!reloadPage)
                        {
                            pageInstancesByKey.Add(key, page);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 获取导航的页面
        /// </summary>
        /// <param name="parent">导航对象(页面)</param>
        /// <param name="name">对象名称(页面名称)</param>
        /// <returns></returns>
        private FrameworkElement GetDescendantFromName(DependencyObject parent, string name)
        {
            var count = VisualTreeHelper.GetChildrenCount(parent);

            if (count < 1)
            {
                return null;
            }

            for (var i = 0; i < count; i++)
            {
                var frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
                if (frameworkElement != null)
                {
                    if (frameworkElement.Name == name)
                    {
                        return frameworkElement;
                    }

                    frameworkElement = GetDescendantFromName(frameworkElement, name);
                    if (frameworkElement != null)
                    {
                        return frameworkElement;
                    }
                }
            }
            return null;
        }
        #endregion
    }

 而下面这些将ViewModel作为属性通过单例实例化自身的代码并不会都在程序启动就实例,而是使用到哪个页面,这个页面绑定的数据源是哪个ViewModel,通过服务定位模式,将会来ViewModelLocator中找到这个ViewModel的属性执行该属性的这段单例。

 程序运行的步骤如下:

依赖注入理解

        /// <summary>
        /// 主窗口
        /// </summary>
        public MainWindowViewModel MainWindowViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainWindowViewModel>();
            }
        }
        /// <summary>
        /// 测试窗口
        /// </summary>
        public TextViewModel TextViewModel
        {
            get
            {
                return ServiceLocator.Current.GetInstance<TextViewModel>();
            }
        }

从上面代码可以看到ViewModel实例的方式是通过单例的方式,单例方式不能够想new一个类那样传递参数。

那如果我的ViewModel构造函数需要传参整么办。这就用到了SimpleIoc中的依赖注入了,在一开始我们就将相关的参数注册进了容器中,这个时候实例化ViewModel的时候,就会自动找到参数类型符合的变量作为构造参数去执行。当然如果构造函数所需的参数在容器中没有,则将会自动创建一个该类型的参数

启动程序后,这个值显示到了界面,这个界面的参数就是通过依赖注入原理在ViewModel中进行传递的。

再次推荐两个能够提供理解MvvmLigh和依赖注入、服务定位模式(Service Locator Pattern)的链接

https://www.cnblogs.com/maanshancss/p/5794780.html

https://www.cnblogs.com/gaochundong/archive/2013/04/12/service_locator_pattern.html

比较推荐第一个链接吧。

还有一个关于这个服务定位我是出于懵逼的状态的,这个SimpleIoc自身就继承了,为什么还要去使用ServiceLocator

     public ViewModelLocator()
        {
            //ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            SimpleIoc.Default.Register<IDataService, DataService>();//公共变量交换器
            SimpleIoc.Default.Register<MainWindowViewModel>();//主窗口ViewModel
            SimpleIoc.Default.Register<TextViewModel>();//测试窗口ViewModel
            //创建导航服务
            var navigationService = this.CreateNavigationService();
            SimpleIoc.Default.Register<INavigationService>(() => navigationService);
        }
        /// <summary>
        /// 主窗口
        /// </summary>
        public MainWindowViewModel MainWindowViewModel
        {
            get
            {
                //return ServiceLocator.Current.GetInstance<MainWindowViewModel>();
                return SimpleIoc.Default.GetInstance<MainWindowViewModel>();//这样使用也可以

            }
        }
发布了36 篇原创文章 · 获赞 8 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_35351282/article/details/105715930