簡體   English   中英

如何在 TreeView 上添加復選框

[英]How to add checkboxes on TreeView

我是 C# 和 WPF 的新手,我想構建一個可以選擇文件夾和文件並獲取路徑的應用程序。 按照此鏈接中的說明,我能夠顯示文件夾結構並添加一個復選框。

在此處輸入圖像描述

但我不確定如何實現這一目標。

在此處輸入圖像描述

下面是我的代碼。

WPF

<TreeView Name="MyTreeView" Margin="249,31,20,24" Background="{x:Null}" BorderThickness="0,0,0,0" Cursor="Arrow">
                        <TreeView.Resources>
                            <Style TargetType="{x:Type TreeViewItem}">
                                <Setter Property="HeaderTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <StackPanel Orientation="Horizontal">
                                                <CheckBox Name="MyCheckBox"/>
                                                    <Image Name="img"  Width="20" Height="20" Stretch="Fill"
                                                    Source="{Binding RelativeSource={RelativeSource 
                                                    Mode=FindAncestor,
                                                    AncestorType={x:Type TreeViewItem}},
                                                    Path=Tag,
                                                    Converter={x:Static local:TagToImageConverter.Instance}}"/>          
                                                <TextBlock Text="{Binding}"/>
                                            </StackPanel>
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </TreeView.Resources>
                    </TreeView>

C#

// Get users' folders
    private void GetUsersFolder_Loaded(object sender, RoutedEventArgs e)
    {
        foreach (string s in Directory.GetDirectories("C:\\Users") )
        {
            if (!s.Contains("All Users") && !s.Contains("Default") && !s.Contains("Default User") && !s.Contains("Public"))
            {
                TreeViewItem user_folders = new TreeViewItem();
                user_folders.Header = s.Substring(s.LastIndexOf("\\") + 1);
                user_folders.Tag = new object[] { PrjRootPath+"icons\\mainpage\\folder.png", s };
                user_folders.FontWeight = FontWeights.Normal;
                user_folders.FontSize = 14;
                user_folders.Items.Add(dummyNode);
                user_folders.Expanded += new RoutedEventHandler(Userfolders_Expanded);
                MyTreeView.Items.Add(user_folders);
            }
        }
    }

    // Get folders inside user's folder
    void Userfolders_Expanded(object sender, RoutedEventArgs e)
    {
        TreeViewItem item = (TreeViewItem)sender;
        if (item.Items.Count == 1 && item.Items[0] == dummyNode)
        {
            item.Items.Clear();
            try
            {
                foreach (string s in Directory.GetDirectories(((Object[])item.Tag)[1].ToString()))
                {
                    if (s.Contains("Desktop") ||
                        s.Contains("Documents") ||
                        s.Contains("Downloads") ||
                        s.Contains("Pictures") ||
                        s.Contains("Contacts") ||
                        s.Contains("Videos"))
                    {
                        TreeViewItem subitem = new TreeViewItem();
                        subitem.Header = s.Substring(s.LastIndexOf("\\") + 1);
                        subitem.Tag = new object[] { PrjRootPath + "icons\\mainpage\\folder.png", s };
                        subitem.FontWeight = FontWeights.Normal;
                        subitem.Items.Add(dummyNode);
                        subitem.Expanded += new RoutedEventHandler(InsideUserfoldersFiles_Expanded);
                        item.Items.Add(subitem);
                    }
                }
            }
            catch (Exception e1) { MessageBox.Show(e1.Message + " " + e1.InnerException);  }
        }
    }

    // Get folders and files inside the folders of user's folder
    void InsideUserfoldersFiles_Expanded(object sender, RoutedEventArgs e)
    {
        TreeViewItem item = (TreeViewItem)sender;
        if (item.Items.Count == 1 && item.Items[0] == dummyNode)
        {
            item.Items.Clear();
            try
            {
                foreach (string s in Directory.GetDirectories(((Object[])item.Tag)[1].ToString()))
                {
                        TreeViewItem subitem = new TreeViewItem();
                        subitem.Header = s.Substring(s.LastIndexOf("\\") + 1);
                        subitem.Tag = new object[] { PrjRootPath + "icons\\mainpage\\folder.png", s };
                        subitem.FontWeight = FontWeights.Normal;
                        subitem.Items.Add(dummyNode);
                        subitem.Expanded += new RoutedEventHandler(InsideUserfoldersFiles_Expanded);
                        item.Items.Add(subitem);
                }

                foreach (string s in Directory.GetFiles(((Object[])item.Tag)[1].ToString()))
                {
                    string f_extention = s.Substring(s.LastIndexOf(".") + 1);
                    
                    if (f_extention != "ini" && f_extention != "lnk")
                    {
                        String f_type = PrjRootPath + "icons\\mainpage\\file.png";
                        if (s.Contains(".csv") ||
                            s.Contains(".xlsx") ||
                            s.Contains(".xlsm") ||
                            s.Contains(".xls"))
                        {
                            f_type = PrjRootPath + "icons\\mainpage\\excel.png";
                        }
                        else if (s.Contains(".docx") ||
                            s.Contains(".doc") ||
                            s.Contains(".docm") ||
                            s.Contains(".dotm"))
                        {
                            f_type = PrjRootPath + "icons\\mainpage\\word.png";
                        }
                        else if (s.Contains(".pptx") ||
                            s.Contains(".pptm") ||
                            s.Contains(".ppt") ||
                            s.Contains(".potm"))
                        {
                            f_type = PrjRootPath + "icons\\mainpage\\powerpoint.png";
                        }
                        else if (s.Contains(".msg"))
                        {
                            f_type = PrjRootPath + "icons\\mainpage\\outlook.png";
                        }
                        else if (s.Contains(".pdf"))
                        {
                            f_type = PrjRootPath + "icons\\mainpage\\pdf.png";
                        }
                        else if (s.Contains(".png"))
                        {
                            f_type = PrjRootPath + "icons\\mainpage\\image.png";
                        }
                        else
                        {
                            f_type = PrjRootPath + "icons\\mainpage\\file.png";
                        }

                        TreeViewItem subitem = new TreeViewItem();
                        subitem.Header = s.Substring(s.LastIndexOf("\\") + 1);
                        subitem.Tag = new object[] { f_type, s };
                        subitem.FontWeight = FontWeights.Normal;
                        item.Items.Add(subitem);
                    } 
                }
            }
            catch (Exception e2) { MessageBox.Show(e2.Message + " " + e2.InnerException); }
        }
    }

你們可以給我一些例子嗎? 太感謝了!

首先,您當前沒有按照設計使用的方式使用 WPF。 具體來說,您正在手動創建樹元素,而不是使用數據綁定。 現在這又回來咬你了,如果你繼續沿着你走的路走下去,情況只會變得更糟。

無論哪種方式,您需要在這里做的是將復選框控件模板化,以便您可以更改刻度圖形。 如果您在 XAML 代碼中的任何位置添加 CheckBox,請將光標放在它上面,然后在右側的“屬性”面板中選擇 Miscellaneous -> Template -> Convert to New Resource,您將獲得一個完全展開的 CheckBox 模板(例如“CheckBoxTemplate1”),然后您可以將其分配給 TreeView 數據模板中的 CheckBox:

<StackPanel Orientation="Horizontal">
    <CheckBox Name="MyCheckBox" Template="{DynamicResource CheckBoxTemplate1}"/>

查看模板本身,您會看到負責其圖形外觀的所有位,包括:

<Grid x:Name="markGrid">
    <Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph}" Margin="1" Opacity="0" Stretch="None"/>
    <Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph}" Margin="2" Opacity="0"/>

“optionMark”元素負責勾選,因此您可以通過簡單地更改路徑標記將其變成矩形:

<Path x:Name="optionMark" Data="M 0,0L 10,0L 10,10L 0,10L 0,0 Z " Fill="{StaticResource OptionMark.Static.Glyph}" Margin="1" Opacity="0" Stretch="None"/>

結果:

在此處輸入圖像描述

現在看來您只希望將其應用於文件夾,而不是文件,這就是它變得棘手的地方。 您的模板代碼需要一種方法來區分您傳遞給它的兩種類型的數據(即文件夾與文件)。 添加它的唯一地方是作為 subitem.Tag 數組中的第三個元素......現在已經變得非常混亂......並使用 DataTemplate 選擇要渲染的路徑,即如下所示:

<Path x:Name="optionMark" Fill="{StaticResource OptionMark.Static.Glyph}" Margin="1" Opacity="0" Stretch="None">
    <Path.Style>
        <Style TargetType="Path">
            <Setter Property="Data" Value="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z" />
            <Style.Triggers>
                <DataTrigger Binding="{RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=Tag[2]}" Value="True">
                    <Setter Property="Data" Value="M 0,0L 10,0L 10,10L 0,10L 0,0 Z" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Path.Style>
</Path>

順便說一句,不要將 DataTrigger 綁定視為逐字記錄,我還沒有對其進行測試,但是您明白了……您必須在標簽中使用一個元素來告訴 Path 要使用哪些數據。

還有其他方法可以做到這一點,例如,當您創建 TreeViewItem 時,您可以在那里為其分配新的 ControlTemplate,您必須從資源塊中加載它。 但老實說,這只會變得更糟。 您最好的選擇,IMO,是從模板中取出 DataTrigger 並按照它的預期使用方式使用 TreeView,即為您嘗試繪制的每種類型的對象創建數據結構:

public FolderViewModel[] Items { get; } = {
    new FolderViewModel(), new FolderViewModel(), new FolderViewModel(),
};

public class FolderViewModel : FileViewModel
{
    public FileViewModel[] Children{ get; } = {
        new FileViewModel(),
        new FileViewModel(),
        new FileViewModel()
    };

    public FolderViewModel() : base("Folder") { }
}

public class FileViewModel
{
    public string Text { get; set; }

    public FileViewModel(string text = "File") => this.Text = text;
}

然后將 Items 分配(或綁定)到您的頂級節點,並根據對象的類型設置 DataTemplate 和 HierarchicalDataTemplate:

<TreeView Name="MyTreeView" Margin="249,31,20,24" Background="{x:Null}" BorderThickness="0,0,0,0" Cursor="Arrow" ItemsSource="{Binding Items}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:FolderViewModel}" ItemsSource="{Binding Children}">
            <StackPanel Orientation="Horizontal">
                <CheckBox Name="MyCheckBox" Template="{DynamicResource CheckBoxTemplate1}"></CheckBox>
                <TextBlock Text="{Binding Text}"/>
            </StackPanel>
        </HierarchicalDataTemplate>
        <DataTemplate DataType="{x:Type local:FileViewModel}">
            <StackPanel Orientation="Horizontal">
                <CheckBox Name="MyCheckBox"></CheckBox>
                <TextBlock Text="{Binding Text}"/>
            </StackPanel>
        </DataTemplate>
    </TreeView.Resources>
</TreeView>

就是這樣。 文件將使用帶有勾選的默認復選框模板,而文件夾將被分配新模板並獲得一個框。

結果:

在此處輸入圖像描述

xaml 中的以下代碼可以提供幫助

<Window.Resources>
    <ResourceDictionary>
        <HierarchicalDataTemplate x:Key="CheckBoxItemTemplate" ItemsSource="{Binding Children, Mode=OneTime}">
            <StackPanel Orientation="Horizontal">
                <CheckBox Focusable="False" IsChecked="{Binding IsChecked}" VerticalAlignment="Center" />
                <ContentPresenter Content="{Binding Name, Mode=OneTime}" Margin="2,0" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </ResourceDictionary>
</Window.Resources>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM