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 

你可能感兴趣的:(content)