20

好吧......这让我难住了。我已经覆盖OnContentTemplateChanged了我的UserControl子类。我正在检查传入的值newContentTemplate是否实际上等于this.ContentTemplate(确实如此)但是当我调用它时......

var textBox = this.ContentTemplate.FindName("EditTextBox", this);

...它引发以下异常...

“此操作仅对应用了此模板的元素有效。”

根据另一个相关问题的评论者,他说你应该传递控件的内容演示者,而不是控件本身,所以我然后尝试了这个......

var cp = FindVisualChild<ContentPresenter>(this);

var textBox = this.ContentTemplate.FindName("EditTextBox", cp);

... whereFindVisualChild只是 MSDN 示例(见下文)中用于查找关联内容演示者的辅助函数。找到时cp,它也会引发相同的错误。我难住了!!

这是供参考的辅助函数...

private TChildItem FindVisualChild<TChildItem>(DependencyObject obj)
where TChildItem : DependencyObject {

    for(int i = 0 ; i < VisualTreeHelper.GetChildrenCount(obj) ; i++) {

        var child = VisualTreeHelper.GetChild(obj, i);

        if(child is TChildItem typedChild) {
            return typedChild;
        }
        else {
            var childOfChild = FindVisualChild<TChildItem>(child);
            if(childOfChild != null)
                return childOfChild;
        }
    }

    return null;
}
4

3 回答 3

18

在调用FindName方法之前显式应用模板将防止此错误。

this.ApplyTemplate(); 
于 2013-03-17T23:46:07.683 回答
4

正如 John 所指出的,在 OnContentTemplateChanged 实际应用于底层 ContentPresenter 之前,它已被触发。因此,您需要延迟对 FindName 的调用,直到它被应用。就像是:

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-19T17:24:32.950 回答
0

直到该事件之后,ContentTemplate 才会应用于 ContentPresenter。虽然此时在控件上设置了 ContentTemplate 属性,但它还没有被推送到 ControlTemplate 内部的绑定,例如 ContentPresenter 的 ContentTemplate。

你最终想用 ContentTemplate 做什么?可能有更好的整体方法来实现您的最终目标。

于 2011-04-15T19:12:32.577 回答