DataGrid – セルの中身をHiddenにしてもセルをクリックしたい(C# WPF)

C#

以下のようなRowの中にボタンが表示されているようなものを想像してください。
特定の条件の時だけボタンを表示する機能を考えます。

<Grid>
    <DataGrid ItemsSource="{Binding UserList}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
            <DataGridTextColumn Header="Age" Binding="{Binding Age}" Width="40"/>
            <DataGridTemplateColumn Width="100">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Content="ボタン" Width="50"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

簡単なように見えてStyleを書く場所によって挙動が違うので注意が必要です。
ざっくり3ヶ所ほど書く場所があります。

<Grid>
    <DataGrid ItemsSource="{Binding UserList}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
            <DataGridTextColumn Header="Age" Binding="{Binding Age}" Width="40"/>
            <DataGridTemplateColumn Width="100">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Content="ボタン" Width="50"/>

                        //ボタンのスタイルに書く
                        <Button.Style />
                    </DataTemplate>

                    //データテンプレートに書く
                    <DataTemplate.Triggers />

                </DataGridTemplateColumn.CellTemplate>

                //セルスタイルに書く
                <DataGridTemplateColumn.CellStyle />

            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

CellStyleに書く

私はセルの中身を消したいのだからCellStyleでVisibility=”Hidden”にすればいいと思いました。
実際にやってみるとこうなりました。

<DataGridTemplateColumn Width="100">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Button Content="ボタン" Width="50" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellStyle>
        <Style TargetType="{x:Type DataGridCell}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Visibility}" Value="Hidden" >
                    <Setter Property="Visibility" Value="Hidden" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>

カラムの形跡はありますが、選択行として認識されていないです。
さらにカラのボタンカラム上ではクリックしてもRowの選択ができません。

CellStyleのVisibilityをHiddenにすると、Rowから見てセルは無いものとみなされるようです。

DataTemplate.Triggerに書く

ということでCellStyleはVisibilityを設定するとRowの構造に影響を与えるのでダメでした。
では一段下げてDataTemplateにStyleを付けたらどうでしょうか。

<DataGridTemplateColumn Width="100">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Button Content="ボタン" Width="50" Visibility="Visible" />
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Visibility}" Value="Hidden" >
                    <Setter Property="Visibility" Value="Hidden" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

無事に通りましたね。
セルはきちんと存在していて、ボタンだけ非表示になりました。

しかし、この方法も問題があります。
それは表示中のものをHiddenに変えることはできても、もともと非表示の中身をVisibleに変えられないのです。

上のやり方だと、DataTemplateのVisibilityを弄っているので中身のButtonのVisibilityがHiddenだと、HiddenのボタンをVisibleにしてしまいます。
伝えにくいのですが、Setter Property=”Visibility” Value=”Hidden” と書いただけでは、DataTemplateの中身が見えるようになるのであって、決してButton visibility=”Visible”にしているわけではありません。

なので上記のやり方でButton visibility=”Visible”にしようとすると

<Button Name="ButtonName" Content="ボタン" Width="50" Visibility="Hidden" />

<Setter TargetName="ButtonName" Property="Visibility" Value="Visible" />

とターゲットを明示しなければなりません。
セル内の要素が1つであればよいのですが、複数あると手間が増えますね。
それに私はNameつけるのをなるべく避けたいのです。いちいち覚えてられないし、名前が被った時に面倒だからです。

他にもTriggerでVisibleにするという発想を転換して、TriggerでHiddenにする発想で組み立てても解決します。この辺りは条件次第ですね。

Button.Styleに書く

結局落ち着くところに落ち着いたんですが、セルやDataTemplateに書くとセル内の他の要素に影響するので、シンプルにButton.Styleに書くのがよいです。

<DataGridTemplateColumn Width="100">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Button Content="ボタン" Width="50" >
                <Button.Style>
                    <Style TargetType="Button">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Visibility}" Value="Hidden" >
                                <Setter Property="Visibility" Value="Hidden" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

なおButtonのVisibilityを先に規定したい場合、下記のようにはしないでください。

<Button Content="ボタン" Width="50" Visibility="Visible" >

xaml的には上位に書かれている条件の方が強いため、Button.styleでVisibilityを変更しているように思えても、実際にはVisibility=”Visible” が常に有効となり変更できなくなります。
もし書くのであれば下記のように行ってください。

<Button Content="ボタン" Width="50" >
    <Button.Style>
        <Style TargetType="Button">
            <Setter Property="Visibility" Value="Visible" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Visibility}" Value="Hidden" >
                    <Setter Property="Visibility" Value="Hidden" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

Tips 選択中の時だけ表示したい

Rowを選択中にだけ表示したい場合がよくあります。
これも少し応用してやるだけで簡単に実現できます。

<Style.Triggers>
    <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow}}" Value="True" >
        <Setter Property="Visibility" Value="Visible" />
    </DataTrigger>
</Style.Triggers>

DataGridにはIsSelectedプロパティが最初から存在しているので利用します。
単純にBinding Path=IsSelectedとしても認識されないので、RelativeSourceでDataGridRowまで遡ればIsSelectedプロパティを利用できます。

C#
スポンサーリンク
Once and Only

コメント