◇Pile Up◇ --Keenag Blog--

プログラミング備忘録ブログです。C#、WPFの記事が中心となります。

【WPF】1画面に複数のUserControlを可変で表示する方法

WPFで1つの画面の中に複数のUseControlを可変で表示させたいという要件があり、
以下のように実装することで実現することができました。

各クラスの役割

各クラスの役割はこんな感じです。 なお、いくつかのクラスは省略しています。詳細はサンプルコードを参考にしてください。

クラス名 役割
DisplayControlViewModel.cs 画面に表示させるUserControlのViewModelを抽象化したクラス
ComboBoxControlViewModel.cs DisplayControlViewmodelを継承したComboBoxControl.xaml のViewModel
ComboBoxControl.xaml ComboBoxを持ったUserControlクラス
MainWindowViewModel.cs MainWindow.xamlのViewModel
MainWindow.xaml 複数のUserControlを表示するWindow

DisplayControlViewModel.cs

画面に表示させるUserControlに紐づくViewModelクラスを抽象化したクラスです。

namespace DisplayUserControlListSample.ViewModels
{
    public abstract class DisplayControlViewModel
    {
    }
}

ComboBoxControlViewModel.cs

ComboBoxControl.xamlのViewModelクラスです。DisplayControlViewModelを継承しています。
コンボボックスに表示されるItemのコレクションを持ち、コンストラクタで値を初期化しています。

using Reactive.Bindings;

namespace DisplayUserControlListSample.ViewModels
{
    public class ComboBoxControlViewModel : DisplayControlViewModel
    {
        /// <summary>
        /// コンボボックスに表示されるItemのコレクション
        /// </summary>
        public ReactiveCollection<string> Items { get; }

        public ComboBoxControlViewModel()
        {
            Items = new ReactiveCollection<string>() { "Apple", "Banana", "Peach" };
        }
    }
}

ComboBoxControl.xaml

1つのComboBoxのみをもつXAMLです。ItemsSourceにComboBoxControlViewModelからItemsをバインドしています。

<UserControl x:Class="DisplayUserControlListSample.Views.ComboBoxControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"             
             prism:ViewModelLocator.AutoWireViewModel="True"
             Width="300"
             Height="30">
    <Grid Margin="0,0,0,5" >
        <StackPanel Orientation="Horizontal">
            <ComboBox Height="30"
                      Width="300"
                      FontFamily="MS Gothic"
                      FontSize="12"
                      HorizontalAlignment="Center"
                      VerticalAlignment="Center"
                      ItemsSource="{Binding Items}"
                       >
            </ComboBox>
        </StackPanel>
    </Grid>
</UserControl>

MainWindowViewModel.cs

DisplayControlViewModelクラスのコレクションをプロパティとしてもちます。このプロパティに表示したいUserControlに紐づいたViewModelのインスタンスを追加します。
※なお、本エントリーではTextBoxControlに関する説明は割愛しています

using Prism.Mvvm;
using Reactive.Bindings;

namespace DisplayUserControlListSample.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        public ReactiveCollection<DisplayControlViewModel> DisplayControlViewModels { get; } = new ReactiveCollection<DisplayControlViewModel>();

        public MainWindowViewModel()
        {
            DisplayControlViewModels.Clear();
            DisplayControlViewModels.Add(new ComboBoxControlViewModel());
            DisplayControlViewModels.Add(new TextBoxControlViewModel());
            DisplayControlViewModels.Add(new ComboBoxControlViewModel());
        }
    }
}

MainWindow.xaml

ItemsControl要素のItemsSourceプロパティにDisplayControlViewModelのリストをバインドします。
WindowのリソースにDataTemplateで表示するUserControlを定義します。
ItemsSourceにバインドされたDisplayControlViewModelのTypeを見て表示するUserControlを切り替えます。

<Window x:Class="DisplayUserControlListSample.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:DisplayUserControlListSample.Views"
        xmlns:viewModels="clr-namespace:DisplayUserControlListSample.ViewModels"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Height="350" Width="525"
        Title="DisplayUserControlListSample"
        Background="LightSlateGray">
    <Window.Resources>
        <DataTemplate DataType="{x:Type viewModels:TextBoxControlViewModel}">
            <views:TextBoxControl/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type viewModels:ComboBoxControlViewModel}">
            <views:ComboBoxControl/>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ScrollViewer  Margin="50,50,50,50"
                       HorizontalScrollBarVisibility="Auto"
                       VerticalScrollBarVisibility="Auto"
                       IsTabStop="False">
            <ItemsControl ItemsSource="{Binding DisplayControlViewModels}"
                          VerticalAlignment="Top"
                          IsTabStop="False" />
        </ScrollViewer>
    </Grid>
</Window>

実行結果

実行結果はこのようになります。
MainWindowViewModel.csで追加したViewmodelに紐づくUserControlが表示されます。
f:id:Keenag:20171008174037p:plain

サンプルコード

以下に公開しています。
https://github.com/Keenag/SampleCode/tree/master/DisplayUserControlListSample