2012-10-15 145 views
2

我已经搜索并尝试了几天,最后必须在这里提出问题。 我有一个Silverlight 5应用程序,使用MVVM Light,我希望能够在主视图中动态切换视图。Silverlight在使用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"/> 

我做它是由relaycommand结合的按钮,然后instanciating对应视图的新视图模型的方式。 即:

private RelayCommand _callTestView1Command; 
    public RelayCommand CallTestView1Command 
    { 
     get 
     { 
      return _callTestView1Command ?? 
        (_callTestView1Command = new RelayCommand(() => 
         { 
          CurrentView = ViewModelLocator.NinjectKernel.Get<TestViewModel1>(); 
         })); 
     } 
    } 

的CurrentViewmodel然后设置为新视图模型。 在的MainView我已绑定了CurrentView到ContentControl中:

<Border x:Name="displayedView" Grid.Row="2"> 
     <ContentControl Content="{Binding CurrentView}" /> 
    </Border> 

这将实际工作在一定程度上,因为CurrentView会改变,但不是实际显示视图的内容也只是显示出的命名空间ViewModel是instanciated。

到目前为止,我主要使用从这些来源采取的知识:

http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/

Loading Views into ContentControl and changing their properties by clicking buttons

,但他们不解决我的问题,还是我不太明白如何实际显示views :-(

因此,没有人有一个很好的解释,如何使用GalaSoft的MVVM Light在Silverlight 5中正确切换视图。

感谢

+0

在CallTestView1Command第一设定CurrentView = NULL;然后分配新的项目,它也许可以解决这个问题 – Masoomian

回答

0

这将实际工作在一定程度上,因为CurrentView会,而是的 变化实际上显示视图它 的内容简单地显示了实例化视图模型的命名空间。

因为您正在将CurrentView属性更改为viewmodel实例并将其绑定为内容。这是错误的,因为内容应该是一个视图,您应该将该视图的DataContext设置为视图模型。

您可以在此处执行的最简单的操作是在命令中创建一个View实例,并将viewmodel设置为其DataContext,然后您可以将该视图设置为CurrentView属性。当然,这将违反 MVVM模式,所以你应该把这个责任移到一个单独的组件。我建议你选择一个现有的解决方案,而不是编写自己的导航逻辑,因为这种任务并不像看起来那么简单。

我建议使用Prism library

+0

嗨,彼得。 我也很好奇,它实际上是ViewModel,我发现的“指南”应该绑定到内容。 如果我添加: ' <查看:TestView1 /> <查看:TestView2 /> ' 它实际上会切换视图,但是我必须在xaml中指定任何可能显示的视图。这不是很有活力。 – ThBlitz

+0

那么你应该发布描述数据模板的xaml。 –

1

标识首先建议您不要显示通过ContentControl中你的意见,但看看在Silverlight工具包使用导航框架。另外,我们不希望我们的ViewModel创建视图...那不会那么好。但是,如果我们的ViewModel执行业务逻辑和决定显示哪个视图,我们并不介意。这里获取该工具包:http://silverlight.codeplex.com/

现在设置您的XAML为使您的主页:

<Border x:Name="displayedView" Grid.Row="2"> 
     <navigation:Frame x:Name="ContentFrame" /> 
    </Border> 

由于您使用的MVVM光,我们将使用消息。您的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的助手类(因此它们在您的应用中无处不在硬编码等等)。祝您好运!

+0

嗨。我会研究这种方法。我确实解决了这个问题(请参阅我的其他帖子),但我有点喜欢你的方法,即消息实际上允许实现新视图。 – ThBlitz

1

因此,我实际上解决了这个问题,这样就不需要在我不喜欢的情况下创建数据模板,当我们正在谈论切换的时候,它应该对它正在显示的视图一无所知。

先决条件:您必须使用来自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我有一个包含有除了可以切换视图的边界。 由于所有的观点从用户控件继承我的内容结合到的MainViewModel

<Border x:Name="displayedView" Grid.Row="2"> 
     <UserControl Content="{Binding CurrentView}" /> 
    </Border> 

财产CurrentViewMainViewModel我有物业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相应的命令被称为:

 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事件。

+1

虽然这可能工作,'ViewModel'不应该包含直接这样到'View'参考因为违反的担忧层之间的分离,这是最强的原因首先使用MVVM之一。你最好使用隐式'DataTemplate'(一个'DataTemplate'仅指定一个'TargetType',没有'X:Key')在''或'',它告诉WPF用您的视图绘制您的ViewModel。 'View'层意味着反映'ViewModel'层,而不是相反。 – Rachel

2

你缺少的部分是DataTemplates告诉WPF如何渲染你的ViewModels

<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,它会被绘制通过使用默认绑定到对象的.ToString()一个TextBlock,这就是为什么你只看到ViewModelnamespace.classnameContentControl

通过定义一个隐含DataTemplate在你的Resources地方(这是一个DataTemplate只有一个TargetType定义 - 没有x:Key),你告诉WPF使用指定DataTemplate随时随地它试图绘制对象绘制指定的对象,而不是使用绑定到.ToString()默认TextBlock的对象。

应当指出的是,隐DataTemplates是不是在早期版本的Silverlight的支持,但他们在5.0+支持。对于较早版本的Silverlight,我通常使用DataTemplateSelector来代替。