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

dum*_*dad 7 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"
            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>
Run Code Online (Sandbox Code Playgroud)

我想交换中心ItemsControl.ItemTemplate DataTemplate并使其成为用户控件,以便我可以在Expression Blend中更轻松地设计它.

我找不到一个包含MVVM Light中的用户控件的简单示例,但是有一些教程文章.例如,在MVVM实例化方法(选项6)中,Paul Stovell建议在MVVM Light中的UserControl的ViewModel中绑定:

<UserControl ...>
    <UserControl.Resources>
        <ViewModelLocator x:Key="ViewModelLocator"/>
    </UserControl.Resources>
    <TextBox Text="{Binding Source={DynamicResource ViewModelLocator}, Path=CalculatorViewModel...}" />
Run Code Online (Sandbox Code Playgroud)

当我在Expression Blend中设计UserControl时,这将非常有用,因为定位器可以为ViewModel提供虚拟数据.但是在运行时会发生什么 如何通过主ViewModel中的集合提供的UserControl的ViewModel类实例覆盖该绑定?在设计时MainWindow也会出现同样的问题.如果我在Expression Blend中使用MainWindow的外观,那么该绑定如何被设计时主ViewModel中的集合提供的UserControl的ViewModel类实例覆盖?

有许多问题和答案已经触及:

  1. /sf/answers/233434631/中, akjoshi建议主ViewModel保存UserControl的ViewModel实例; 但是当我设计UserControl本身时,它是如何工作的?

  2. /sf/answers/693720891/中, tam指出"你想让你的datacontext保持打开并可用于绑定到你正在使用这个控件的控件",并在下面的评论SoMoS补充说,需要一个"在ViewModel中为绑定属性创建属性,当有人想要更改控件的一个属性时(比如启用了一些子控件),他必须通过View Model".这是有希望的,但我不知道该怎么做代替MainViewModel的可绑定的UserControlViewModel集合.

  3. /sf/answers/443846791/中, Ehsan Ershadi建议"对于UserControles使用MVVM Light ViewModelLocator不是一个好主意,因为它是一个静态属性,当你要实例化你的用户控件的多个实例时将会有相同的常见ViewModel,所以它们都是相同的,如果您决定在整个项目中使用它一次,这不是我们想要的UserControl." 然后声明"要解决这个问题,你需要通过使所有属性非静态来修改ViewModelLocator".我不确定这对我有什么帮助.

  4. /sf/answers/184648131/之后的评论中,Jon Mitchell提到"看起来MVVM看起来并不适合创建用户控件".我希望这不对.

  5. 相比之下,在何时应该使用UserControl而不是Page?dthrasher提到"许多WPF MVVM框架似乎都避免使用NavigationWindow和Page控件来支持使用嵌套的UserControls组合页面",即UserControls是MVVM中的普通设备.

  6. /sf/answers/125905461/中, Reed Copsey提醒沙箱"UserControls总是可以通过暴露属性和使用DataBinding与其控件进行通信.这非常好,因为它在所有方面都保留了MVVM样式. " 并且"包含控件可以使用属性将两个用户控件上的两个属性链接在一起,同样保留干净的边界"但是当我在Expression Blend中设计UserControl时,我再也看不出这有什么帮助.

  7. 我应该使用用户控件我的意见,而不是的DataTemplates?Rachel在将代码剪切并粘贴到DataTemplate之前偶尔提到使用Expression Blend来设计UserControl:"如果我想用它来设计DataTemplate,我通常会创建一个新的UserControl,按照我想要的方式设计它,然后将内容复制/粘贴到DataTemplate中"

对这篇文章长度问题感到抱歉!我很困惑如何在设计一个UserControl时使用MVVM Light,注定是MainWindow上集合中项目的可视化,特别是如何设置三个绑定:运行时视图模型,主要设计时间视图模型窗口及其用户控件的实例化,以及用户控件的设计时间视图模型.

Fas*_*ons 6

我觉得你过于复杂了:

有什么不对:

<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>
Run Code Online (Sandbox Code Playgroud)

将每个VibeEvent绑定到用户控件的DataContext.在用户控件本身,我建议创建一个设计时DataContext,以使设计更容易.Design-Time 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}"
    >
Run Code Online (Sandbox Code Playgroud)

这将使您进入可以构建用户控件并在其中包含设计时数据的位置.它很简单,并且不需要太多(如果有的话)脚手架.