Зачем 'this.ContentTemplate.FindName' генерировать исключение InvalidOperationException для своего собственного шаблона? - PullRequest
13 голосов
/ 15 апреля 2011

Хорошо ... это меня озадачило.Я переопределил OnContentTemplateChanged в своем UserControl.Я проверяю, что значение, переданное для newContentTemplate, на самом деле равно this.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

Ответы [ 3 ]

10 голосов
/ 18 марта 2013

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

this.ApplyTemplate(); 
4 голосов
/ 19 апреля 2011

Как указал Джон, 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";
}
0 голосов
/ 15 апреля 2011

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

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

...