我已经搜索并试了几天,最后一定要问这个问题。 我有一个Silverlight 5应用程序,使用MVVM Light,我希望能够在主视图中动态切换视图。
为简单起见,假设我有2个按钮。
Button1将切换到TestView1。
Button2将切换到TestView2。
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
我这样做的方法是将relay命令绑定到按钮,然后实例化相应视图的新视图模型。 即:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = ViewModelLocator.NinjectKernel.Get<TestViewModel1>();
}));
}
}
然后将CurrentViewmodel设置为新的viewmodel。 在MainView中,我将CurrentView绑定到ContentControl:
<Border x:Name="displayedView" Grid.Row="2">
<ContentControl Content="{Binding CurrentView}" />
</Border>
这实际上会在某种程度上起作用,因为CurrentView会改变,但它不是实际显示视图的内容,而是简单地显示了实例化的ViewModel的命名空间。
到目前为止,我主要使用从这些来源获取的知识:
http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/
Loading Views into ContentControl and changing their properties by clicking buttons
但他们没有解决我的问题,或者我不太明白如何实际显示观点。: - (
所有人都有一个很好的解释,如何使用GalaSoft的MVVM Light在Silverlight 5中正确切换视图。
由于
答案 0 :(得分:2)
您缺少的部分是告诉WPF如何渲染ViewModel的DataTemplates
<Window.Resources>
<DataTemplate TargetType="{x:Type local:TestViewModel1}">
<local:TestView1 />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TestViewModel2}">
<local:TestView2 />
</DataTemplate>
</Window.Resources>
在可视树中插入对象时,例如在ViewModel
中放置ContentControl.Content
对象,默认情况下会使用绑定到{{1}的TextBlock
绘制对象对象,这就是为什么你只看到.ToString()
namespace.classname
的{{1}}
通过在ViewModel
某处定义隐式ContentControl
(仅DataTemplate
定义了Resources
- 没有DataTemplate
),您告诉WPF在尝试绘制该对象的任何时候使用指定的TargetType
绘制指定的对象,而不是使用绑定到对象的x:Key
的默认DataTemplate
。
应该注意,早期版本的Silverlight不支持隐式TextBlock
,但5.0+支持它们。对于早期版本的Silverlight,我通常使用DataTemplateSelector代替。
答案 1 :(得分:1)
我首先建议您不要通过ContentControl显示您的视图,而是使用silverlight工具包中的导航框架。此外,我们不希望我们的ViewModel创建视图...这不是那么好。但是,如果我们的ViewModel执行业务逻辑和确定要显示哪个视图,我们并不介意。在此处获取工具包:http://silverlight.codeplex.com/
现在在主页面中设置您的XAML:
<Border x:Name="displayedView" Grid.Row="2">
<navigation:Frame x:Name="ContentFrame" />
</Border>
由于您使用的是MVVM Light,我们将使用消息传递。您的View模型将获得更改视图,确定要更改的视图的命令,然后向主页发送消息以指示其更改视图。
在主页面中设置一个监听器,以获取导航请求,如下所示:
public MainPage()
{
InitializeComponent();
Messenger.Default.Register<Uri>(this, "NavigationRequest", (uri) => ContentFrame.Navigate(uri));
}
接下来,在视图模型中设置命令。
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
Messenger.Default.Send<Uri>(new Uri("/Views/.../Page.xaml", UriKind.Relative), "NavigationRequest");
}));
}
}
这些是适合我的基础知识。你可以扩展这个并获得真正的“架构”。例如,您可以为查看发送导航请求的模型创建基类,创建生成URI的帮助程序类(因此它们不会在应用程序的任何位置进行硬编码等等。祝您好运!
答案 2 :(得分:1)
所以我实际上解决了这个问题,不需要在 MainView 中创建数据模板,这是我不喜欢的。当我们谈论切换视图时, MainView 应该对它所显示的视图一无所知。
先决条件:您必须使用来自GalaSoft的MVVM Light 才能获得此解决方案
这是我的测试解决方案: 我的 MainView 中添加了两个按钮,每个按钮都会打开一个新视图。 clickevent与命令绑定。
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
在 MainView 中,我有一个边框应该包含可以切换的视图。 由于所有视图都继承自 UserControl ,因此我将内容绑定到 MainViewModel 的 CurrentView 属性
<Border x:Name="displayedView" Grid.Row="2">
<UserControl Content="{Binding CurrentView}" />
</Border>
在 MainViewModel 中,我有属性CurrentView。
public const string CurrentViewPropertyName = "CurrentView";
private UserControl _currentView;
/// <summary>
/// Sets and gets the "CurrentView property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (_currentView == value)
{
return;
}
RaisePropertyChanging(CurrentViewPropertyName);
_currentView = value;
RaisePropertyChanged(CurrentViewPropertyName);
}
}
单击按钮时,在 MainViewModel 中调用相应的Command:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = new TestView1();
}));
}
}
private RelayCommand _callTestView2Command;
public RelayCommand CallTestView2Command
{
get
{
return _callTestView2Command ??
(_callTestView2Command = new RelayCommand(() =>
{
CurrentView = new TestView2();
}));
}
}
如图所示,每个命令都会将 CurrentView 设置为新视图,并且视图将切换到 MainView ,因为 CurrentView 会引发ProperTyChanged事件。
答案 3 :(得分:0)
这实际上会在某种程度上起作用,因为CurrentView会 改变但不是实际显示视图的内容 只显示实例化的ViewModel的命名空间。
因为您要将CurrentView
属性更改为viewmodel实例并将其绑定为Content。这是错误的,因为内容应该是一个视图,您应该将该视图的DataContext
设置为viewmodel。
这里最简单的方法是在命令中创建一个View实例,并将viewmodel设置为DataContext,然后将视图设置为CurrentView
属性。当然,这会违反 MVVM模式,因此您应该将此职责移至单独的组件。我建议您选择现有的解决方案,而不是编写自己的导航逻辑,因为这种任务并不像看起来那么简单。
我建议使用Prism library