В WPF как найти элемент в шаблоне, который включается через триггер? - PullRequest
2 голосов
/ 15 апреля 2011

У меня есть UserControl (не простой пользовательский элемент управления), который, в зависимости от некоторых пользовательских свойств состояния, заменяет различные ContentTemplates, все они определены как ресурсы в связанном файле XAML. В выделенном фрагменте кода мне нужно найти один из элементов в замененных шаблонах ContentTemplates.

Теперь в элементе управления без внешнего вида (то есть в пользовательском элементе управления) вы просто переопределяете OnApplyTemplate, а затем используете FindName, но это переопределение не срабатывает, когда ContentTemplate переключается триггером (... по крайней мере, не для UserControl. I не проверял эту функциональность с помощью пользовательского элемента управления.)

Теперь я попытался связать событие Loaded с элементом управления в шаблоне подкачки, который запускается в коде, а затем я просто сохраняю 'sender' в переменной уровня класса. Однако, когда я пытаюсь очистить это значение, подписавшись на событие 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 Ответ

2 голосов
/ 15 апреля 2011

К сожалению, лучшего способа нет. Вы можете переопределить 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";
}
...