• 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 

  • 相关阅读:
    Android_程序未处理异常的捕获与处理
    instanceof关键字
    乐优商城项目爬坑
    [LeetCode]Isomorphic Strings
    [LeetCode]Contains Duplicate II
    [LeetCode]Valid Sudoku
    [LeetCode]Valid Anagram
    [LeetCode]Contains Duplicate
    [LeetCode]Single Number
    [LeetCode]Valid Number
  • 原文地址:https://www.cnblogs.com/gaoshang212/p/3961011.html
Copyright © 2020-2023  润新知