Подтвердить что ты не робот

Зачем "this.ContentTemplate.FindName" бросать InvalidOperationException на свой собственный шаблон?

Хорошо... это меня озадачило. Я переопределил OnContentTemplateChanged в моем UserControl. Я проверяю, что значение, переданное для newContentTemplate, фактически равно этому. ContentTemplate (он делает) еще, когда я называю это...

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

... он выдает следующее исключение...

"Эта операция действительна только для элементов, которые применяют этот шаблон."

В комментариях к другому связанному вопросу он сказал, что вы должны передать в контент-презентаторе элемент управления, а не сам элемент управления, поэтому я тогда попробовал это...

var cp = FindVisualChild<ContentPresenter>(this);

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

где FindVisualChild - это просто вспомогательная функция, используемая в примере MSDN (см. ниже), чтобы найти связанный контент-презентатор. Хотя "cp" найден, он также вызывает такую ​​же ошибку. Я в тупике!

Здесь вспомогательная функция для справки...

private childItem FindVisualChild<childItem>(DependencyObject obj)
    where childItem : DependencyObject
{
    for(int i = 0 ; i < VisualTreeHelper.GetChildrenCount(obj) ; i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if(child != null && child is childItem)
            return (childItem)child;
        else
        {
            childItem childOfChild = FindVisualChild<childItem>(child);
            if(childOfChild != null)
                return childOfChild;
        }
    }
    return null;
}

M

4b9b3361

Ответ 1

Явное применение шаблона перед вызовом метода FindName предотвратит эту ошибку.

this.ApplyTemplate(); 

Ответ 2

Как указал Джон, 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);
}

В качестве альтернативы вы можете подключить обработчик к 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";
}

Ответ 3

ContentTemplate не применяется к ContentPresenter до этого события. Хотя свойство ContentTemplate задано в элементе управления в этой точке, оно не было перенесено на привязки, внутренние к ControlTemplate, например ContentPresenter ContentTemplate.

Что вы в конечном итоге пытаетесь сделать с ContentTemplate? Возможно, будет лучший общий подход для достижения конечной цели.