Khi bạn tìm hiểu về thiết kế giao diện bên WPF, có 2 thành phần quan trọng mà bạn cần chú ý, đó là Logical Tree và Visual Tree. Hôm nay mình bàn về Visual Tree
Gần như các thành phần được hiển thị trên window được gọi là visual tree. Bạn có thể nghĩ rằng visual tree được mở rộng từ logical tree, trong đó, ngoài các thành phần chính trong logical tree, nó còn có thành phần khác như border, scrollview... Dưới đây là hình ví dụ về Visual Tree, trong đó các node được tô đậm là Logical Tree.
Ví dụ với TreeView, khi mới tạo, nó ở dạng Collapse, và bạn chỉ in được phần tử đầu tiên
Ví dụ: Để trả về ListBoxItem trong ListBox,bạn có thể gọi ListBox.ItemContainerGenerator.GetContainerFromItem. Nhưng nếu bạn áp dụng cho TreeViewItem, bạn chỉ nhận được các phần tử con trực tiếp. Vì vậy bạn cần phải đệ quy theo chiều sâu. Nếu các phần tử trong TreeView chưa được tạo (như lý do nêu trên), bạn cần phải thiết lập VirtualizingStackPanel.IsVirtualizing = true.
Tham khảo: http://msdn.microsoft.com/en-us/library/ff407130.aspx
Gần như các thành phần được hiển thị trên window được gọi là visual tree. Bạn có thể nghĩ rằng visual tree được mở rộng từ logical tree, trong đó, ngoài các thành phần chính trong logical tree, nó còn có thành phần khác như border, scrollview... Dưới đây là hình ví dụ về Visual Tree, trong đó các node được tô đậm là Logical Tree.
Duyệt cây Visual Tree
private void PrintVisualTree(int depth, DependencyObject obj) { Debug.WriteLine(new string(' ', depth) + obj); //txtKq.Text = txtKq.Text + "\n" + new string(' ', depth) + obj; for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { PrintVisualTree(depth + 1, VisualTreeHelper.GetChild(obj, i)); } }
Hạn chế của Visual Tree-Khắc phục
Visual Tree chỉ lấy ra được những object đã được wpf tạo ra sẵn (vì việc tạo hết các control sẽ tốn rất nhiều thời gian.Ví dụ với TreeView, khi mới tạo, nó ở dạng Collapse, và bạn chỉ in được phần tử đầu tiên
<TreeView Margin="0,0,466,0" Name="TreeView1" HorizontalAlignment="Right" VerticalAlignment="Top" Width="169" Height="200"> <TreeViewItem Header="Cold Drinks"> <TreeViewItem Header="Coke"></TreeViewItem> <TreeViewItem Header="Pepsi"></TreeViewItem> <TreeViewItem Header="Orange Juice"></TreeViewItem> <TreeViewItem Header="Milk"></TreeViewItem> <TreeViewItem Header="Iced Tea"></TreeViewItem> <TreeViewItem Header="Mango Shake"></TreeViewItem> </TreeViewItem> </TreeView>
private void PrintValuesFromVisualTree(int depth, DependencyObject obj) { var item = obj as TreeViewItem; if (item != null) { lblKetqua.Content = lblKetqua.Content + new string(' ', depth) + item.Header.ToString() + "\n"; } for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { PrintValuesFromVisualTree(depth + 1, VisualTreeHelper.GetChild(obj, i)); } }Các phần tử trong TreeView chưa được khởi tạo và VisualTreeHelper không thể tìm thấy được các children control của nó. Để tìm kiếm TreeViewItem trong TreeView, bạn phải tìm kiếm Item Containers trong ItemControl (vd như ListBoxItem trong ListBox) bởi vì các phần tử trong TreeViewItems lồng nhau.
Ví dụ: Để trả về ListBoxItem trong ListBox,bạn có thể gọi ListBox.ItemContainerGenerator.GetContainerFromItem. Nhưng nếu bạn áp dụng cho TreeViewItem, bạn chỉ nhận được các phần tử con trực tiếp. Vì vậy bạn cần phải đệ quy theo chiều sâu. Nếu các phần tử trong TreeView chưa được tạo (như lý do nêu trên), bạn cần phải thiết lập VirtualizingStackPanel.IsVirtualizing = true.
Tham khảo: http://msdn.microsoft.com/en-us/library/ff407130.aspx
/// <summary> /// Recursively search for an item in this subtree. /// </summary> /// <param name="container"> /// The parent ItemsControl. This can be a TreeView or a TreeViewItem. /// </param> /// <param name="item"> /// The item to search for. /// </param> /// <returns> /// The TreeViewItem that contains the specified item. /// </returns> private TreeViewItem GetTreeViewItem(ItemsControl container, object item) { if (container != null) { if (container.DataContext == item) { return container as TreeViewItem; } // Expand the current container if (container is TreeViewItem && !((TreeViewItem)container).IsExpanded) { container.SetValue(TreeViewItem.IsExpandedProperty, true); } // Try to generate the ItemsPresenter and the ItemsPanel. // by calling ApplyTemplate. Note that in the // virtualizing case even if the item is marked // expanded we still need to do this step in order to // regenerate the visuals because they may have been virtualized away. container.ApplyTemplate(); ItemsPresenter itemsPresenter = (ItemsPresenter)container.Template.FindName("ItemsHost", container); if (itemsPresenter != null) { itemsPresenter.ApplyTemplate(); } else { // The Tree template has not named the ItemsPresenter, // so walk the descendents and find the child. itemsPresenter = FindVisualChild<ItemsPresenter>(container); if (itemsPresenter == null) { container.UpdateLayout(); itemsPresenter = FindVisualChild<ItemsPresenter>(container); } } Panel itemsHostPanel = (Panel)VisualTreeHelper.GetChild(itemsPresenter, 0); // Ensure that the generator for this panel has been created. UIElementCollection children = itemsHostPanel.Children; MyVirtualizingStackPanel virtualizingPanel = itemsHostPanel as MyVirtualizingStackPanel; for (int i = 0, count = container.Items.Count; i < count; i++) { TreeViewItem subContainer; if (virtualizingPanel != null) { // Bring the item into view so // that the container will be generated. virtualizingPanel.BringIntoView(i); subContainer = (TreeViewItem)container.ItemContainerGenerator. ContainerFromIndex(i); } else { subContainer = (TreeViewItem)container.ItemContainerGenerator. ContainerFromIndex(i); // Bring the item into view to maintain the // same behavior as with a virtualizing panel. subContainer.BringIntoView(); } if (subContainer != null) { // Search the next level for the object. TreeViewItem resultContainer = GetTreeViewItem(subContainer, item); if (resultContainer != null) { return resultContainer; } else { // The object is not under this TreeViewItem // so collapse it. subContainer.IsExpanded = false; } } } } return null; } /// <summary> /// Search for an element of a certain type in the visual tree. /// </summary> /// <typeparam name="T">The type of element to find.</typeparam> /// <param name="visual">The parent element.</param> /// <returns></returns> private T FindVisualChild<T>(Visual visual) where T : Visual { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++) { Visual child = (Visual)VisualTreeHelper.GetChild(visual, i); if (child != null) { T correctlyTyped = child as T; if (correctlyTyped != null) { return correctlyTyped; } T descendent = FindVisualChild<T>(child); if (descendent != null) { return descendent; } } } return null; }
public class MyVirtualizingStackPanel : VirtualizingStackPanel { ///Download ví dụ: Visual Tree, TreeView/// Publically expose BringIndexIntoView. /// public void BringIntoView(int index) { this.BringIndexIntoView(index); } }
Nhận xét
Đăng nhận xét