Хочу подробней изучить шаблоны стандартных контролов WPF.
Для этого извлекаю интересующие меня контролы из стандартной сборки .Net Framework 3.5 SP1, формируя из их имён иерархию в объекте TreeView. При клике мышки на имени класса получаю в верхней правой части окна экземпляр выбранного класса, а в нижней - XAML-код визуального дерева элемента.
Выглядит это так:

Но есть проблема:
Поскольку класс Window и его наследников нельзя использовать в качестве элемента содержимого, то при клике на этих классах (их там два, как видим из скрина) я открываю экземпляр выбранного класса в виде отдельного дочернего окна. Проблема заключается в том, что если я в TreeView кликну мышью на элементе NavigationWindow (см. скрин), то сразу после открытия экземпляра класса NavigationWindow, в дереве происходит самопроизвольнное назначение выбранным элементом тот, который находится на один уровень выше указанного мною, создавая и отображая тем самым экземпляр класса Window (поскольку именно он расположен выше), после чего тут же происходит ещё один аналогичный переход на один уровень выше и выбирается элемент ContentControl.
В результате у меня получается открыто два окна (одно - NavigationWindow и одно - Window), выбранным в TreeView оказывается элемент ContentControl, и шаблон, соответственно, отображается его же...
Не раз в отладчике прошелся построчно с F11, но так и не понял, почему происходит самопроизвольный переход. Причём это происходит только при выборе в древе элемента NavigationWindow или же при выборе Window. Все остальные работают как нужно...
Исходный код:
| Код |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Xml; using System.Windows.Markup; using System.Reflection;
namespace VisualTreeViewer { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { TextBox txtBox; Grid grid;
public Window1() { InitializeComponent(); Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; MyInitialize(); } void MyInitialize() { //Формирую список элементов TreeViewItem[] items = typeof(Control).Assembly.GetTypes() .Where(n => n.IsPublic && (n.IsSubclassOf(typeof(Control)) || n == typeof(Control))).OrderBy(n => n.Name).Select(n => new TreeViewItem {Header = n.Name, Tag = n}).ToArray();
//Реализовываю иерархию элементов IEnumerable<TreeViewItem> items2 = items.Select(n => { n.ItemsSource = items.Where(m => (m.Tag as Type) .BaseType.Name == (n.Header as string)); return n;});
ScrollViewer sv = new ScrollViewer() { HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, VerticalScrollBarVisibility = ScrollBarVisibility.Auto };
//В качестве источника данных назначаю корневой элемент иерархии классов TreeView tv = new TreeView() { ItemsSource = items2.Where(n => (n.Header as string) == "Control") };
//В результате работы кода TreeView содержит только первый уровень вложенности sv.Content = tv; Title = "Просмотр визуального дерева (состав шаблона) стандартных компонентов WPF."; grid = new Grid(); GridLength[] gridLengths = new GridLength[] { new GridLength(3, GridUnitType.Star), GridLength.Auto, new GridLength(7, GridUnitType.Star) }; for (int i = 0; i < 3; i++) { RowDefinition row = new RowDefinition() { Height = gridLengths [i]}; grid.RowDefinitions.Add(row); } for (int i = 0; i < 3; i++) { ColumnDefinition column = new ColumnDefinition(){Width=gridLengths[i]}; grid.ColumnDefinitions.Add(column); } grid.Children.Add(sv); Grid.SetRowSpan(sv, grid.RowDefinitions.Count); GridSplitter vertSplitter = new GridSplitter() { Width = 5, HorizontalAlignment = HorizontalAlignment.Center }; grid.Children.Add(vertSplitter); Grid.SetColumn(vertSplitter, 1); Grid.SetRowSpan(vertSplitter, grid.RowDefinitions.Count);
GridSplitter horSplitter = new GridSplitter() { Height = 5, HorizontalAlignment = HorizontalAlignment.Stretch}; grid.Children.Add(horSplitter); Grid.SetRow(horSplitter, 1); Grid.SetColumn(horSplitter, 2);
txtBox = new TextBox();
ScrollViewer sv2 = new ScrollViewer() { HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, VerticalScrollBarVisibility = ScrollBarVisibility.Auto }; sv2.Content = txtBox; grid.Children.Add(sv2); Grid.SetColumn(sv2, 2); Grid.SetRow(sv2, 2); txtBox.AcceptsReturn = true; Background = Brushes.Gray; tv.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(tv_SelectedItemChanged);
Content = grid; }
void tv_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { Control[] cls = grid.Children.Cast<Control>().Where(n => Grid.GetRow(n) == 0 && Grid.GetColumn(n) == 2).ToArray(); foreach (Control item in cls) { grid.Children.Remove(item); } txtBox.Clear(); Type type = (e.NewValue as TreeViewItem).Tag as Type; ConstructorInfo ci = type.GetConstructor(System.Type.EmptyTypes); Control ctrl = null; try { ctrl = (Control)ci.Invoke(null); if (ctrl is Window) { Window win = ctrl as Window; win.Width = 300; win.Height = 300; ShowWindow(win); } else { ShowXaml(ctrl); } } catch (Exception ex) { if (type.IsAbstract) { MsgShow("Класс является абстрактным."); } else if (ctrl == null) { MsgShow("Класс не содержит конструктора по умолчанию."); } else if (ctrl.Template == null) { MsgShow("Выбранный класс не имеет шаблона."); } else { MsgShow(ex.Message); } } }
private void ShowXaml(Control ctrl) { StringBuilder sb = new StringBuilder(); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.IndentChars = new string(' ', 4); settings.NewLineOnAttributes = true; XmlWriter xw = XmlWriter.Create(sb, settings); grid.Children.Add(ctrl); Grid.SetColumn(ctrl, 2); ctrl.Margin = new Thickness(10); XamlWriter.Save(ctrl.Template, xw); txtBox.Text = sb.ToString(); }
private void ShowWindowXaml(Window ctrl) { StringBuilder sb = new StringBuilder(); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.IndentChars = new string(' ', 4); settings.NewLineOnAttributes = true; XmlWriter xw = XmlWriter.Create(sb, settings); XamlWriter.Save(ctrl.Template, xw); txtBox.Text = sb.ToString(); }
private void ShowWindow(Window win) { win.Owner = this; win.WindowStartupLocation = WindowStartupLocation.CenterOwner; win.Topmost = true; win.Show(); ShowWindowXaml(win); }
private void MsgShow( string txt) { Label tblock = new Label() { Content = txt }; grid.Children.Add(tblock); Grid.SetColumn(tblock, 2); tblock.HorizontalAlignment = HorizontalAlignment.Center; tblock.VerticalAlignment = VerticalAlignment.Center; } } }
|
если немного изменить метод ShowWindow(Window win) так:
| Код |
private void ShowWindow(Window win) { win.Owner = this; win.WindowStartupLocation = WindowStartupLocation.CenterOwner; win.Topmost = true; win.Show(); ShowWindowXaml(win); win.Close();//Добавил эту строку }
|
То никакого самопроизвольного перехода не происходит и я получаю XAML-код нужного мне шаблона, однако получается, что я не вижу экземпляра, который описан этим шаблоном. Не хотелось бы мне закрывать созданный экземпляр...
Почему так происходит?
Это сообщение отредактировал(а) Compositum - 2.4.2010, 17:11