ContentControl 与 ViewModel (二)

上文说到 可以使用DataTemplateSelector。

其实等于是用 DataTemplateSelector + 动态创建DataTemplate来实现。

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

namespace ContentDemo
{
    class ContentDataTemplateSelector : DataTemplateSelector
    {
        //用来标示一下已经创建过的类型
        private readonly static Queue<Type> ViewModelQueue = new Queue<Type>();

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            do
            {
                if (item == null
                    || item is UIElement)
                {
                    break;
                }

                // item 就是你的ViewModel的类型,在这主要就是找到ViewModel对应的View
                // 好多框架中会在启动时构建自己的Map,来对应ViewModel
                // 比如 Caliburn.Micro 框架中默认 是以名称对应的 XXXViewModel 和 XXXView,来构建Map的
                //      MvvmCross 框架中不仅提供了 名称,还提供了 Attribute的对应方式

                // 在这为了演示方便,我就直接反射对应名称的 View了。

                var viewModelType = item.GetType();

                if (ViewModelQueue.Contains(viewModelType))
                {
                    break;
                }

                var viewModelName = viewModelType.Name;

                string name = null;
                var index = viewModelName.LastIndexOf("ViewModel", StringComparison.OrdinalIgnoreCase);
                if (index > 0)
                {
                    name = viewModelName.Substring(0, index);
                }

                if (string.IsNullOrEmpty(name))
                {
                    break;
                }

                var viewName = string.Format("{0}.{1}{2}", viewModelType.Namespace, name, "View");

                var view = viewModelType.Assembly.GetType(viewName, false, true);

                if (view != null)
                {
                    var dataTemplate = CreateDataTemplate(view, viewModelType);

                    var dtkey = dataTemplate.DataTemplateKey;

                    if (dtkey != null)
                    {
                        Application.Current.Resources.Add(dtkey, dataTemplate);
                        ViewModelQueue.Enqueue(viewModelType);
                    }

                    return dataTemplate;
                }

            } while (false);

            return base.SelectTemplate(item, container);
        }

        /// <summary>
        /// 创建DataTemplate
        /// </summary>
        /// <param name="viewType"></param>
        /// <param name="viewModelType"></param>
        /// <returns></returns>
        private DataTemplate CreateDataTemplate(Type viewType, Type viewModelType)
        {
            const string xamlTemplate = "<DataTemplate DataType=\"{{x:Type vm:{0}}}\"><v:{1} /></DataTemplate>";
            var xaml = String.Format(xamlTemplate, viewModelType.Name, viewType.Name);

            var context = new ParserContext();

            context.XamlTypeMapper = new XamlTypeMapper(new string[0]);
            context.XamlTypeMapper.AddMappingProcessingInstruction("vm", viewModelType.Namespace, viewModelType.Assembly.FullName);
            context.XamlTypeMapper.AddMappingProcessingInstruction("v", viewType.Namespace, viewType.Assembly.FullName);

            context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
            context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
            context.XmlnsDictionary.Add("vm", "vm");
            context.XmlnsDictionary.Add("v", "v");

            var template = (DataTemplate)XamlReader.Parse(xaml, context);
            return template;
        }
    }
}

WPF有一个好处就是他的DataType,所以 你也不一定非要在选择器中处理,你可以在任意你想要的时机时,把DataTemplate加到资源里就可以了。

但是WinRT中,的DataTemplate,就没有DataType,就得通过 选择器来,返回对应的DataTemplate.

还有 WinRT中 不支持 ParserContext,就只能拼字符串构造了。

Xaml:

<Window x:Class="ContentDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContentDemo"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:MainViewModel x:Key="MainViewModel" />
        <!--<DataTemplate DataType="{x:Type local:FirstViewModel}">
            <local:FirstView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:SecondViewModel}">
            <local:SecondView />
        </DataTemplate>-->

        <local:ContentDataTemplateSelector x:Key="ContentDataTemplateSelector" />
    </Window.Resources>
    <Grid DataContext="{StaticResource MainViewModel}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ContentControl Grid.ColumnSpan="2" Content="{Binding Path=ViewModel}" ContentTemplateSelector="{StaticResource ContentDataTemplateSelector}" />
        <Button Grid.Row="1" Grid.Column="0" Content="ViewModel  1" Command="{Binding Path=FirstCommand}"/>
        <Button Grid.Row="1" Grid.Column="1" Content="ViewModel  2" Command="{Binding Path=SecondCommand}"/>
    </Grid>
</Window>

其实要是,用了很多的 ContentControl ,每一个 都去绑定 Selector,还是很烦人的一件事。

这时候,我们可以加一个 ContentControl 的 默认样式,让他默认就使用这个Selector.

<Application x:Class="ContentDemo.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ContentDemo"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <local:ContentDataTemplateSelector x:Key="ContentDataTemplateSelector" />

        <Style TargetType="{x:Type ContentControl}">
            <Setter Property="ContentTemplateSelector"
                    Value="{StaticResource ContentDataTemplateSelector}"/>
        </Style>
    </Application.Resources>
</Application>
<Window x:Class="ContentDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContentDemo"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:MainViewModel x:Key="MainViewModel" />
        <!--<DataTemplate DataType="{x:Type local:FirstViewModel}">
            <local:FirstView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:SecondViewModel}">
            <local:SecondView />
        </DataTemplate>-->

        <!--<local:ContentDataTemplateSelector x:Key="ContentDataTemplateSelector" />-->
    </Window.Resources>
    <Grid DataContext="{StaticResource MainViewModel}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ContentControl Grid.ColumnSpan="2" Content="{Binding Path=ViewModel}" />
        <Button Grid.Row="1" Grid.Column="0" Content="ViewModel  1" Command="{Binding Path=FirstCommand}"/>
        <Button Grid.Row="1" Grid.Column="1" Content="ViewModel  2" Command="{Binding Path=SecondCommand}"/>
    </Grid>
</Window>

源码:Code

转载请注明出处:http://www.cnblogs.com/gaoshang212/p/3961011.html

时间: 2024-10-14 05:08:07

ContentControl 与 ViewModel (二)的相关文章

ContentControl 与 ViewModel (一)

前阵子有人问我MVVM模式下,在View中嵌套View,切换View.想一想还是写下来吧. 主要就是用到 ContentControl 和 DataTemplate,这算是一种 ViewModel First 的思想吧. 其实好多MVVM的框架,也都提供这样的功能.在ContentControl绑定ViewModel,就可以显示 对应的View.比如 Caliburn.Micro(CM框架). MVVMLight应该是没有提供的,对他我本身不是很熟,以前知道他很轻,看过原码,真心没有多少,最近在

Python Flask高级编程

第1章 课程导语介绍课程的内容1-1 开宗明义1-2 课程维护与提问 第2章 Flask的基本原理与核心知识本章我们首先介绍Python官方推荐的最佳包与虚拟环境管理工具:Pipenv.接着我们来学习唯一URL原则.重定向.响应对象Response.2-1 鱼书是一个什么样的产品2-2 准备工作2-3 使用官方推荐的pipenv创建虚拟环境(很好用哦~)2-4 开发工具推荐2-5 设置开发工具的默认解释器2-6 flask最小原型与唯一URL原则2-7 路由的另一种注册方法2-8 app.run

后端MVC与前端MVVM的区别

一.概念 MVC 是后端的分层开发概念: MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel 二.详情图 原文地址:https://www.cnblogs.com/wangyuxue/p/11790886.html

WPF系列之二:解耦View层控件事件与ViewModel层事件的响应

以前的做法: 1.当项目的时间比较紧迫的时候,对UI层中控件的事件的处理,往往采取的是类似Winform中最简单的做法,直接做一个事件的Handler直接去调用VM层的方法. 2.控件只有一个Command属性,其它的事件的处理方法没有办法和ViewModel层进行解耦的时候往往也采取上面提到的方法. 如下图所示: 新的做法: 为了实现事件的处理与View层的解耦,我们可以利用WPF提供的附加属性来为需要的事件添加附加行为.附加属性扮演了一个在View层与Model层牵线的角色. 需要下面三个步

Android Jetpack -- ViewModel篇(二)

支持SharedPreference等使用到Application的相关 因为 SharedPreference 需要使用到 Application 来获取到,所以要想配合ViewModel还需要传入Application作为参数,当然,Jetpack已经为我们准备好了AndroidViewModel:感知应用上下文的ViewModel,它继承自ViewModel. 下面以一个非常简单的实例来说明 因为使用dataBinding以及SharedPreference需要在build.grade中添

Caliburn.Micro学习笔记(二)----Actions

Caliburn.Micro学习笔记(二)----Actions 上一篇已经简单说了一下引导类和简单的控件绑定 我的上一个例子里的button自动匹配到ViewModel事件你一定感觉很好玩吧 今天说一下它的Actions,看一下Caliburn.Micro给我们提供了多强大的支持 我们还是从做例子开始 demo的源码下载在文章的最后 例子1.无参数方法调用 点击button把textBox输入的文本弹出来 如果textbox里没有文本button不可点,看一下效果图 看一下前台代码 <Stac

WPF:MVVM模式下ViewModel关闭View

不外乎两种基本方法. 消息通知和参数传递. 一.消息通知 利用View里的IsEnable属性 原理是这样的: 1.UI中的IsEnabled绑定VM中的属性 2.UI的后台代码中,注册IsEnableChange事件,在这个事件里,检测到传过来的值满足某个条件,即可触发Close()命令 如此,VM控制自己那个属性就能达到关闭V的目的了. 二.参数传递. 根据参数传递的不同.分为传递函数和传递View对象. 1传递函数 该方法:需要三步. 1.重写ViewModel的构造函数 public P

MVVM下 利用反射动态创建ViewModel 实现单例

在MVVM一般情况下都会希望ViewModel 在整个应用程序中只有一份实例 传统的做法是用单例模式去实现 : public class ViewModelTest { private ViewModelTest() { } private static ViewModelTest viewModelInstace; public static ViewModelTest GetViewModelTestInstace() { if (viewModelInstace == null) { vi

ASP.NETMVC 视图(二)

ASP.NETMVC 视图(二) 前言 上篇中对于视图引擎只是做了简单的演示,对于真正的理解视图引擎的工作过程可能还有点模糊,本篇将会对由MVC框架提供给我们的Razor视图引擎的整个执行过程做一个粗略的讲解,目的在于让大家对Razor视图引擎的执行过程留个印象以便联想的思考到视图引擎的作用以及视图在MVC框架中的表示. ASP.NETMVC 视图 l  自定义视图引擎 l Razor视图引擎执行过程 l  Razor视图的依赖注入.自定义视图辅助器 l  分段.分部视图的使用 l Razor语