Wpf 在运行时和设计时将ViewModels分配给UserControls

Wpf 在运行时和设计时将ViewModels分配给UserControls,wpf,mvvm,wpf-controls,mvvm-light,Wpf,Mvvm,Wpf Controls,Mvvm Light,我正在用MVVM Light在WPF中编写一些数据可视化代码。这里有一个片段: <Window x:Class="EventBlockVisualization.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

我正在用MVVM Light在WPF中编写一些数据可视化代码。这里有一个片段:

    <Window x:Class="EventBlockVisualization.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:ignore="http://www.ignore.com"
            Title="MainWindow"
            mc:Ignorable="d ignore"
            DataContext="{Binding Main, Source={StaticResource Locator}}">
        <Window.Resources>
            <ItemsPanelTemplate x:Key="GraphRowItemsPanelTemplate">
                <StackPanel IsItemsHost="True" Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </Window.Resources>
        <Grid IsSharedSizeScope="True">
            <ScrollViewer Margin="8" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True">
                <ItemsControl x:Name="GraphItemsControl" Margin="8"  ItemsSource="{Binding VibeEvents, Mode=OneTime}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition SharedSizeGroup="NameWidthSizeGroup" Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock x:Name="NameTextBlock" Text="{Binding Name}" Grid.Column="0" Margin="4,0"/>
                                <ItemsControl x:Name="GraphRowItemsControl" ItemsSource="{Binding VibeEventViewModels, Mode=OneTime}" ItemsPanel="{DynamicResource GraphRowItemsPanelTemplate}"  Grid.Column="1" Margin="4,0">
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <Grid HorizontalAlignment="Left" VerticalAlignment="Center" Height="10">
                                                <TextBlock x:Name="FGTitleTextBox" Text="{Binding FGTitle}" Visibility="Collapsed"/>
                                                <Button Margin="1,0,0,0" Width="{Binding LengthInSeconds}" HorizontalAlignment="Left" Background="{Binding BackgroundColor}" BorderBrush="#FF2186A1">
                                                    <Button.ToolTip>
                                                        <ToolTip>
                                                            <StackPanel>
                                                                <TextBlock FontWeight="Bold" Text="{Binding FGTitle}"/>
                                                                <TextBlock Text="{Binding LengthText}"/>
                                                            </StackPanel>
                                                        </ToolTip>
                                                    </Button.ToolTip>
                                                </Button>
                                            </Grid>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                            </Grid>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
        </Grid>
    </Window>
当我在Expression Blend中设计UserControl时,这将非常有用,因为定位器可以提供充满虚拟数据的ViewModel。但是在运行时发生了什么;主ViewModel中集合提供的UserControl的ViewModel类的实例如何覆盖该绑定?主窗口在设计时也会出现同样的问题。如果我在主窗口的外观上使用Expression Blend,那么该绑定如何被设计时主ViewModel中集合提供的UserControl的ViewModel类的实例覆盖

有许多问题和答案已经涉及到这一点:

  • 在akjoshi中,他建议主ViewModel保存UserControl的ViewModel实例;但是当我设计UserControl本身时,它是如何工作的呢

  • In tam指出,“您希望保持datacontext的打开状态,并可用于绑定到使用此控件的控件中的控件”,在下面的注释中,SoMoS补充说,您需要“在ViewModel中为绑定的属性创建属性,以及当有人想要更改控件的一个属性时”(就像启用了一些子控件)他将必须检查视图模型”。这是一个承诺,但我不确定该做什么来代替MainViewModel的可绑定UserControlViewModels集合

  • 在Ehsan Ershadi看来"在UserControls中使用MVVM Light ViewModelLocator不是一个好主意,因为它是一个静态属性,当您要实例化用户控件的多个实例时,会有相同的公共ViewModel,因此它们的行为都是相同的,这不是我们想要的UserControls,以防您决定在entir中使用它一次然后声明“为了解决这个问题,您需要修改ViewModelLocator,例如使所有属性都是非静态的”。我不确定这对我有什么帮助

  • 在Jon Mitchell提到“看起来MVVM并不适合创建用户控件”之后的评论中,我希望这是不对的

  • 相比之下,在dthrasher中提到“许多WPF MVVM框架似乎避免使用导航窗口和页面控件,而倾向于使用嵌套的UserControls组合页面”,即UserControls是MVVM中常见的设备

  • 在Reed中,Copsey提醒sandbox“用户控件总是可以通过公开属性和使用数据绑定与它们的包含控件对话。这非常好,因为它在所有方面都保留了MVVM样式。”包含控件可以使用属性将两个用户控件上的两个属性链接在一起,同样,保留干净的边界”,但同样,当我在Expression Blend中设计UserControl时,我看不出这有什么帮助

  • 在中,Rachel偶尔提到在将代码剪切和粘贴到DataTemplate之前使用Expression Blend设计UserControl:“如果我确实想使用它来设计DataTemplate,我通常会创建一个新的UserControl,按照我想要的方式设计,然后将内容复制/粘贴到DataTemplate中”


  • 对于这篇文章长度的问题,我很抱歉!我不知道在设计一个用户控件时如何使用MVVM Light,该控件将成为主窗口集合中项目的可视控件,特别是如何设置三个绑定:运行时视图模型、主窗口的设计时视图模型及其用户控件的实例化,以及为用户控件单独设计时间视图模型。

    我认为您将事情复杂化了:

    有什么问题吗

    <Grid IsSharedSizeScope="True">
       <ScrollViewer Margin="8" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True">
          <ItemsControl x:Name="GraphItemsControl" Margin="8"  ItemsSource="{Binding VibeEvents, Mode=OneTime}">
             <ItemsControl.ItemTemplate>
                <DataTemplate>
                   <MyShinyUserControl DataContext={Binding}/>
                </DataTemplate>
             </ItemsControl.ItemTemplate>
          </ItemsControl>
       </ScrollViewer>
    </Grid>
    
    
    
    将每个VibeEvent绑定到用户控件的DataContext。在用户控件本身中,我建议创建设计时DataContext以简化设计。设计时DataContext如下所示:

    <UserControl x:Class="EMC.Windows.AlarmsModule.UserControl1"
                 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"
        mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:AlarmsModule="clr-namespace:EMC.Windows.AlarmsModule" d:DesignHeight="300"
        d:DesignWidth="300"
                 d:DataContext="{d:DesignInstance Type=AlarmsModule:Alarm}"
        >
    
    
    
    这将使您能够构建用户控件,并在其中包含设计时数据。它非常简单,不需要太多(如果有)脚手架。

    基于以下我可以想到的最简单的示例,使用UserControl在MVVM Light中显示列表的内容

    为了完整起见,我将包含所有代码,这些代码尽量简短,同时仍然在用户控件的视图模型和主视图模型中提供与运行时数据不同的设计时数据

    首先是定位器VMUCExample/ViewModel/viewmodellator.cs:

    使用GalaSoft.MvvmLight.Ioc;
    使用Microsoft.Practices.ServiceLocation;
    命名空间VMUCExample.ViewModel
    {
    公共类ViewModelLocator
    {
    静态ViewModelLocator()
    {
    ServiceLocator.SetLocatorProvider(()=>SimpleIoc.Default);
    SimpleIoc.Default.Register();
    SimpleIoc.Default.Register建议放置
    d:DataContext
    ,以便在设计用户控件时,Expression Blend所需的设计时绑定不会阻止运行时所需的数据上下文,也不会阻止在Expression Blend中设计主窗口时父级提供的数据上下文尽管如此,这并不是保罗·斯托维尔(Paul Stovell)和@LBugnion中描述为MVVM光的方法

    另一个视图文件是VMUCExample\MainWindow.xaml:

    
    
    示例应用程序本身仅在一列中绘制十个矩形:

    我知道这太简单了,但希望能显示出正在使用的三个潜在数据集:

  • 用户控件的设计时间(#FF7878粉彩
    <UserControl x:Class="EMC.Windows.AlarmsModule.UserControl1"
                 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"
        mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:AlarmsModule="clr-namespace:EMC.Windows.AlarmsModule" d:DesignHeight="300"
        d:DesignWidth="300"
                 d:DataContext="{d:DesignInstance Type=AlarmsModule:Alarm}"
        >