Silverlight使用MVVM Light框架在视图中切换视图

时间:2012-10-15 10:54:43

标签: silverlight view mvvm-light silverlight-5.0

我已经搜索并试了几天,最后一定要问这个问题。 我有一个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中正确切换视图。

由于

4 个答案:

答案 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