WPF 下拉菜单样式

u014752905 2014-07-05 03:50:48
最近在做一个文件管理器,右边是一个类似于Windows开始菜单——所有程序中的文件夹打开之后的样式,是一级一级的,并且呈下拉式,不是弹出式的菜单,请问:向这样的下拉菜单怎么实现?新手,所以问得有点粗糙,求各路大神指导。
...全文
805 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
u014752905 2014-07-09
  • 打赏
  • 举报
回复
谢谢各位了,结贴啦!
u014752905 2014-07-08
  • 打赏
  • 举报
回复
楼上大神,第一篇我看过,我想把每一列前面“”干掉,可惜前段时间试了一下,结果。。。我没能实现,大神,能教教吗?
至于第二篇,不知道是不是我的网络原因,一直访问不了。求指导,求帮助!!!
save4me 2014-07-08
  • 打赏
  • 举报
回复
第二篇是在WordPress上面的,因为被屏蔽掉了,所以可能你没打开。
第二篇是针对第一篇中的图标是硬编码在Value Converter里面,并且是通过Heder的文本来判断这两个问题的改进。
下面是我的翻译

Reaction to: A Simple WPF Explorer Tree

Sacha 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.asp

namespace 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,但是今天上传页面打不开,等恢复后再上传。但是关键代码上面已经贴上了。
引用 7 楼 u014752905 的回复:
楼上大神,第一篇我看过,我想把每一列前面“”干掉,可惜前段时间试了一下,结果。。。我没能实现,大神,能教教吗?
至于第二篇,不知道是不是我的网络原因,一直访问不了。求指导,求帮助!!!
save4me 2014-07-08
  • 打赏
  • 举报
回复
你把StackPanel的Image节点和DataTemplate.Triggers节点都去掉看看
引用 11 楼 u014752905 的回复:
学习了,很有帮助,感觉对这个控件有多了很多了解。 对了,前面那个小三角还是没办法弄掉吗?我尝试了一下,刚开始不会出现,但点击之后还是冒出来了。
  • 打赏
  • 举报
回复
WPF UI设计的帖子很不错。
u014752905 2014-07-08
  • 打赏
  • 举报
回复
学习了,很有帮助,感觉对这个控件有多了很多了解。 对了,前面那个小三角还是没办法弄掉吗?我尝试了一下,刚开始不会出现,但点击之后还是冒出来了。
u014752905 2014-07-07
  • 打赏
  • 举报
回复

是这种效果吗?
u014752905 2014-07-07
  • 打赏
  • 举报
回复
引用
C# 对菜单进行自定义样式 借鉴一下版主的
那个只是对颜色进行了自定义,我想做成下拉式的。那篇是Windows窗体应用程序,不是WPF。下拉菜单,有木有大神教我下。
wingtious 2014-07-05
  • 打赏
  • 举报
回复
treeview控件?
wangnaisheng 2014-07-05
  • 打赏
  • 举报
回复
C# 对菜单进行自定义样式 借鉴一下版主的、
浅醉风情 2014-07-05
  • 打赏
  • 举报
回复
在ComboBox中放MenuItem?

110,567

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧