DataGrid のプレースホルダを判別する
◆ DataGrid のプレースホルダのオブジェクトは internal
◆ Trigger でイコールか比較したいのでリフレクションで static 変数に入れておく
◆ Trigger でイコールか比較したいのでリフレクションで static 変数に入れておく
DataGrid では一番下にプレースホルダというのがあります
ここをクリックして次の行を作って入力します
テンプレートはプレースホルダにも適用されてしまいます
そのため ボタンを入れると プレースホルダにもボタンが出てしまいます
よくあるのは行の削除ボタンを作るときかと思います
一応いれる C# 側も
このコードだとこうなります
この一番下のプレースホルダではボタンを消したいです
行ごとに処理されるので この例だと通常は Row のインスタンスが入ってるところにプレースホルダのときは何が入ってるのかをデバッグ実行で調べてみると NewItemPlaceholder というものが入っていました
名前で調べてみると CollectionView.NewItemPlaceholder というのがありました
この値とイコールなら 非表示にする Trigger を作ってみたのですが 非表示になりません
もう少しちゃんと調べてみると DataGrid のプレースホルダは DataGrid.NewItemPlaceholder でした
こっちは internal なので普通にアクセスすることはできません
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGrid.cs,eca4a34baa0f9921
プレースホルダかどうかの判定したいことはあるのに internal にされているのはちょっと困ります
Converter を使って 行が目的の型(Row)と≠かとか 型名が "NewItemPlaceholder" と一緒ならプレースホルダとみなすとかできなくはないのですが コード量がムダに増えますし 目的の型と比べる方法では型が変わるごとに変えないといけないのも不便です
型名を使うためにリフレクションするのならいっそ internal のをもってくればいっか ということでこういうヘルパクラスを作りました
DataGridHelper.NewItemPlaceholder で internal になっているプレースホルダのオブジェクトが取得できます
XAML の方は単純にイコールチェックの Trigger でいいのでシンプルに
を追加するだけです
全体はこうなります
一番下の行では×ボタンが消えていますね
ここをクリックして次の行を作って入力します
プレースホルダなのに表示される
DataGridTextColumn や DataGridCheckBoxColumn などでは気にすることはないのですが DataGridTemplateColumn のときには気をつけないといけないですテンプレートはプレースホルダにも適用されてしまいます
そのため ボタンを入れると プレースホルダにもボタンが出てしまいます
よくあるのは行の削除ボタンを作るときかと思います
<StackPanel>
<DataGrid ItemsSource="{Binding items}" AutoGenerateColumns="False" HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="列1" Binding="{Binding column1}" />
<DataGridTextColumn Header="列2" Binding="{Binding column2}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="Delete">×</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button>フォーカス外す用ボタン</Button>
</StackPanel>
<DataGrid ItemsSource="{Binding items}" AutoGenerateColumns="False" HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="列1" Binding="{Binding column1}" />
<DataGridTextColumn Header="列2" Binding="{Binding column2}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="Delete">×</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button>フォーカス外す用ボタン</Button>
</StackPanel>
一応いれる C# 側も
public class BindingData
{
public List<Row> items { get; set; } = new List<Row>
{
new Row { column1 = "1", column2 = "2" },
new Row { column1 = "11", column2 = "22" },
};
}
public class Row
{
public string column1 { get; set; }
public string column2 { get; set; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = new BindingData() {};
}
{
public List<Row> items { get; set; } = new List<Row>
{
new Row { column1 = "1", column2 = "2" },
new Row { column1 = "11", column2 = "22" },
};
}
public class Row
{
public string column1 { get; set; }
public string column2 { get; set; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = new BindingData() {};
}
このコードだとこうなります
この一番下のプレースホルダではボタンを消したいです
プレースホルダを判定する
XAML なので Trigger でプレースホルダかどうかを判定できればおっけいです行ごとに処理されるので この例だと通常は Row のインスタンスが入ってるところにプレースホルダのときは何が入ってるのかをデバッグ実行で調べてみると NewItemPlaceholder というものが入っていました
名前で調べてみると CollectionView.NewItemPlaceholder というのがありました
この値とイコールなら 非表示にする Trigger を作ってみたのですが 非表示になりません
もう少しちゃんと調べてみると DataGrid のプレースホルダは DataGrid.NewItemPlaceholder でした
こっちは internal なので普通にアクセスすることはできません
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGrid.cs,eca4a34baa0f9921
プレースホルダかどうかの判定したいことはあるのに internal にされているのはちょっと困ります
Converter を使って 行が目的の型(Row)と≠かとか 型名が "NewItemPlaceholder" と一緒ならプレースホルダとみなすとかできなくはないのですが コード量がムダに増えますし 目的の型と比べる方法では型が変わるごとに変えないといけないのも不便です
型名を使うためにリフレクションするのならいっそ internal のをもってくればいっか ということでこういうヘルパクラスを作りました
public class DataGridHelper
{
static public readonly object NewItemPlaceholder =
typeof(System.Windows.Controls.DataGrid)
.GetProperty("NewItemPlaceholder", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
.GetValue(null);
}
{
static public readonly object NewItemPlaceholder =
typeof(System.Windows.Controls.DataGrid)
.GetProperty("NewItemPlaceholder", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
.GetValue(null);
}
DataGridHelper.NewItemPlaceholder で internal になっているプレースホルダのオブジェクトが取得できます
XAML の方は単純にイコールチェックの Trigger でいいのでシンプルに
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Static local:DataGridHelper.NewItemPlaceholder}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Static local:DataGridHelper.NewItemPlaceholder}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
を追加するだけです
全体はこうなります
<StackPanel>
<DataGrid ItemsSource="{Binding items}" AutoGenerateColumns="False" HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="列1" Binding="{Binding column1}" />
<DataGridTextColumn Header="列2" Binding="{Binding column2}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Static local:DataGridHelper.NewItemPlaceholder}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
<Button Command="Delete">×</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button>フォーカス外す用ボタン</Button>
</StackPanel>
<DataGrid ItemsSource="{Binding items}" AutoGenerateColumns="False" HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="列1" Binding="{Binding column1}" />
<DataGridTextColumn Header="列2" Binding="{Binding column2}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Static local:DataGridHelper.NewItemPlaceholder}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
<Button Command="Delete">×</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button>フォーカス外す用ボタン</Button>
</StackPanel>
一番下の行では×ボタンが消えていますね