WPF 下拉菜单样式 wpfC# 下拉菜单 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 那个只是对颜色进行了自定义,我想做成下拉式的。那篇是Windows窗体应用程序,不是WPF。下拉菜单,有木有大神教我下。 参考:A Simple WPF Explorer TreeReaction to: A Simple WPF Explorer Tree 第二篇是在WordPress上面的,因为被屏蔽掉了,所以可能你没打开。第二篇是针对第一篇中的图标是硬编码在Value Converter里面,并且是通过Heder的文本来判断这两个问题的改进。下面是我的翻译Reaction to: A Simple WPF Explorer TreeSacha Barber最近在CodeProject发表了一篇文章,标题为"A Simple WPF Explorer Tree"。这篇文章展示了怎么延迟加载目录结构的TreeView控件到电脑上。这是一篇非常好的WPF TreeView延迟加载的介绍文章,我强烈推荐阅读原文。在读完Sacha的这篇好文章后,我总感觉有点不对劲。在他的Demo中,树的根节点有个驱动器的图标,所有的其他节点有文件夹的图标。他对于使用哪种图标的实现逻辑是通过一个值转换器。一个TreeViewItem用来显示图标的图形元素的Source属性绑定到了TreeViewItem的Header上。这个绑定有一个转换器,转换器检查Header的文本里面是否有一个反斜杠(\),如果找到的话,那说明这是一个根节点 (比如它表示一个驱动器,而不是一个目录)。例如值转换器接收到的Header文本是"C:\",它就返回驱动器图标。值转换器包含了图标资源的名字,它通过那些硬编码的资源标识符去加载一个BitmapSource。这就是我觉得有问题的地方。我认为应该避免在值转换器和模板选择器中使用硬编码的资源标识符,因为这严重限制了这些累的重用性。至少有两种其他的方法可以用来实现图标选择功能而不需要在值选择器里面固定死资源的键值。第一种是允许你通过值选择器的属性来设置图标文件名 (资源标识符)。这样你就能指定XAML使用哪个图标,而值转换器只需要知道怎么解析它接收到的数据。我并不是很喜欢那个方法。事实上我想在这种情况使用值转换器并不是最好的方法。转换器的逻辑依赖于Header文本的某个字符虽然可以,但是我感觉和生硬。我认为TreeView结构和它的元素应该提供我们需要的所有信息,而不是元素数据里面的某个内容。我的解决方案为根节点提供一个属性,然后使用DataTriggers绑定这个属性并决定使用哪个图标。下面是代码。首先我们要有一个包含绑定属性的类,注意默认值是false:using System.Windows;namespace WpfExplorerTreeNoConverter{ public static class TreeViewItemProps { public static bool GetIsRootLevel(DependencyObject obj) { return (bool)obj.GetValue(IsRootLevelProperty); } public static void SetIsRootLevel( DependencyObject obj, bool value) { obj.SetValue(IsRootLevelProperty, value); } public static readonly DependencyProperty IsRootLevelProperty = DependencyProperty.RegisterAttached( "IsRootLevel", typeof(bool), typeof(TreeViewItemProps), new UIPropertyMetadata(false)); }}(很抱歉这里的缩进有点奇怪,因为我的图片只能这么宽…)当我们初始化TreeViewItems的时候,它们代表电脑上的驱动器,我们把每个元素绑定的IsRootLevel属性设置为true:using System;using System.Collections.Generic;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.Shapes;using System.IO;// NOTE: The original concept of this demo was created by Sacha Barber, in// this article: http://www.codeproject.com/useritems/WPF_Explorer_Tree.aspnamespace WpfExplorerTreeNoConverter{ /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : System.Windows.Window { private readonly object _dummyNode = null; public Window1() { InitializeComponent(); this.Loaded += new RoutedEventHandler(Window1_Loaded); } void Window1_Loaded(object sender, RoutedEventArgs e) { foreach (string drive in Directory.GetLogicalDrives()) { TreeViewItem item = new TreeViewItem(); item.Header = drive; item.Tag = drive; item.Items.Add(_dummyNode); item.Expanded += folder_Expanded; // Apply the attached property so that // the triggers know that this is root item. TreeViewItemProps.SetIsRootLevel(item, true); foldersTree.Items.Add(item); } } void folder_Expanded(object sender, RoutedEventArgs e) { TreeViewItem item = (TreeViewItem)sender; if (item.Items.Count == 1 && item.Items[0] == _dummyNode) { item.Items.Clear(); try { foreach (string dir in Directory.GetDirectories(item.Tag as string)) { TreeViewItem subitem = new TreeViewItem(); subitem.Header = new DirectoryInfo(dir).Name; subitem.Tag = dir; subitem.Items.Add(_dummyNode); subitem.Expanded += folder_Expanded; item.Items.Add(subitem); } } catch (Exception) { } } } }}最后,我们的模板指明每一个TreeViewItem的Header如何呈现。注意由于Header是通过ContentPresenter来展现的,我们需要绑定DataTriggers到一个可以找到相应的TreeViewItem相对资源:<Window x:Class="WpfExplorerTreeNoConverter.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfExplorerTreeNoConverter" Title="WpfExplorerTreeNoConverter" Height="300" Width="300" > <Grid> <TreeView x:Name="foldersTree"> <TreeView.Resources> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="HeaderTemplate"> <Setter.Value> <DataTemplate DataType="ContentPresenter"> <StackPanel Orientation="Horizontal"> <Image Name="img" Width="20" Height="20" Stretch="Fill" /> <TextBlock Text="{Binding}" Margin="5,0" /> </StackPanel> <DataTemplate.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}, Path=(local:TreeViewItemProps.IsRootLevel)}" Value="True" > <Setter TargetName="img" Property="Source" Value="Images/diskdrive.png" /> </DataTrigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}, Path=(local:TreeViewItemProps.IsRootLevel)}" Value="False" > <Setter TargetName="img" Property="Source" Value="Images/folder.png" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </Setter.Value> </Setter> </Style> </TreeView.Resources> </TreeView> </Grid></Window>在这里下载演示项目: ExplorerTree (demo project) 请把文件后缀从.DOC改成.ZIP然后解压。译者按: 因为WordPress被屏蔽,所以想把资源上传到了CSDN,但是今天上传页面打不开,等恢复后再上传。但是关键代码上面已经贴上了。 WPF UI设计的帖子很不错。 你把StackPanel的Image节点和DataTemplate.Triggers节点都去掉看看 在winform中写入和读取 到 text中 SqlParameter 有什么用处/? 楼主头像,和回帖人等级问题 问一个C#语法问题 TextBox的数据绑定问题 回文素数算法 c#编程环境的解决方案资源管理器的"引用"是指什么?与"引用类型"的"引用"有什么不同? 怎么用SaveFileDialog 控件先保存一空excel文件,再导入内容 如何获最新的 SQL Notification Server Service Pack 求教一个关于C#编写的CardGame 导出EXCEL表格时 如何优化代码适应大数据量 WPF自定义窗口不能显示com控件
A Simple WPF Explorer Tree
Reaction to: A Simple WPF Explorer Tree
第二篇是针对第一篇中的图标是硬编码在Value Converter里面,并且是通过Heder的文本来判断这两个问题的改进。
下面是我的翻译Reaction to: A Simple WPF Explorer TreeSacha Barber最近在CodeProject发表了一篇文章,标题为"A Simple WPF Explorer Tree"。这篇文章展示了怎么延迟加载目录结构的TreeView控件到电脑上。这是一篇非常好的WPF TreeView延迟加载的介绍文章,我强烈推荐阅读原文。
在读完Sacha的这篇好文章后,我总感觉有点不对劲。在他的Demo中,树的根节点有个驱动器的图标,所有的其他节点有文件夹的图标。他对于使用哪种图标的实现逻辑是通过一个值转换器。一个TreeViewItem用来显示图标的图形元素的Source属性绑定到了TreeViewItem的Header上。这个绑定有一个转换器,转换器检查Header的文本里面是否有一个反斜杠(\),如果找到的话,那说明这是一个根节点 (比如它表示一个驱动器,而不是一个目录)。例如值转换器接收到的Header文本是"C:\",它就返回驱动器图标。
值转换器包含了图标资源的名字,它通过那些硬编码的资源标识符去加载一个BitmapSource。这就是我觉得有问题的地方。我认为应该避免在值转换器和模板选择器中使用硬编码的资源标识符,因为这严重限制了这些累的重用性。
至少有两种其他的方法可以用来实现图标选择功能而不需要在值选择器里面固定死资源的键值。第一种是允许你通过值选择器的属性来设置图标文件名 (资源标识符)。这样你就能指定XAML使用哪个图标,而值转换器只需要知道怎么解析它接收到的数据。
我并不是很喜欢那个方法。事实上我想在这种情况使用值转换器并不是最好的方法。转换器的逻辑依赖于Header文本的某个字符虽然可以,但是我感觉和生硬。我认为TreeView结构和它的元素应该提供我们需要的所有信息,而不是元素数据里面的某个内容。
我的解决方案为根节点提供一个属性,然后使用DataTriggers绑定这个属性并决定使用哪个图标。下面是代码。首先我们要有一个包含绑定属性的类,注意默认值是false:using System.Windows;namespace WpfExplorerTreeNoConverter
{ public static class TreeViewItemProps
{
public static bool GetIsRootLevel(DependencyObject obj)
{
return (bool)obj.GetValue(IsRootLevelProperty);
} public static void SetIsRootLevel(
DependencyObject obj, bool value)
{
obj.SetValue(IsRootLevelProperty, value);
}
public static readonly DependencyProperty IsRootLevelProperty =
DependencyProperty.RegisterAttached(
"IsRootLevel",
typeof(bool),
typeof(TreeViewItemProps),
new UIPropertyMetadata(false));
}}(很抱歉这里的缩进有点奇怪,因为我的图片只能这么宽…)
当我们初始化TreeViewItems的时候,它们代表电脑上的驱动器,我们把每个元素绑定的IsRootLevel属性设置为true:using System;
using System.Collections.Generic;
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.Shapes;
using System.IO;// NOTE: The original concept of this demo was created by Sacha Barber, in
// this article: http://www.codeproject.com/useritems/WPF_Explorer_Tree.aspnamespace WpfExplorerTreeNoConverter
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary> public partial class Window1 : System.Windows.Window
{
private readonly object _dummyNode = null; public Window1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
} void Window1_Loaded(object sender, RoutedEventArgs e)
{
foreach (string drive in Directory.GetLogicalDrives())
{
TreeViewItem item = new TreeViewItem();
item.Header = drive;
item.Tag = drive;
item.Items.Add(_dummyNode);
item.Expanded += folder_Expanded; // Apply the attached property so that
// the triggers know that this is root item.
TreeViewItemProps.SetIsRootLevel(item, true); foldersTree.Items.Add(item);
}
} void folder_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = (TreeViewItem)sender;
if (item.Items.Count == 1 && item.Items[0] == _dummyNode)
{
item.Items.Clear();
try
{
foreach (string dir in Directory.GetDirectories(item.Tag as string))
{
TreeViewItem subitem = new TreeViewItem();
subitem.Header = new DirectoryInfo(dir).Name;
subitem.Tag = dir;
subitem.Items.Add(_dummyNode);
subitem.Expanded += folder_Expanded;
item.Items.Add(subitem);
}
}
catch (Exception) { }
}
}
}
}最后,我们的模板指明每一个TreeViewItem的Header如何呈现。注意由于Header是通过ContentPresenter来展现的,我们需要绑定DataTriggers到一个可以找到相应的TreeViewItem相对资源:<Window x:Class="WpfExplorerTreeNoConverter.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfExplorerTreeNoConverter"
Title="WpfExplorerTreeNoConverter" Height="300" Width="300"
>
<Grid>
<TreeView x:Name="foldersTree">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate DataType="ContentPresenter">
<StackPanel Orientation="Horizontal">
<Image
Name="img"
Width="20" Height="20"
Stretch="Fill"
/>
<TextBlock Text="{Binding}" Margin="5,0" />
</StackPanel> <DataTemplate.Triggers>
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type TreeViewItem}},
Path=(local:TreeViewItemProps.IsRootLevel)}"
Value="True"
>
<Setter
TargetName="img"
Property="Source"
Value="Images/diskdrive.png"
/>
</DataTrigger> <DataTrigger Binding="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type TreeViewItem}},
Path=(local:TreeViewItemProps.IsRootLevel)}"
Value="False"
>
<Setter
TargetName="img"
Property="Source"
Value="Images/folder.png"
/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>在这里下载演示项目: ExplorerTree (demo project) 请把文件后缀从.DOC改成.ZIP然后解压。
译者按: 因为WordPress被屏蔽,所以想把资源上传到了CSDN,但是今天上传页面打不开,等恢复后再上传。但是关键代码上面已经贴上了。