Эти непростые XAML Namescopes
- modified:
- reading: 3 minutes
На прошлой неделе я, просто ради интереса, подготовил 4 примера в Еще одно сравнение паттернов MVVM и MVP для Silverlight. Примеры возникли не случайно, просто, попался на эту проблему пару недель назад. Там же был опросник о том, какие из примеров рабочие. Определить просил просто, посмотрев на код. Было получено 56 (вместе с моим) ответов, и только два из них были верными (вместе с моим). В этих примерах я уточнил, что опрос касается Silverlight, так как в WPF все немного по-другому.
Результаты ответов вы можете посмотреть на графике:
Честно говоря, я ожидал совершенно другие ответы, и для меня было не понятно, почему многие считали, что примеры, выполненные с паттерном MVP, не рабочие. Я вот надеялся, что сейчас MVP выйдет в лидеры, и я спокойно скажу – ага, видите, как легко определить в MVP что работает, а что нет. А в MVVM сложнее. Что ж мой эксперимент не удался. И не понятно, почему вышел вперед первый пример с MVVM.
Правильный ответ такой: только первый пример, сделанный на MVVM, не работает (в WPF оба MVVM примера не работают). Почему так? Нужно, наверное, обратиться к статье о XAML Namescopes. Честно говоря, точного ответа я не получил там, но, думаю, что можно начинать раскопки от предложения:
Generally, each name specified within the XAML is added to the default XAML namescope, which is associated with the root element in the XAML markup provided.
И все дело в том, что ContextMenu имеет свой Root Element с типом Popup (это скриншот из Silverlight Spy):
Но! Почему тогда работает второй пример, сделанный на MVVM? Думаю, что тут у нас работает специальное отношение к контролам, наследуемым от ItemsControl.
Самое интересное начинается, когда захочется назначить имя для элемента ContextMenu. В первом примере назвать его ParentElement нельзя, так как все лежит в одном XAML файле, а так как компилятор генерирует поля для элементов, у которых есть имена, то там сгенерируется два поля с одинаковым именем. Примерно, так:
public partial class MainPage : System.Windows.Controls.UserControl {
internal System.Windows.Controls.UserControl ParentControl;
internal System.Windows.Controls.Grid LayoutRoot;
internal System.Windows.Controls.ContextMenu ParentControl;
private bool _contentLoaded;
/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()] public void InitializeComponent() {
if (_contentLoaded) {
return;
} _contentLoaded = true;
System.Windows.Application.LoadComponent(this, new System.Uri("/SilverlightApplication3;component/MainPage.xaml", System.UriKind.Relative));
this.ParentControl = ((System.Windows.Controls.UserControl)(this.FindName("ParentControl")));
this.LayoutRoot = ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
this.ParentControl = ((System.Windows.Controls.ContextMenu)(this.FindName("ParentControl")));
}
}
А во втором случае мы можем это сделать без проблем, так как в случае ItemsControl, каждый элемент из коллекции будет иметь свой собственный XAML Namescope, и здесь мы просто перекроем именем ParentElement root элемент из XAML.
В общем-то, доказывать тут нечего. Реализация в моих примерах сильно хромает, и мои непростые ссылки на DataContext главного элемента можно быстро поменять на более правильное и красивое в реализации (по этому, наверное, большинство выбрало 5-й вариант в опроснике). Но факт в том, что уж очень не просто разобраться в байндингах этого Silverlight, и не просто так разработчики просят дебаггер для Silverlight байндинга (который уже есть в 5-й бета версии). И это не первая непонятная ситуация, которая возникает в байндинге Silverlight для меня. В WPF, честно говоря, вопросов намного меньше возникало.
See Also
- Еще одно сравнение паттернов MVVM и MVP для Silverlight
- Впечатления о втором дне MIX’11: Silverlight 5 Beta
- Как часто вы думаете о том, что пользователь может нажать Double-Click, где это не предусмотрено?
- Никогда не делайте классы и пространства имен с одним и тем же именем
- Application Developers Days 2010–как это было…