我一直在尝试使用层次结构和导航来跟踪这个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);
}
}
}
我希望有人可以帮我解决这个问题。
答案 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进行编码。