我正在尝试创建一个支持 MVVM 的自定义控件,该控件扩展 Frame(来自 Navigation),并考虑了两个主要目标。
首先,我希望能够通过触发器更改帧的来源。这样,当 ViewModel 的属性发生更改时,我可以根据某些情况更改使用哪个视图。
其次,我希望视图本身能够更改使用哪个视图,在某些情况下,ViewModel 中没有任何变化。使用框架/页面系统并从页面内部调用 NavigationCommands.GoToPage 命令似乎是最合适的方法,因为每个不同的视图都可以定义为一个页面。
我遇到的问题是通过触发器设置 Frame.Source 工作得很好,直到第一次使用 GoToPage。在那之后,触发器似乎没有效果。GoToPage 似乎随时都可以工作。我整天都在搜索,找不到任何解释这一点的文档。
无论如何,这是我的自定义 Frame 的实现,我所做的只是绑定到 GoToPage 并确保 Pages 继承 DataContext:
public class FrameExtended : Frame
{
public FrameExtended()
{
CommandBindings.Add(new CommandBinding(NavigationCommands.GoToPage, GoToPage_Executed));
Navigated += new System.Windows.Navigation.NavigatedEventHandler(FrameExtended_Navigated);
}
void FrameExtended_Navigated(object sender, NavigationEventArgs e)
{
(Content as FrameworkElement).DataContext = this.DataContext;
}
void GoToPage_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter is Uri) Source = e.Parameter as Uri;
else if (e.Parameter is string) Source = new Uri(e.Parameter as string, UriKind.Relative);
}
}
这是我的 ViewModel 的测试用例,它与 ViewModel 一样简单:
public enum MyEnum { MyEnumVal1, MyEnumVal2, MyEnumVal3 }
public class ViewModel : INotifyPropertyChanged
{
private MyEnum enumVal = MyEnum.MyEnumVal1;
public MyEnum EnumVal
{
get { return enumVal; }
set
{
if (enumVal != value)
{
enumVal = value;
OnPropertyChanged("EnumVal");
}
}
}
protected void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
这是 XAML 的测试片段,我在其中使用了框架并定义了一些触发器:
<Button Content="ChangeViewModel" Click="Button_Click"/>
<Control>
<Control.Template>
<ControlTemplate TargetType="Control">
<local:FrameExtended x:Name="MyFrame"/>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding EnumVal}" Value="MyEnumVal1">
<Setter TargetName="MyFrame" Property="Source" Value="Pages/testpage.xaml"/>
</DataTrigger>
<DataTrigger Binding="{Binding EnumVal}" Value="MyEnumVal2">
<Setter TargetName="MyFrame" Property="Source" Value="Pages/testpage2.xaml"/>
</DataTrigger>
<DataTrigger Binding="{Binding EnumVal}" Value="MyEnumVal3">
<Setter TargetName="MyFrame" Property="Source" Value="Pages/testpage3.xaml"/>
<Setter TargetName="MyFrame" Property="Background" Value="Green"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Control.Template>
</Control>
其中 Button_Click 指的是:
private void Button_Click(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as ViewModel;
switch (vm.EnumVal)
{
case MyEnum.MyEnumVal1: vm.EnumVal = MyEnum.MyEnumVal2; break;
case MyEnum.MyEnumVal2: vm.EnumVal = MyEnum.MyEnumVal3; break;
case MyEnum.MyEnumVal3: vm.EnumVal = MyEnum.MyEnumVal1; break;
}
}
除此之外,testpage.xaml 包含以下行:
<Button Content="NEXTPAGE" Command="GoToPage" CommandParameter="Pages/testpage2.xaml"/>
其余页面只有一个 TextBlock 指示它是哪个页面。
如果我一遍又一遍地单击“ChangeViewModel”按钮,框架将按预期循环浏览页面(并在第 3 页上将其背景更改为绿色)。单击 testpage.xaml 中调用 GoToPage 的按钮后,Frame 将切换到 testpage2。之后,对 ChangeViewModel 的后续点击永远不会改变显示哪个页面,但仍然使 Frame 的背景在每 3 次点击时变为绿色(当 testpage3 应该显示时)。