В WPF как найти элемент в шаблоне, который включается через триггер?
У меня есть UserControl (не простой пользовательский элемент управления), который, в зависимости от некоторых пользовательских свойств состояния, заменяет различные ContentTemplates, все они определены как ресурсы в связанном файле XAML. В коде позади мне нужно найти один из элементов в замененных шаблонах ContentTemplates.
Теперь в элементе управления без внешнего вида (т.е. в пользовательском элементе управления) вы просто переопределяете OnApplyTemplate, а затем используете FindName, но это переопределение не срабатывает, когда ContentTemplate переключается триггером (... по крайней мере, для UserControl. У меня нет протестировал эту функциональность с помощью пользовательского элемента управления.)
Теперь я попытался связать событие Loaded с элементом управления в шаблоне подкачки, который срабатывает в коде, а затем я просто сохраняю "отправителя" в переменной уровня класса. Однако, когда я пытаюсь очистить это значение, подписавшись на событие Unloaded, оно также не срабатывает, потому что tempalte выгружается, таким образом отключая это событие до того, как оно сможет вызвать, и элемент управления выгружается с экрана без вывода сообщений, но у меня все еще есть эта ссылка в коде позади.
Чтобы имитировать функциональность OnApplyTemplate, я подумываю подписаться на уведомление ContentTemplateChanged и просто использовать VisualTreeHelper для поиска нужного элемента управления, но мне интересно, есть ли лучший способ, отсюда и этот пост.
Есть идеи?
Для справки вот очень урезанный пример элемента управления, который у меня есть. В этом примере, если IsEditing имеет значение true, я хочу найти текстовое поле с именем "FindMe". Если IsEditing имеет значение false, что означает, что ContentTemplate не поменялся местами, я хочу получить 'null'...
<UserControl x:Class="Crestron.Tools.ProgramDesigner.Controls.EditableTextBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Crestron.Tools.ProgramDesigner.Controls"
x:Name="Root">
<UserControl.Resources>
<DataTemplate x:Key="EditModeTemplate">
<TextBox x:Name="FindMe"
Text="{Binding Text, ElementName=Root}" />
</DataTemplate>
<Style TargetType="{x:Type local:EditableTextBlock}">
<Style.Triggers>
<Trigger Property="IsEditing" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditModeTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<TextBlock x:Name="TextBlock"
Text="{Binding Text, ElementName=Root}" />
</UserControl>
Ааааааа и иди!
M
1 ответ
К сожалению, лучшего способа нет. Вы можете переопределить OnContentTemplateChanged вместо подключения к событию.
Вам нужно будет использовать метод DataTemplate.FindName, чтобы получить фактический элемент. Ссылка содержит пример того, как этот метод используется.
Однако вам придется отложить вызов FindName, если используется OnContentTemplateChanged, поскольку он не применяется к базовому ContentPresenter немедленно. Что-то вроде:
protected override void OnContentTemplateChanged(DataTemplate oldContentTemplate, DataTemplate newContentTemplate) {
base.OnContentTemplateChanged(oldContentTemplate, newContentTemplate);
this.Dispatcher.BeginInvoke((Action)(() => {
var cp = FindVisualChild<ContentPresenter>(this);
var textBox = this.ContentTemplate.FindName("EditTextBox", cp) as TextBox;
textBox.Text = "Found in OnContentTemplateChanged";
}), DispatcherPriority.DataBind);
}
Кроме того, вы можете подключить обработчик к событию LayoutUpdated в UserControl, но это может происходить чаще, чем вы хотите. Это также будет обрабатывать случаи неявных DataTemplates, хотя.
Что-то вроде этого:
public UserControl1() {
InitializeComponent();
this.LayoutUpdated += new EventHandler(UserControl1_LayoutUpdated);
}
void UserControl1_LayoutUpdated(object sender, EventArgs e) {
var cp = FindVisualChild<ContentPresenter>(this);
var textBox = this.ContentTemplate.FindName("EditTextBox", cp) as TextBox;
textBox.Text = "Found in UserControl1_LayoutUpdated";
}