2

我有一个 UserControl(不是一个不起眼的自定义控件),它根据一些自定义状态属性交换各种 ContentTemplates,所有内容都定义为关联 XAML 文件中的资源。在代码隐藏中,我需要在换入的 ContentTemplates 中找到其中一个元素。

现在在一个无外观的控件(即自定义控件)中,您只需覆盖 OnApplyTemplate 然后使用 FindName,但是当 ContentTemplate 被触发器切换时,该覆盖不会触发(......至少对于 UserControl 而言不是。我没有使用自定义控件测试了该功能。)

现在我尝试将 Loaded 事件连接到交换模板中的控件,该模板在代码隐藏中触发,然后我只需将“发送者”存储在类级变量中。但是,当我尝试通过订阅 Unloaded 事件来清除该值时,它也不会触发,因为模板被换出,因此在它有机会被调用之前取消连接该事件并且控件从屏幕上静默卸载,但我在代码隐藏中仍然有那个挂起的引用。

为了模拟 OnApplyTemplate 功能,我正在考虑订阅 ContentTemplateChanged 通知并仅使用 VisualTreeHelper 来查找我想要的控件,但我想知道是否有更好的方法,因此这篇文章。

有任何想法吗?

作为参考,这是我拥有的控件的一个非常精简的示例。在这个例子中,如果 IsEditing 为真,我想找到名为“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>

啊啊啊,去吧!

4

1 回答 1

2

不幸的是,没有更好的方法。您可以覆盖OnContentTemplateChanged,而不是连接到事件。

您需要使用DataTemplate.FindName方法来获取实际元素。该链接有一个如何使用该方法的示例。

如果使用 OnContentTemplateChanged,则需要延迟对 FindName 的调用,因为它不会立即应用于底层 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);
}

或者,您可以将处理程序附加到 UserControl 的LayoutUpdated事件,但这可能会比您想要的更频繁地触发。不过,这也可以处理隐式 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";
}
于 2011-04-15T11:33:38.047 回答