【问题标题】:Silverlight Switching views in a view using MVVM Light frameworkSilverlight 使用 MVVM Light 框架在视图中切换视图
【发布时间】:2012-10-15 10:54:43
【问题描述】:

我已经搜索并尝试了几天,最后必须在这里提出问题。 我有一个 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"/>

我这样做的方法是将relaycommand绑定到按钮,然后实例化相应视图的新视图模型。 即:

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 的命名空间。

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

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 中正确切换视图有很好的解释。

谢谢

【问题讨论】:

  • 在CallTestView1Command中首先设置CurrentView = null;然后给它分配新的项目,它也许可以解决这个问题

标签: silverlight view mvvm-light silverlight-5.0


【解决方案1】:

您缺少的部分是DataTemplates,它告诉 WPF 如何呈现您的 ViewModel

<Window.Resources>
    <DataTemplate TargetType="{x:Type local:TestViewModel1}">
        <local:TestView1 />
    </DataTemplate>

    <DataTemplate TargetType="{x:Type local:TestViewModel2}">
        <local:TestView2 />
    </DataTemplate>
</Window.Resources>

当您在可视化树中插入一个对象时,例如在ContentControl.Content 中放置一个ViewModel 对象,默认情况下会使用绑定到该对象的.ToString()TextBlock 来绘制它,这就是为什么您只在ContentControl 中看到ViewModel 中的namespace.classname

通过在 Resources 某处定义一个隐式 DataTemplate(这是一个 DataTemplate 只定义了一个 TargetType - 没有 x:Key),你告诉 WPF 使用指定的 @987654336 绘制指定的对象@ 尝试绘制该对象的任何时候,而不是使用绑定到该对象的.ToString() 的默认TextBlock

需要注意的是,在 Silverlight 的早期版本中不支持隐式 DataTemplates,但在 5.0+ 中支持它们。对于 Silverlight 的早期版本,我通常使用 DataTemplateSelector 代替。

【讨论】:

    【解决方案2】:

    我首先建议您不要通过 ContentControl 显示您的视图,而是考虑使用 silverlight 工具包中的导航框架。此外,我们不希望我们的 ViewModel 创建 Views ......那不是很好。但是,如果我们的 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 的辅助类(这样它们就不会在您的应用程序的任何地方进行硬编码等等。祝您好运!

    【讨论】:

    • 嗨。我会研究这种方法。我确实解决了这个问题(参见我的另一篇文章),但我有点喜欢你的方法,其中消息实际上允许实例化新视图。
    【解决方案3】:

    所以我实际上解决了这个问题,不需要在 MainView 中创建数据模板,这是我不喜欢的。当我们谈论切换视图时,imo MainView 应该对它正在显示的视图一无所知。

    先决条件:您必须使用 GalaSoft 的 MVVM Light 进行此解决方案

    这是我的测试解决方案: 我的 MainView 中添加了两个按钮,每个按钮都会打开一个新视图。点击事件绑定到命令。

    <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中被调用:

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

    【讨论】:

    • 虽然这可能有效,但ViewModel 不应该像这样直接包含对View 的引用,因为这违反了层之间的关注点分离,这是使用 MVVM 的最重要原因之一首先。您最好在&lt;Window.Resources&gt;&lt;Application.Resources&gt; 中使用隐式DataTemplateDataTemplate,仅指定TargetType,没有x:Key),告诉WPF 用您的视图绘制您的ViewModel。 View 层旨在反映ViewModel 层,而不是相反。
    【解决方案4】:

    这实际上会在某种程度上起作用,因为 CurrentView 将 更改,而不是实际显示视图的内容 只是显示了被实例化的 ViewModel 的命名空间。

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

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

    我建议使用Prism library

    【讨论】:

    • 嗨,彼得。我还发现它实际上是我发现的“指南”所说的应该绑定到内容的 ViewModel。如果我添加:&lt;DataTemplate DataType="ViewModels:TestViewModel1"&gt; &lt;Views:TestView1/&gt; &lt;/DataTemplate&gt; &lt;DataTemplate DataType="ViewModels:TestViewModel2"&gt; &lt;Views:TestView2/&gt; &lt;/DataTemplate&gt; 它实际上会切换视图,但是我必须在 xaml 中指定任何可能显示的视图。这不是很动态。
    • 那么你也应该发布描述数据模板的xaml。
    猜你喜欢
    • 2014-01-18
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多