- 2012-04-08 (日) 3:56
- WPF
DataGridコントロールなどのセル内部にDataTemplateでコントロールを配置した場合、コントロールにNameを付けていてもプログラム側から直接アクセスすることはできません。複数行ある場合にどの行のコントロールを表しているのか分からないので当たり前と言えば当たり前の仕様なのですが、それでもアクセスしたいことってありますよね!
いろいろと悩んだのですが、「VisualTreeから引っ張ってくるしかない」という結論に至りました。
(他の方法があれば是非ご教授ください・・・^^;)
別に何のコントロールでもよいのですが、とりあえずDataGridで話を進めていきます。まず、元となるDataTemplate部分。
XAML
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="tbxHoge"/>
</DataTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
上記のTextBoxにアクセスしたいからといって、プログラムに”tbxHoge”と書いてもコンパイラに怒られてしまいます。
面倒ですが、地道にVisualTreeを辿っていきましょう。
VisualTreeから対象要素を取り出す
VisualTreeを辿って行く方法ですが、今回はVisualTreeHelperクラスのGetChild()を使用します。名前からも想像できるかと思いますが、子要素を取得するためのメソッドです。
任意のコントロールを取得できるようにジェネリックメソッドとして実装しておきます。
ソース
private T GetElement<T>(DependencyObject reference) where T : FrameworkElement
{
//==== 渡された要素の確認 ====//
if (reference is T)
{
return reference as T;
}
//==== 子要素内を探す ====//
DependencyObject elem = null;
int count = VisualTreeHelper.GetChildrenCount(reference);
for (int idx = 0; idx < count; idx++)
{
elem = GetChildElement<T>(reference, idx);
if (elem != null)
{
break;
}
}
return elem as T;
}
private T GetChildElement<T>(DependencyObject reference, int childIdx) where T : FrameworkElement
{
//==== 子要素取得 ====//
var child = VisualTreeHelper.GetChild(reference, childIdx);
if (child == null)
{
return null;
}
if (child is T)
{
return child as T;
}
//==== 子要素内を探す ====//
DependencyObject elem = reference;
int count = VisualTreeHelper.GetChildrenCount(child);
for (int idx = 0; idx < count; idx++)
{
elem = GetChildElement<T>(child, idx);
if (elem != null)
{
break;
}
}
return elem as T;
}
同じようなメソッドを2つも定義するなと突っ込まれそうですが、呼び出し元のコードをシンプルにするためにこのような形にしています。
対象コントロールが子要素に見つからなかった場合は孫、孫に見つからなかったら更にその子・・・といった具合に、どんどんと子を探しに行くように再帰関数として実装しています。
さて、準備が整ったのでさっそくTextBoxにアクセスしてみましょう。
GetElement()の引数にはコンテナオブジェクトを指定するのですが、DataGridでコンテナを引っ張ってくる方法を考えなければなりません。実はこれまた面倒・・・ここにきてDataGridを例にあげたことを後悔・・・(苦笑)
DataGridのコンテナ取得
DataGridのコンテナに相当するものはDataGridCellなのですが、残念ながらさっくりと取得できるようなメソッドは(たぶん)用意されていません。
ではどのように取得するのかですが、次のステップを踏まなければなりません。
- LoadingRowイベントハンドラでDataGridRowデータを取得
- ColumnsおよびDataGridRowデータからDataGridCellオブジェクトを取得
LoadingRowイベントハンドラでDataGridRowデータを取得
DataGridCell取得時にDataGridRowオブジェクトが必要となります。DataGridRowオブジェクトはLoadingRowイベントで取得できるので、このタイミングで適当な領域に格納しておきます。Dictionaryを使えばインデックスとDataGridRowオブジェクトの関係を分かりやすく管理できます。
ソース
private Dictionary<int, DataGridRow> rowContainer = new Dictionary<int, DataGridRow>();
void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
rowContainer[e.Row.GetIndex()] = e.Row;
}
ColumnsおよびDataGridRowデータからDataGridCellオブジェクトを取得
DataGridCellはDataGridColumnクラスのGetCellContent()により取得することができます。そして、このメソッドの引数としてDataGridRowオブジェクトが必要となってくるわけです。
row行、2列目のセル取得の場合は次のようになります。
ソース
var elem = dataGrid.Columns[2].GetCellContent(rowContainer[row]);
これでDataTemplateの内のコントロールにアクセスする準備が全て整いました。
DataTemplate内コントロール取得
上記で作った関数を呼び出すことで任意コントロールを簡単に取得できます。
ソース
public void Hoge()
{
//==== TextBox取得 ====//
TextBox tbTmp = GetTextBox(1);
// 任意の処理
}
private TextBox GetTextBox(int row)
{
//==== 対象セルのContent取得 ====//
var elem = dataGrid.Columns[0].GetCellContent(rowContainer[row]);
if (elem == null)
{
return null;
}
//==== コントロール取得 ====//
TextBox tb = GetElement<TextBox>(elem);
return tb;
}
お気づきの方もいるかも知れませんが、今回作ったメソッドには1つ問題があります。例えば、DataTemplate内に複数のTextBoxがあるようなケースでは最初の1つのTextBoxしか取り出すことが出来ません。
まぁ、必要に応じていろいろ弄ってみてください。
関連があると思われる記事:
- [WPF] DataGridのスクロール制御
- [WPF] DataGrid ヘッダー背景色の設定
- [WPF] DataGrid 指定セルにフォーカス移動
- [Android] ScrollViewのスクロールいろいろ
- [Android] Fix Project Properties.
- Newer: [WPF] XAMLだけでコントロール回転
- Older: [WPF] DataGrid ヘッダー背景色の設定
Comments:0
Trackbacks:0
- Trackback URL for this entry
- http://gacken.com/wp/program/wpf/641/trackback/
- Listed below are links to weblogs that reference
- [WPF] DataTemplateの中のコントロール取得 from ミライニトドケ