【问题标题】:MVVM – Hierarchies & Navigation Implicit Binding for UWPMVVM – UWP 的层次结构和导航隐式绑定
【发布时间】:2017-04-04 20:15:43
【问题描述】:

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

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

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

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

我将把目前用于 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>

如您所见,主要问题可能出在我的主页上。资源我猜我的绑定在

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

这是我的主视图的视图模型:

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);
        }
    }
}

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

【问题讨论】:

  • 为了在 UWP 中工作,我想您需要在 ContentTemplateSelector 元素的 ContentTemplateSelector 属性中实现 DataTemplateSelector,因为您正在学习 WPF 教程。请记住,并非 WPF 中的每个 XAML 逻辑 都只能在 UWP 中工作。
  • 是的,我刚刚发现了一个 DataTemplateSelector 问题,我现在就应用它。谢啦!!!这是在 UWP 中解决此问题的正确方法。

标签: c# xaml mvvm uwp uwp-xaml


【解决方案1】:

好吧,我花了一段时间,但我能够理解如何使用 DataTemplateSelector 类,事实证明,这种解决方法非常适合我的问题,因为 UWP 没有隐式绑定。 因此,如果您正在探索 UWP,则没有隐式绑定,您必须使用显式绑定才能解决这个特定问题。

所以这是我的解决方案,我仍然使用我的 BindableBase,因为它可以帮助我为我正在调用的视图选择正确的 ViewModel,因此无需更改它。

首先,我创建了一个模板类,这样我就可以检索有助于我使用 ExplicitBinding 的属性:

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

    public DataTemplate DataTemplate { get; set; }
}

然后我会使用一个集合,但我会在这个上使用一个外观图案,我喜欢图案既有趣又优雅。

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 编写代码。

【讨论】:

    猜你喜欢
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多