Mam ContentControl, który chcę zmienić to ContentTemplate w niektórych przypadkach. Chcę dodać niektóre wartości (tekst do TextBox) po załadowaniu kontroli w ContentTemplate. Ale odkryłem, że nowy ContentTemplate jest stosowany (pod względem ładowania wszystkich kontrolek nowego szablonu) NIE BEZPOŚREDNIO po zmianie właściwości ContentTemplate.Czy jest jakieś zdarzenie wskazujące, że nowy ContentTemplate jest w pełni zastosowany?
myContentControl.ContentTemplate = newContentTemplate;
// at this line controls of new template are not loaded!
testowałem przez dodanej tym kodem po tej linii:
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;
}
Mam błąd:
This operation is valid only on elements that have this template applied.
Czy istnieje jakiś wydarzenie pokazując nowy ContentTemplat e jest całkowicie zastosowane?
EDIT 1
@eran Próbowałem 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";
}
ale mam błąd:
Object reference not set to an instance of an object.
EDIT 2
to "brudne" metoda działa dobrze:
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();
może ktoś mi pomóc osiągnąć ten sam rezultat z bardziej "czysty" (profesional) sposób :)
EDIT 3
My Scenariusz: Mam TreeView (po lewej stronie) jako menu i siatkę (po prawej stronie) jako wyświetlacz dla ContentControl. TreeView ma kilka węzłów. Każdy węzeł ma swój własny DataTemplate. Za każdym razem, gdy kliknięty jest węzeł TreeView, DataTemplate jest ustawiona na ContentControl, a wartość (np. Path_Cover.Text) jest ustawiana z bazy danych. Układ mniej więcej przypominający Eksplorator Windows.
Cóż, to wszystko jest niezbędne kod:
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>
Code Behind
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();
}
mi wystarczy "przenieść" kod wewnątrz timera .tick event handler do jakiegoś nowego zdarzenia, które po aplikacji DataTemplate/ContentTemplate zostało całkowicie zastosowane.
OnLoad lub OnLoadData jest wywoływana po OnApplayTemplate check msdn –
@eran Zdarzenie Onload jest uruchamiane tylko jeden raz. Chcę zdarzenia, które będzie uruchamiane za każdym razem, gdy zmienię ContentTemplate. – Reyn