MVVM - 层次结构& UWP的导航隐式绑定

时间:2017-04-04 20:15:44

标签: c# xaml mvvm uwp uwp-xaml

我一直在尝试使用层次结构和导航来跟踪这个MVVM教程:

https://www.tutorialspoint.com/mvvm/mvvm_hierarchies_and_navigation.htm

到目前为止,我已经完成了大部分教程,但是当谈到UWP时,似乎隐式绑定不适用于UWP,因此我无法复制本教程,因为即使我使用了x:DataType with x:编译器要求x:key属性以便用viewmodel绑定视图的键,我得到的只是我的viewmodel的全名,而不是能够看到实际的内容。

有人可以帮助我如何在没有MVVM Light或MVVM Cross等工具的帮助下,使用Plain MVVM模式在UWP中正确使用层次结构。

到目前为止,我将为您提供UWP应用程序的代码:

<Page
x:Class="MVVMHeirarchiesDemo.MainPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVMHeirarchiesDemo"
xmlns:views="using:MVVMHeirarchiesDemo.Views"
xmlns:viewmodel="using:MVVMHeirarchiesDemo.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<!--Anytime the current view model is set to an instance of a CustomerListViewModel,
it will render out a CustomerListView with the ViewModel is hooked up. It’s an order ViewModel,
it'll render out OrderView and so on.

We now need a ViewModel that has a CurrentViewModel property and some logic and commanding
to be able to switch the current reference of ViewModel inside the property.-->
<Page.DataContext>
    <viewmodel:MainPageViewModel/>
</Page.DataContext>

<Page.Resources>
    <DataTemplate x:Key="CustomerTemplate" x:DataType="viewmodel:CustomerListViewModel">
        <views:CustomerListView/>
    </DataTemplate>

    <DataTemplate x:Key="OrderTemplate" x:DataType="viewmodel:OrderViewModel">
        <views:OrderView/>
    </DataTemplate>
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <Grid x:Name="NavBar"
          Grid.Row="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>


        <Button Content="Customers"
                Command="{Binding NavCommand}"
                CommandParameter="customers"
                Grid.Column="0"
                Grid.Row="0"/>

        <Button Content="Orders"
                Command="{Binding NavCommand}"
                CommandParameter="orders"
                Grid.Column="2"
                Grid.Row="0"/>
    </Grid>

    <Grid x:Name="MainContent"
          Grid.Row="1">
        <ContentControl Content="{Binding CurrentViewModel}"/>
    </Grid>      
</Grid>

正如您所看到的那样,主要问题可能出在我的Page上。资源我想我的约束力

因为该行代码无法访问我的视图的实际内容。

这是我的主视图的viewmodel:

namespace MVVMHeirarchiesDemo.ViewModel
{
    /*Derive all of your ViewModels from BindableBase class.*/
    public class MainPageViewModel : BindableBase
    {
        public MainPageViewModel()
        {
            NavCommand = new MyCommand<string>(OnNavigation);
        }

        private CustomerListViewModel _customerListViewModel = new CustomerListViewModel();

        private OrderViewModel _orderViewModel = new OrderViewModel();

        private BindableBase _currentViewModel;

        public BindableBase CurrentViewModel
        {
            get
            {
                return _currentViewModel;
            }

            set
            {
                SetProperty(ref _currentViewModel, value);
            }
        }

        public MyCommand<string> NavCommand { get; private set; }

        private void OnNavigation(string destination)
        {
            switch (destination)
            {
                case "orders":
                    {
                        CurrentViewModel = _orderViewModel;
                        break;
                    }
                case "customers":
                default:
                    CurrentViewModel = _customerListViewModel;
                    break;
            }
        }
    }
}

最后这是我的帮助程序可绑定类:

namespace MVVMHeirarchiesDemo
{
    /*The main idea behind this class is to encapsulate the INotifyPropertyChanged implementation
     * and provide helper methods to the derived class so that they can easily trigger the appropriate notifications.
     * Following is the implementation of BindableBase class.*/
    public class BindableBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName]string propertyName = null)
        {
            if (object.Equals(member, val))
                return;

            member = val;
            OnPropertyChanged(propertyName);
        }
    }
}

我希望有人可以帮我解决这个问题。

1 个答案:

答案 0 :(得分:0)

好吧我花了一段时间但我能够理解如何使用DataTemplateSelector类,事实证明这个工作很适合我的问题,因为UWP没有隐式绑定。 因此,如果您正在探索UWP,那么您必须使用显式绑定才能使此特定问题发挥作用。

所以这是我的解决方案,我仍然使用我的BindableBase,因为它帮助我为View i调用选择合适的ViewModel,因此无需更改它。

第一次我已经创建了一个Template类,因此我可以检索有助于我使用ExplicitBinding的属性:

public class Template
{
    public string DataType { get; set; }

    public DataTemplate DataTemplate { get; set; }
}

然后我会使用一个Collection,但我会在这个上使用Facade Pattern,我喜欢的模式很有趣和优雅。

public class TemplateCollection : Collection<Template>
{
}

然后我通过另一个答案使用了一个类,帮助我重用它来解决我的问题:

public class MyDataTemplateSelector : DataTemplateSelector
{
    public TemplateCollection Templates { get; set; }

    private ICollection<Template> _templateCache { get; set; }

    public MyDataTemplateSelector()
    {

    }

    private void InitTemplateCollection()
    {
        _templateCache = Templates.ToList();
    }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (_templateCache == null)
        {
            InitTemplateCollection();
        }

        if (item != null)
        {
            var dataType = item.GetType().ToString();

            var match = _templateCache.Where(m => m.DataType == dataType).FirstOrDefault();

            if (match != null)
            {
                return match.DataTemplate;
            }
        }

        return base.SelectTemplateCore(item, container);
    }
}

此课程可在此处找到:How to associate view with viewmodel or multiple DataTemplates for ViewModel?

我不喜欢偷信用,但我确实从这个答案和我的伴侣sugesstion MLavoie那里学到了。

所以这是我的修复视图,这要归功于我能够使用导航并创建了一个分层MVVM模式。

<Page
x:Class="MVVMHeirarchiesDemo.MainPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVMHeirarchiesDemo"
xmlns:dtempsltor="using:MVVMHeirarchiesDemo.Templates"
xmlns:views="using:MVVMHeirarchiesDemo.Views"
xmlns:viewmodel="using:MVVMHeirarchiesDemo.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<!--Anytime the current view model is set to an instance of a CustomerListViewModel,
it will render out a CustomerListView with the ViewModel is hooked up. It’s an order ViewModel,
it'll render out OrderView and so on.

We now need a ViewModel that has a CurrentViewModel property and some logic and commanding
to be able to switch the current reference of ViewModel inside the property.-->
<Page.DataContext>
    <viewmodel:MainPageViewModel/>
</Page.DataContext>

<Page.Resources>
    <dtempsltor:TemplateCollection2 x:Key="templates">
        <dtempsltor:Template DataType="MVVMHeirarchiesDemo.ViewModel.CustomerListViewModel">
            <dtempsltor:Template.DataTemplate>
                <DataTemplate x:DataType="viewmodel:CustomerListViewModel">
                    <views:CustomerListView/>
                </DataTemplate>
            </dtempsltor:Template.DataTemplate>
        </dtempsltor:Template>
        <dtempsltor:Template DataType="MVVMHeirarchiesDemo.ViewModel.OrderViewModel">
            <dtempsltor:Template.DataTemplate>
                <DataTemplate x:DataType="viewmodel:OrderViewModel">
                    <views:OrderView/>
                </DataTemplate>
            </dtempsltor:Template.DataTemplate>
        </dtempsltor:Template>
    </dtempsltor:TemplateCollection2>
    <dtempsltor:MyDataTemplateSelector x:Key="MyDataTemplateSelector" 
                                       Templates="{StaticResource templates}"/>
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <Grid x:Name="NavBar"
          Grid.Row="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>


        <Button Content="Customers"
                Command="{Binding NavCommand}"
                CommandParameter="customers"
                Grid.Column="0"
                Grid.Row="0"/>

        <Button Content="Orders"
                Command="{Binding NavCommand}"
                CommandParameter="orders"
                Grid.Column="2"
                Grid.Row="0"/>
    </Grid>

    <Grid x:Name="MainContent"
          Grid.Row="1">
        <ContentControl ContentTemplateSelector="{StaticResource MyDataTemplateSelector}"
                        Content="{Binding CurrentViewModel}"/>
    </Grid>      
</Grid>

感谢那些帮助我自己的人,这很棒,能够用一些提示解决它并且能够解决它感觉很好,我有点喜欢用C#和Xaml进行编码。