何らかのイベントで ContentTemplate を変更したい ContentControl があります。ContentTemplate のコントロールが読み込まれたときに、いくつかの値 (TextBox へのテキスト) を追加したいと考えています。しかし、プロパティ ContentTemplate を変更した後、新しい ContentTemplate が直接ではなく (新しいテンプレートのすべてのコントロールをロードするという点で) 適用されることを発見しました。
myContentControl.ContentTemplate = newContentTemplate;
// at this line controls of new template are not loaded!
その行の後にこのコードを追加してテストしました:
var cp = GetVisualChild<ContentPresenter>(myContentControl);
var txt = myContentControl.ContentTemplate.FindName("Path_Cover", cp) as TextBox;
txt.Text = "test";
GetVisualChild
private T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
エラーが発生しました:
この操作は、このテンプレートが適用されている要素に対してのみ有効です。
新しい ContentTemplate が完全に適用されたことを示すイベントはありますか?
編集1
@eran私はonApplyTemplateを試しました
public override void OnApplyTemplate()
{
var cp = GetVisualChild<ContentPresenter>(Content_Option);
var txt = Content_Option.ContentTemplate.FindName("Path_Cover", cp) as TextBox;
txt.Text = "test";
}
しかし、エラーが発生しました:
オブジェクト参照がオブジェクト インスタンスに設定されていません。
編集2
この「ダーティ」メソッドは問題なく機能します。
myContentControl.ContentTemplate = newContentTemplate;
System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(0.000001);
timer.Tick += new EventHandler(delegate(object s, EventArgs a)
{
timer.Stop();
var cp = GetVisualChild<ContentPresenter>(Content_Option);
var txt = Content_Option.ContentTemplate.FindName("Path_Cover", cp) as TextBox;
txt.Text = "teSt";
});
timer.Start();
誰かがより「クリーンな」(プロフェッショナルな)方法で同じ結果を達成するのを手伝ってくれませんか:)
編集3
私のシナリオは、メニューとして TreeView (左側) を持ち、ContentControl の表示として Grid (右側) を持っています。TreeView にはいくつかのノードがあります。各ノードには独自の DataTemplate があります。TreeView ノードがクリックされるたびに、DataTemplate が ContentControl に設定され、値 (例: Path_Cover.Text) がデータベースから設定されます。レイアウトはほぼ Windows エクスプローラーに似ています。
さて、これはすべて必要なコードです:
XAML
<UserControl.Resources>
<DataTemplate x:Key="General">
<StackPanel>
<DockPanel>
<TextBlock Text="Cover"/>
<TextBox Name="Path_Cover"/>
</DockPanel>
<DockPanel>
<TextBlock Text="Slide"/>
<TextBox Name="Path_Slide"/>
</DockPanel>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Appearance">
<StackPanel>
<DockPanel>
<TextBlock Text="Cover"/>
<TextBox Name="Path_Cover"/>
</DockPanel>
<DockPanel>
<Button Content="Get Theme"/>
<TextBox Name="Txt_Theme"/>
</DockPanel>
</StackPanel>
</DataTemplate>
<UserControl.REsources>
<Grid>
<ContentControl Name="myContentControl"/>
</Grid>
コードビハインド
private void TreeMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
myContentControl.ContentTemplate =(DataTemplate)this.Resources[Tree_Menu.SelectedItem.ToString()];
System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(0.000001);
timer.Tick += new EventHandler(delegate(object s, EventArgs a)
{
timer.Stop();
switch (Tree_Menu.SelectedItem.ToString())
{
case "General":
var cp = GetVisualChild<ContentPresenter>(Content_Option);
var txt = Content_Option.ContentTemplate.FindName("Path_Cover", cp) as TextBox;
txt.Text = "test";
txt = Content_Option.ContentTemplate.FindName("Path_Slide", cp) as TextBox;
txt.Text = "test";
break;
case "Appearance":
var cp = GetVisualChild<ContentPresenter>(Content_Option);
var txt = Content_Option.ContentTemplate.FindName("Txt_Theme", cp) as TextBox;
txt.Text = "test";
break;
}
});
timer.Start();
}
timer.tick イベント ハンドラー内のコードを、DataTemplate/ContentTemplate が完全に適用された後に発生する新しいイベントに "移動" する必要があります。