Chuyển đến nội dung chính

WPF: Visual Tree và TreeView

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.
anhso.net

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
{
    /// 
    /// Publically expose BringIndexIntoView.
    /// 
    public void BringIntoView(int index)
    {

        this.BringIndexIntoView(index);
    }
}
Download ví dụ: Visual Tree, TreeView

Nhận xét

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.