[WPF]Dictionaryを使ったVisibilityの制御

このようなアプリがあったとします。

ボタンがたくさん並んでいるアプリですね。
このアプリのボタンを以下のように消したいとなったとき皆さんはどうしますか?

私なりの方法を共有させていただきます。

プロパティをバインディングする

1つ目の方法として、各ボタンに対してVisibilityのプロパティを用意しそれぞれバインディングさせる方法です。

以下がソースコードとなります。

xaml

<Window x:Class="App.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:local="clr-namespace:WpfApp8"
        xmlns:converter="clr-namespace:SD2APGB1.Model.ValueConverter"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>

    <Window.Resources>
        <ResourceDictionary>
            <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </ResourceDictionary>
    </Window.Resources>

    <StackPanel>
        <Button Content="button1" Height="30" Margin="5" Visibility="{Binding IsButton1Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button2" Height="30" Margin="5" Visibility="{Binding IsButton2Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button3" Height="30" Margin="5" Visibility="{Binding IsButton3Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button4" Height="30" Margin="5" Visibility="{Binding IsButton4Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button5" Height="30" Margin="5" Visibility="{Binding IsButton5Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button6" Height="30" Margin="5" Visibility="{Binding IsButton6Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button7" Height="30" Margin="5" Visibility="{Binding IsButton7Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button8" Height="30" Margin="5" Visibility="{Binding IsButton8Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button9" Height="30" Margin="5" Visibility="{Binding IsButton9Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
        <Button Content="button10" Height="30" Margin="5" Visibility="{Binding IsButton10Visibility,Converter={StaticResource BoolToVisibilityConverter}}" />
    </StackPanel>
</Window>

C#(ViewModel側)

using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace App
{
    public class MainWindowViewModel : BindableBase
    {

        public MainWindowViewModel()
        {
            IsButton1Visibility = true;
            IsButton2Visibility = false;
            IsButton3Visibility = true;
            IsButton4Visibility = false;
            IsButton5Visibility = true;
            IsButton6Visibility = false;
            IsButton7Visibility = true;
            IsButton8Visibility = false;
            IsButton9Visibility = true;
            IsButton10Visibility = false;
        }

        public bool IsButton1Visibility
        {
            get { return isButton1Visibility; }
            set { SetProperty(ref isButton1Visibility, value); }
        }
        private bool isButton1Visibility;

        public bool IsButton2Visibility
        {
            get { return isButton2Visibility; }
            set { SetProperty(ref isButton2Visibility, value); }
        }
        private bool isButton2Visibility;

        public bool IsButton3Visibility
        {
            get { return isButton3Visibility; }
            set { SetProperty(ref isButton3Visibility, value); }
        }
        private bool isButton3Visibility;

        public bool IsButton4Visibility
        {
            get { return isButton4Visibility; }
            set { SetProperty(ref isButton4Visibility, value); }
        }
        private bool isButton4Visibility;

        public bool IsButton5Visibility
        {
            get { return isButton5Visibility; }
            set { SetProperty(ref isButton5Visibility, value); }
        }
        private bool isButton5Visibility;

        public bool IsButton6Visibility
        {
            get { return isButton6Visibility; }
            set { SetProperty(ref isButton6Visibility, value); }
        }
        private bool isButton6Visibility;

        public bool IsButton7Visibility
        {
            get { return isButton7Visibility; }
            set { SetProperty(ref isButton7Visibility, value); }
        }
        private bool isButton7Visibility;

        public bool IsButton8Visibility
        {
            get { return isButton8Visibility; }
            set { SetProperty(ref isButton8Visibility, value); }
        }
        private bool isButton8Visibility;

        public bool IsButton9Visibility
        {
            get { return isButton9Visibility; }
            set { SetProperty(ref isButton9Visibility, value); }
        }
        private bool isButton9Visibility;

        public bool IsButton10Visibility
        {
            get { return isButton10Visibility; }
            set { SetProperty(ref isButton10Visibility, value); }
        }
        private bool isButton10Visibility;
    }
}

ボタンの数が増えるたびにプロパティを増やすのはなんか嫌ですよね。

その問題を解決したのが次の方法です。

Dictionaryを使ってプロパティをバインディング

C#(ViewModel側)

ExpandoObjectを使いプロパティを動的に生成します。だいぶスッキリしたんじゃないでしょうか。

using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp8
{
    public class MainWindowViewModel : BindableBase
    {
        public MainWindowViewModel()
        {
            visibilitiesAsDictionary.Add("button1", Visibility.Visible);
            visibilitiesAsDictionary.Add("button2", Visibility.Hidden);
            visibilitiesAsDictionary.Add("button3", Visibility.Visible);
            visibilitiesAsDictionary.Add("button4", Visibility.Hidden);
            visibilitiesAsDictionary.Add("button5", Visibility.Visible);
            visibilitiesAsDictionary.Add("button6", Visibility.Hidden);
            visibilitiesAsDictionary.Add("button7", Visibility.Visible);
            visibilitiesAsDictionary.Add("button8", Visibility.Hidden);
            visibilitiesAsDictionary.Add("button9", Visibility.Visible);
            visibilitiesAsDictionary.Add("button10", Visibility.Hidden);

            Visibilities = visibilitiesAsDictionary;
        }

        IDictionary<string, Object> visibilitiesAsDictionary = new ExpandoObject() as IDictionary<string, Object>;

        public dynamic Visibilities
        {
            get { return _visibilities; }
            set { SetProperty(ref _visibilities, value); }
        }
        private dynamic _visibilities;
    }
}

xaml

xaml側はこのように記述します。

<Window x:Class="App.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:local="clr-namespace:WpfApp8"
        xmlns:converter="clr-namespace:SD2APGB1.Model.ValueConverter"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>

    <Window.Resources>
        <ResourceDictionary>
            <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </ResourceDictionary>
    </Window.Resources>

    <StackPanel>
        <Button Content="button1" Height="30" Margin="5" Visibility="{Binding Visibilities.button1}" />
        <Button Content="button2" Height="30" Margin="5" Visibility="{Binding Visibilities.button2}" />
        <Button Content="button3" Height="30" Margin="5" Visibility="{Binding Visibilities.button3}" />
        <Button Content="button4" Height="30" Margin="5" Visibility="{Binding Visibilities.button4}" />
        <Button Content="button5" Height="30" Margin="5" Visibility="{Binding Visibilities.button5}" />
        <Button Content="button6" Height="30" Margin="5" Visibility="{Binding Visibilities.button6}" />
        <Button Content="button7" Height="30" Margin="5" Visibility="{Binding Visibilities.button7}" />
        <Button Content="button8" Height="30" Margin="5" Visibility="{Binding Visibilities.button8}" />
        <Button Content="button9" Height="30" Margin="5" Visibility="{Binding Visibilities.button9}" />
        <Button Content="button10" Height="30" Margin="5" Visibility="{Binding Visibilities.button10}" />
    </StackPanel>
</Window>

みなさんの参考になれば幸いです。

補足

コンバーターはこんな感じです。

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace App.ValueConverter
{
    public class BoolToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is bool valueBool)) return DependencyProperty.UnsetValue;

            return (valueBool) ? Visibility.Visible : Visibility.Hidden;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA