8

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>

ItemsControl.ItemTemplate DataTemplateExpression Blend でより簡単に設計できるように、中央を交換してユーザー コントロールにしたいと考えています。

MVVM Light にユーザー コントロールを含む単純なサンプルは見つかりませんが、いくつかのチュートリアル記事があります。たとえば、MVVM Instantiation Approaches (Option 6) では、Paul Stovell が MVVM Light で UserControl の ViewModel にバインドすることを提案しています。

<UserControl ...>
    <UserControl.Resources>
        <ViewModelLocator x:Key="ViewModelLocator"/>
    </UserControl.Resources>
    <TextBox Text="{Binding Source={DynamicResource ViewModelLocator}, Path=CalculatorViewModel...}" />

ロケーターがダミー データでいっぱいの ViewModel を提供できるため、Expression Blend で UserControl を設計している場合、これはうまく機能します。しかし、実行時に何が起こるか。そのバインディングは、メイン ViewModel のコレクションによって提供される UserControl の ViewModel クラスのインスタンスでどのように上書きされますか? 設計時に MainWindow でも同じ問題が発生します。MainWindow のルック アンド フィールで Expression Blend を使用している場合、そのバインディングは、デザイン タイムのメイン ViewModel のコレクションによって提供される UserControl の ViewModel クラスのインスタンスでどのように上書きされますか?

すでにこれに触れている多くの質問と回答があります。

  1. https://stackoverflow.com/a/3334780/575530で akjoshi は、メインの ViewModel が UserControl の ViewModel のインスタンスを保持することを提案しています。しかし、UserControl 自体を設計しているときに、それはどのように機能しますか?

  2. https://stackoverflow.com/a/9910298/575530で、タムは「データコンテキストを開いたままにして、このコントロールを使用しているコントロールにバインドできるようにしたい」と指摘し、次のコメントで SoMoS は必要なことを追加します「バインドされたプロパティのViewModelにプロパティを作成し、誰かがコントロールの1つのプロパティを変更したい場合(サブコントロールが有効になっているなど)、ビューモデルを通過する必要があります」。それは有望ですが、MainViewModel のバインド可能な UserControlViewModels のコレクションの代わりに何をすべきかわかりません。

  3. https://stackoverflow.com/a/6340668/575530で Ehsan Ershadi は、「MVVM Light ViewModelLocator は静的プロパティであり、ユーザー コントロールの複数のインスタンスをインスタンス化する場合に、UserControles に使用することはお勧めできません」と示唆しています。同じ共通の ViewModel があるため、それらはすべて同じように動作しますが、これは、プロジェクト全体で 1 回使用することにした場合に備えて、UserControl に必要なものではありません。」そして、「この問題を回避するには、たとえば、すべてのプロパティを非静的にすることで ViewModelLocator を変更する必要がある」と述べています。それがどのように役立つかわかりません。

  4. https://stackoverflow.com/a/2637830/575530の後のコメントで、Jon Mitchell は「MVVM はユーザー コントロールの作成には理想的ではないようです」と述べています。それが正しくないことを願っています。

  5. 対照的に、ページの代わりに UserControl を使用する必要があるのはいつですか? dthrasher は、「WPF MVVM フレームワークの多くは、ネストされた UserControls を使用してページを構成することを優先して、NavigationWindow コントロールと Page コントロールの使用を避けているように見える」と述べています。つまり、UserControls は MVVM では一般的なデバイスです。

  6. https://stackoverflow.com/a/1798649/575530で、Reed Copsey はサンドボックスに、「UserControls は、プロパティを公開し、DataBinding を使用することで、常に含まれているコントロールと通信できることを思い出させます。MVVM スタイルをすべての面で保持するため、これは非常に優れています。 " そして、「包含コントロールは、プロパティを使用して2つのユーザーコントロールの2つのプロパティをリンクし、きれいな境界を維持できます」しかし、Expression BlendでUserControlを設計しているときに、これがどのように役立つかわかりません。

  7. ビューに DataTemplates の代わりにUserControls を使用する必要がありますか? Rachel は、DataTemplate にコードをカット アンド ペーストする前に、Expression Blend を使用して UserControl を設計することについて言及しています。次に、内容をコピーして DataTemplate に貼り付けます」

このエッセイの長さの質問で申し訳ありません!MainWindow のコレクション内のアイテムのビジュアルになる予定の UserControl を設計するときに MVVM Light を使用する方法、特に 3 つのバインディング (ランタイム ビュー モデル、メインのデザイン タイム ビュー モデル) をセットアップする方法について混乱しています。ウィンドウとそのユーザー コントロールのインスタンス化、および分離されたユーザー コントロールの設計時のビュー モデル。

4

2 に答える 2

7

私はあなたが物事を過度に複雑にしていると思います:

どうしたの:

<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}"
    >

これにより、ユーザー コントロールを構築し、その中にデザイン時のデータを含めることができる場所に移動します。そしてそれは単純で、足場があったとしても多くは必要ありません。

于 2012-12-12T09:25:18.247 に答える
4

Faster Solutions の回答に基づいて、UserControl を使用して MVVM Light 内のリストの内容を表示する最も簡単な例を次に示します。

完全を期すために、ユーザー コントロールのビュー モデルとメイン ビュー モデルの両方で実行時データとは異なる設計時データを提供しながら、できるだけ短くしようとしたすべてのコードを含めます。

まず、ロケータ VMUCExample/ViewModel/ViewModelLocator.cs:

using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;

namespace VMUCExample.ViewModel
{
    public class ViewModelLocator
    {
        static ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<ASquareViewModel>();
        }

        public ASquareViewModel ASquare
        {
            get
            {
                return ServiceLocator.Current.GetInstance<ASquareViewModel>();
            }
        }

        public MainViewModel Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }

        public static void Cleanup() {}
    }
}

単純にするために、MVVM Light のデータ サービス側は使用していません。ライブ データと設計時のデータの目に見える違いは、2 つのビュー モデル クラスで処理されます。

VMUCExample/ViewModel/ASquareViewModel.cs:

using System.Windows.Media;
using GalaSoft.MvvmLight;

namespace VMUCExample.ViewModel
{
    public class ASquareViewModel : ViewModelBase
    {
        private Brush _SquareColour;
        public Brush SquareColour
        {
            get
            {
                return _SquareColour ?? (_SquareColour = IsInDesignModeStatic ?
                    new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0x78, 0x78)) : // FF7878 (pastel red)
                    new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xBB, 0x78))); // FFBB78 (nectarine)
            }
            set { _SquareColour = value; }
        }
    }
}

したがって、Expression Blend のユーザー コントロールを見ると、パステル カラーの赤で塗りつぶされた単純な四角形が表示されます。

編集用にユーザー コントロールを開いた状態でスクリーンショットをブレンドする

メイン ビュー モデルはファイル VMUCExample/ViewModel/MainViewModel.cs にあります。

using System.Collections.Generic;
using System.Windows.Media;
using GalaSoft.MvvmLight;

namespace VMUCExample.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        private List<ASquareViewModel> _Squares;
        public List<ASquareViewModel> Squares
        {
            get
            {
                if (_Squares == null)
                {
                    _Squares = new List<ASquareViewModel>();
                    var colour = IsInDesignModeStatic ?
                        new SolidColorBrush(Color.FromArgb(0xFF, 0x78, 0xB2, 0xFF)) : // 78B2FF (pastel blue)
                        new SolidColorBrush(Color.FromArgb(0xFF, 0xF9, 0xFF, 0xC7)); // F9FFC7 (eggshell)
                    for (var i = 0; i < 10; i++)
                    {
                        _Squares.Add(new ASquareViewModel {SquareColour = colour});
                    }
                }
                return _Squares;
            }
            set { _Squares = value; }
        }
        public MainViewModel() {}
    }
}

このビューは Expression Blend で編集することもできますが、ビュー モデル コードはデザイン時のカラー データを別の方法で設定します。

編集用にメイン ウィンドウを開いた状態でスクリーンショットをブレンドする

これらは 2 つの XAML ファイルで、最初は VMUCExample/ASquareUC.xaml です。

<UserControl x:Class="VMUCExample.ASquareUC"
             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"
             mc:Ignorable="d" d:DesignHeight="15" d:DesignWidth="60"
             d:DataContext="{Binding ASquare, Mode=OneWay, Source={StaticResource Locator}}">
    <Grid>
        <Rectangle Fill="{Binding SquareColour}" Margin="2" Width="50" Height="10"/>
    </Grid>
</UserControl>

ユーザー コントロールを設計しているときに、 Expression Blendd:DataContextに必要な設計時のバインディングが、実行時に必要なデータ コンテキストや、 Expression Blend でメイン ウィンドウを設計している場合は親になります。私はこれについて不快ですが、Paul Stovell のOption 6: A XAML View Model LocatorでMVVM Light として特徴付けられ、 @LBugnionによって承認されたアプローチではありません。

もう 1 つのビュー ファイルは VMUCExample\MainWindow.xaml です。

<Window x:Class="VMUCExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vmucExample="clr-namespace:VMUCExample"
        Height="200" Width="100"
        DataContext="{Binding Main, Source={StaticResource Locator}}">
    <Grid x:Name="LayoutRoot">
        <ScrollViewer ScrollViewer.CanContentScroll="True">
            <ItemsControl ItemsSource="{Binding Squares}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <vmucExample:ASquareUC/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl> 
        </ScrollViewer>
    </Grid>
</Window>

サンプル アプリケーション自体は、列に 10 個の四角形を描画するだけです。

簡単なテスト アプリのスクリーンショット

単純すぎることはわかっていますが、うまくいけば、使用されている 3 つの潜在的なデータ セットが表示されます。

  1. ユーザー コントロールのデザインタイム (#FF7878 パステル レッド)、
  2. メイン ウィンドウ (#78B2FF パステル ブルー) によって設定されたユーザー コントロールのデザイン タイム、および
  3. メイン ウィンドウによって設定されたユーザー コントロールの実行時間 (#F9FFC7 Eggshell)。

(注意: メイン ウィンドウによって設定されないユーザー コントロールの実行時間という別のデータ オプションがあります。この場合、ユーザー コントロールのビュー モデルは #FFBB78/nectarine を選択しますが、これらのバインドを調べるためにそれは必要ありませんでした。)

完全を期すために、VMUCExample\App.xaml ファイルを次に示します。

<Application x:Class="VMUCExample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="clr-namespace:VMUCExample.ViewModel"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             StartupUri="MainWindow.xaml"
             mc:Ignorable="d">
    <Application.Resources>
        <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
    </Application.Resources>
</Application>
于 2012-12-12T11:53:01.997 に答える