我在Windows Phone 8应用程序中使用MVVM。我想在我的shell视图模型中从1个视图模型移动到另一个视图模型。我似乎无法将ContentControl绑定到视图模型上的usercontrol / phoneApplicationPage模板。
我错过了什么?
我正在努力避免像MVVM这样的事情。 (我希望我的应用程序尽量少下载)这应该可以做到。
P.S。我还是WPF / WP8的新手
以下是我到目前为止的一个示例,请原谅这个愚蠢的功能:)
/ ** Shell视图** /
<phone:PhoneApplicationPage
x:Class="PhoneAppWithDataContext.Navigation.ViewModelNavigation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True"
xmlns:vm="clr-namespace:PhoneAppWithDataContext.Navigation">
<phone:PhoneApplicationPage.DataContext>
<vm:AppViewModel/>
</phone:PhoneApplicationPage.DataContext>
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="MonthViewModel">
<vm:MonthViewControl/>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ContentControl Content="{Binding CurrentViewModel}"
ContentTemplate="{Binding ContentTemplate}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"/>
</Grid>
<Button Content="Change VM" Command="{Binding ChangeViewModelCommand}"/>
</Grid>
</phone:PhoneApplicationPage>
/ ** Shell / Application View Model ** /
public class AppViewModel : ViewModelBase
{
private ViewModelBase _currentViewModel;
private List<ViewModelBase> _viewModels = new List<ViewModelBase>();
private byte _month = 1;
public ViewModelBase CurrentViewModel
{
get
{
return _currentViewModel;
}
set
{
if (_currentViewModel == value)
return;
_currentViewModel = value;
NotifyPropertyChanged("CurrentViewModel");
}
}
public DataTemplate SelectedTemplate
{
get
{
if (_currentViewModel == null)
return null;
return DataTemplateSelector.GetTemplate(_currentViewModel);
}
}
public List<ViewModelBase> ViewModels
{
get
{
return _viewModels;
}
}
public AppViewModel()
{
ViewModels.Add(new MonthViewModel(_month));
CurrentViewModel = ViewModels.FirstOrDefault();
}
private ICommand _changeViewModelCommand;
public ICommand ChangeViewModelCommand
{
get
{
return _changeViewModelCommand ?? (_changeViewModelCommand = new GenericCommand(() =>
{
_month++;
var newVM = new MonthViewModel(_month);
ViewModels.Add(newVM);
CurrentViewModel = newVM;
}, true));
}
}
private void ChangeViewModel(ViewModelBase viewModel)
{
if (!ViewModels.Contains(viewModel))
ViewModels.Add(viewModel);
CurrentViewModel = ViewModels.FirstOrDefault(vm => vm == viewModel);
}
}
/ ** DataTemplateSelector ** /
public static class DataTemplateSelector
{
public static DataTemplate GetTemplate(ViewModelBase param)
{
Type t = param.GetType();
return App.Current.Resources[t.Name] as DataTemplate;
}
}
/ **用户控制** /
<UserControl x:Class="PhoneAppWithDataContext.Navigation.MonthViewControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480"
xmlns:vm="clr-namespace:PhoneAppWithDataContext.Navigation">
<UserControl.DataContext>
<vm:MonthViewModel/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Id" Width="100" VerticalAlignment="Center" Grid.Row="0" Grid.Column="0" />
<TextBlock Text="{Binding Id}" Width="100" VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" />
<TextBlock Text="Name" Width="100" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" />
<TextBlock Text="{Binding Name}" Width="100" VerticalAlignment="Center" Grid.Row="1" Grid.Column="1" />
</Grid>
</UserControl>
/ ** ViewModelBase ** /
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
/ **查看用户控件应绑定到**的模型 /
public sealed class MonthViewModel : ViewModelBase
{
private byte _id;
private string _name;
public MonthViewModel()
{
}
public MonthViewModel(byte id)
{
_id = id;
_name = "Month " + id.ToString() + " of the year";
}
public override string ToString()
{
return _name;
}
public byte Id
{
get
{
return _id;
}
}
public string Name
{
get
{
return _name;
}
}
}
答案 0 :(得分:2)
我认为这里的问题是:
<UserControl.DataContext>
<vm:MonthViewModel/>
</UserControl.DataContext>
当您的Content
从一个MonthViewModel
更改为下一个Content
时,返回的DataTemplate的DataContext将设置为绑定到vm:MonthViewModel
的对象。好吧,一旦设置了DataContext,你就应该好了,但是一旦加载了UserControl,它就会将DataContext重置为一个空的MonthViewModel(SelectedTemplate
)的新实例。摆脱那个显式的DataContext声明 - 换句话说,删除我上面发布的代码。
这样,当您第一次调用CurrentViewModel并引发INPC时,它将不会重置DataContext。当您在MonthViewModel类型的CurrentViewModel之间切换时,您的UserControl将不会再次调用InitializeComponent,而是DataContext将更改。
修改强>
此外,如果您仍然没有看到更改,那么我会指向GetTemplate
属性。而不是在属性中进行空检查,只需将null传递给GetTemplate
。在{{1}}内部,检查null并返回null,如果它为null。