Почему "this.ContentTemplate.FindName" создает исключение InvalidOperationException для своего собственного шаблона?
Хорошо... это поставило меня в тупик. Я переопределил 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 ответа
Явное применение шаблона перед вызовом FindName
Метод предотвратит эту ошибку.
this.ApplyTemplate();
Как указал Джон, 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";
}
ContentTemplate не применяется к ContentPresenter до окончания этого события. Хотя свойство ContentTemplate установлено в элементе управления в этот момент, оно не было перенесено на привязки, внутренние для ControlTemplate, как ContentTemplate ContentPresenter.
Что вы в конечном итоге пытаетесь сделать с помощью ContentTemplate? Там может быть лучший общий подход для достижения вашей конечной цели.