WPF / MVVM在运行时加载UserControl

时间:2011-12-30 08:15:32

标签: c# wpf mvvm

我知道有很多关于我的问题的文章,但我找不到解决方案。 我是WPF的新手 - MVVM,我尝试理解MVVM-Logic。 所以我做了一个小项目来理解这一点。 对于我以后的应用程序,我想动态地将UserControl加载到我的窗口。

在我的StartView中,我有一个绑定到StartViewModel。 (绑定在APP.xaml中)

StartView app = new StartView();
StartViewModel context = new StartViewModel();

StartView

<Window x:Class="test.Views.StartView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:test.ViewModel"
        Title="Window1" Height="300" Width="516">
    <Grid>
        <Menu IsMainMenu="True" Margin="0,0,404,239">
            <MenuItem Header="_Einstellungen">
                <MenuItem Header="Server" />
            </MenuItem>
        </Menu>
        <ContentControl Content="{Binding LoadedControl}" Margin="0,28,0,128" />
    </Grid>
</Window>

StartViewModel

namespace test.ViewModel
{
    public class StartViewModel : ViewModelBase
    {
        #region Fields
        private UCStastistikViewModel _loadedControl;
        #endregion

        public StartViewModel()
        {
            LoadedControl = new UCStastistikViewModel();
        }

        #region Properties / Commands
        public UCStastistikViewModel LoadedControl
        {
            get { return _loadedControl; }
            set
            {
                if (value == _loadedControl)
                    return;

                _loadedControl = value;
                OnPropertyChanged("LoadedControl");
            }
        }
        #endregion

        #region Methods

        #endregion
    }
}

UCStatistikView

<UserControl x:Class="test.Views.UCStatistik"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:vm="clr-namespace:test.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="188" d:DesignWidth="508">
    <UserControl.DataContext>
        <vm:UCStastistikViewModel />
    </UserControl.DataContext>
    <Grid Background="red">       
    </Grid>
</UserControl>

UCStatistikViewModel

namespace test.ViewModel
{
    public class UCStastistikViewModel : ViewModelBase
    {
        #region Fields
        #endregion

        public UCStastistikViewModel()
        {
        }

        #region Properties / Commands
        #endregion

        #region Methods
        #endregion
    }
}

现在我想在我的StartView的ContentControl中加载我的UCStatistikView。 但是在Startview中只显示Path test.UCStatistikViewModel而不是整个UC 任何人都可以给我一些想法,我的问题在哪里/哪里出错?

再见

4 个答案:

答案 0 :(得分:18)

您的ViewModel不应该关心UserControls。相反,让他们持有ViewModels,让WPF解决如何使用DataTemplate绘制ViewModel。

例如,

<Window x:Class="test.Views.StartView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:test.Views"
        xmlns:viewmodels="clr-namespace:test.ViewModel"
        Title="Window1" Height="300" Width="516">

    <Window.Resources>
        <DataTemplate DataType="{x:Type viewmodels:UCStastistikViewModel}">
            <views:UCStastistikView />
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <Menu IsMainMenu="True" Margin="0,0,404,239">
            <MenuItem Header="_Einstellungen">
                <MenuItem Header="Server" />
            </MenuItem>
        </Menu>
        <ContentControl Content="{Binding LoadedControl}" Margin="0,28,0,128" />
    </Grid>
</Window>

另外,摆脱UserControl中的<UserControl.DataContext>DataContext应该通过使用控件的任何内容传入,而不是在UserControl中定义:)

修改

根据您对之前关于通过切换ViewModel来切换StartPage内容的回答的评论,您可能有兴趣查看this post of mine。它的标题为Navigation with MVVM,但同样的概念适用于切换视图或用户控件

基本上,您要创建LoadedControl类型的属性ViewModelBase,而不是对其类型进行硬编码,然后将其设置为您希望在ContentControl中显示的任何对象。 WPF的DataTemplates将负责连接ViewModel的正确视图。

答案 1 :(得分:2)

WPF不支持自动解析给定视图模型的视图。您问题的天真解决方案是直接将UCStatistikView添加到您的StartView并将VM绑定到它

<Window x:Class="test.Views.StartView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:views="clr-namespace:test.ViewModel"
    Title="Window1" Height="300" Width="516">
<Grid>
    <Menu IsMainMenu="True" Margin="0,0,404,239">
        <MenuItem Header="_Einstellungen">
            <MenuItem Header="Server" />
        </MenuItem>
    </Menu>
    <UCStatistikView DataContext="{Binding LoadedControl}" Margin="0,28,0,128" />
</Grid>

更详细的方法是实现ViewLocator(视图模型第一种方法)或ViewModelLocator(视图第一种方法)。定位器自动定位视图并将其绑定到视图模型。有一些MVVM框架/工具包可以实现这样的定位器。

  • Caliburn.Micro:根据命名约定提供灵活的ViewLocator和ViewModelLocator。 Here是关于他们的文章
  • MVVM Light:提供ViewModelLocator。 Here是一个介绍

使用Caliburn.Micro,你开始查看将会是这样的

<Window x:Class="test.Views.StartView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:views="clr-namespace:test.ViewModel"
    Title="Window1" Height="300" Width="516">
<Grid>
    <Menu IsMainMenu="True" Margin="0,0,404,239">
        <MenuItem Header="_Einstellungen">
            <MenuItem Header="Server" />
        </MenuItem>
    </Menu>
    <ContentControl cm:View.Model="{Binding LoadedControl}" Margin="0,28,0,128" />
</Grid>

cm:View.Model="{Binding LoadedControl}"附加属性告诉caliburn找到绑定的视图模型的视图,并将ContentControl的Content属性设置为它。

答案 2 :(得分:1)

这是你应该做的:

StartView:

<Window x:Class="test.Views.StartView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:test.ViewModel"
        Title="Window1" Height="300" Width="516">
    <Grid>
        <Menu IsMainMenu="True" Margin="0,0,404,239">
            <MenuItem Header="_Einstellungen">
                <MenuItem Header="Server" />
            </MenuItem>
        </Menu>
        <UCStatistikView x:Name="myUCStatistikView" Margin="0,28,0,128" />
    </Grid>
</Window>

StartViewModel:

namespace test.ViewModel
{
    public class StartViewModel : ViewModelBase
    {
        private UCStastistikViewModel _myControlViewModel;

        public StartViewModel()
        {
            _myControlViewModel = new UCStastistikViewModel();
        }

        public UCStastistikViewModel MyControlViewModel
        {
            get { return _myControlViewModel; }
            set
            {
                if (value == _myControlViewModel)
                    return;

                _myControlViewModel = value;
                OnPropertyChanged("MyControlViewModel");
            }
        }
    }
}

UCStatistikView:

<UserControl x:Class="test.Views.UCStatistik"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:vm="clr-namespace:test.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="188" d:DesignWidth="508">
    <Grid Background="red">       
    </Grid>
</UserControl>

StartView的代码:

this.myUCStatistikView.DataContext = ((StartViewModel)this.DataContext).MyControlViewModel;

在测试了不同的方法之后,我的结论是,如果你有userControls,那么datacontext绑定的最佳方法是父视图的代码隐藏。

编辑:ViewModel定位器适用于简单的示例,但如果您的ViewModel必须动态实例化(大多数情况下它的构造函数需要参数),您就无法使用它。 我个人因此而停止使用定位器。

答案 3 :(得分:-1)

评论: - http://patelrocky1608.wordpress.com/2013/12/26/how-to-add-custom-control-dynamically-in-wpf/

其中包含用于动态理解UserControl的整个代码..