imani-cの日記

WPFスタイルとテンプレートの本をAmazonで出しました。ちょっととっつきにくいこれらについて知りたいお方は、覗いてみてください。「WPF スタイル」で検索すると、トップに出ます。gRPCの本もあります。「gRPC入門」で検索するとすぐ見つかります。

WPF - Gridの行を非表示にする

Gridは、画面に整理された感じを与えてくれるコンテナです。

しかし、場合によって一部の行や列を非表示にするということができず、いまいち融通が利きません。ここでは、これを解決するための二つのアイデアを説明します。

行も列も同じなので、ここでは行に関してだけ記します。

目次

Gridの行や列を非表示にする方法1 - ViewModelで高さを指定する

Gridの行の高さを決めるRowDefinitionには、行を非表示ににする機能がありません。そのため、行を非表示にするには、行の高さを0にするしかありません。

単純には、行の高さをビューモデルのdouble型プロパティにバインドすればよいわけです。

    <Grid.RowDefinitions>
        <RowDefinition Height="{Binding RowHeight}"/>
    </Grid.RowDefinitions>
    // ViewModel内
    public class MyViewModel{
        ...
        public double RowHeight {get; set; } = 20;
    }

確かにこの方法でも非表示にできますが、ViewModelがViewが管理すべきコントロールのサイズまで知っていなければなりません。これは、ViewとViewModelの分離という観点から、あまり好きになれません。

Gridの行や列を非表示にする方法2 - コンバーターを使ってbool型プロパティで制御する

そこで、ViewModelでは行を表示すべきか否かだけを制御すればよいようにしましょう。行を表示すべきか否かを示すbool型プロパティをViewModelに置き、Viewからはそれを参照して行の表示・非表示を決めます。表示されているときの行の高さは、Viewの中だけに閉じることができます。

RowDefinition.Heightをbool型プロパティにバインドし、trueの時には非表示にするために高さを0にして、falseの時には指定した高さにするコンバーターを作ります。

// bool型の値を、trueなら高さ0、falseならしていた高さに変換するコンバーター。
namespace RowHeightByBool
{
    public class TrueToCollapseCoverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            => (bool)value ? new GridLength(0) : Length;

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            => new NotImplementedException();

        public GridLength Length { get; set; }
    }
}

以下は、このコンバーターの利用例です。「...」の部分は、VisualStudioが自動生成したままですの部分です。

<Window x:Class="RowHeightByBool.MainWindow"
        ...
        xmlns:local="clr-namespace:RowHeightByBool"
        ...>

    <Grid Width="300" Height="200" Background="Silver" VerticalAlignment="Center" HorizontalAlignment="Center">
        <Grid.Resources>
            <!-- 行を非表示にするためのコンバーター -->
            <local:TrueToCollapseCoverter x:Key="TrueToCollapse" Length="*"/>
        </Grid.Resources>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <!-- この行を非表示にできる -->
            <RowDefinition Height="{Binding CollapseRow, Converter={StaticResource TrueToCollapse}}"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <CheckBox Content="Collapse" IsChecked="{Binding CollapseRow}"/>
        <TextBlock Text="This is row to collapse." Background="Salmon" Grid.Row="1"/>
        <TextBlock Text="This is Bottom line which is always shown." Background="SkyBlue" Grid.Row="2"/>
    </Grid>
</Window>

ビューモデルは以下のようになりますが、作るのが面倒なのでコードビハインドで済ませました。

namespace RowHeightByBool
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            DataContext = this;
            InitializeComponent();
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        // 行の表示を制御する。
        public bool CollapseRow
        {
            get => _collapseRow;
            set
            {
                _collapseRow = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CollapseRow)));
            }
        }
        private bool _collapseRow = false;
    }

チェックボックスのチェックの有無により表示が以下のように変化します。

チェックされていないとき、2行目が表示される
チェックされているとき、2行目が消える