WPF控件模版TreeView
- 整个Demo效果图。
基于WPF图像处理Demo软件(一)WPF控件模版TreeView -
前言
最近准备接触图像处理领域的东西,想想还是从opencv开始学习,看到模板匹配的时候忍不住想动手实现之间接触的一个项目,因某些原因图像的来源是从CSharp程序里实现的,所以得转到C++里面调用opencv处理,但是到选取模板区域有点麻烦了,wpf自带的image控件并不支持选区的编辑,而且还有坐标、RGB等重要信息输出,就动手Do It!
-
感谢
首先这里参阅了好多大神的例子,包括这个主界面(论坛大神用纯C/C++实现基础的图像处理功能,简直是我等楷模!),站在别人的肩膀上果然不腰疼。。。
-
正题
框架介绍下,左边是TreeViee控件,反射的自己磁盘目录文件,如果是文件的话只区分两种,图片文件和非图片文件,本软件仅处理图像文件(bmp、jpg、png);右边为图像控件,能实现鼠标操作,缩放、拖动、实时显示位置和RGB、局部放大、选取编辑(仅以三种形状Demo)等功能。
-
TreeView控件
添加节点选中的属性,对外通知选中的文件路径(参阅)。
-
class MyTreeView : TreeView
{
public MyTreeView() : base()
{
this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(___ICH);
}
void ___ICH(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (SelectedItem != null)
{
//将SelectedItem通知到SelectedItem_Property也就是代理属性SelectedItem_,以便往外传播
SetValue(SelectedItem_Property, SelectedItem);
}
}
public object SelectedItem_
{
get { return (object)GetValue(SelectedItem_Property); }
set { SetValue(SelectedItem_Property, value); }
}
public static readonly DependencyProperty SelectedItem_Property = DependencyProperty.Register("SelectedItem_", typeof(object), typeof(MyTreeView), new UIPropertyMetadata(null));
}
在设计节点时,设计属性当前目录所有元素信息:路径、元素名称、元素图标、下一层路径元素列表
class BindDirectory
{
static readonly string[] Imageforamt = new[] { ".bmp", ".jpg", ".png" };
private List<BindDirectory> _directories;
public string Name { get; private set; }
public string Path { get; private set; }
public ImageSource Icon { get; private set; }
public BindDirectory(string directoryPath)
{
//正规化目录路径,确保Path以'\\'结尾
directoryPath = directoryPath.TrimEnd('\\');
Path = directoryPath + '\\';
int indexLastSlash = directoryPath.LastIndexOf('\\');//计算出目录名称(不包含路径)
Name = indexLastSlash >= 0 ? directoryPath.Substring(indexLastSlash + 1) : directoryPath;
Icon = new BitmapImage(new Uri("pack://application:,,,/images/Floder.png"));
}
public BindDirectory(FileInfo info)
{
Name = info.Name;
Path = info.FullName;
if (Imageforamt.Contains(info.Extension.ToLower()))
Icon = new BitmapImage(new Uri("pack://application:,,,/images/image.png"));
else
Icon = new BitmapImage(new Uri("pack://application:,,,/images/file.png"));
}
public IEnumerable<BindDirectory> Directories
{
get
{
if (_directories == null) //延迟加载
{
_directories = new List<BindDirectory>();
foreach (string d in Directory.GetDirectories(Path))
_directories.Add(new BindDirectory(d));
foreach (var f in Directory.GetFiles(Path))
_directories.Add(new BindDirectory(new FileInfo(f)));
}
return _directories;
}
}
public static bool ValidImageFile(string path)
{
return Imageforamt.Contains(new FileInfo(path).Extension.ToLower());
}
public override string ToString()
{
return Path;
}
}
这里要注意的是当选择某一目录(鼠标选中)时,需要读出该路径下层元素信息,第一层由磁盘管理器读取各个磁盘信息。
foreach (var drive in DriveInfo.GetDrives())
{
Add(new BindDirectory(drive.RootDirectory.FullName));
}
然后设计TreeViewItem的样式,每一层需要三个元素,左边图标折叠展开,右边元素图标(文件、文件夹),元素名称,网上摘录了一个第三方的Style
<Style x:Key="DefaultTreeViewItem" TargetType="{x:Type TreeViewItem}">
<Setter Property="MinHeight" Value="25" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Margin" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<StackPanel>
<Border x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"
MinHeight="{TemplateBinding MinHeight}" UseLayoutRounding="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<!--多层级间隔,暂缓-->
<!--<Grid Margin="{Binding Converter={StaticResource LengthConverter}, RelativeSource={x:Static RelativeSource.TemplatedParent}}"-->
<Grid Margin="{TemplateBinding Margin}" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="18" Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!--展开收缩按钮-->
<ToggleButton x:Name="ExpanderBtn"
IsChecked="{Binding Path=IsExpanded, RelativeSource={x:Static RelativeSource.TemplatedParent}, Mode=TwoWay}"
ClickMode="Press" >
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border>
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
</ToggleButton.Template>
<ToggleButton.Content>
<Image x:Name="ExpanderIcon"/>
<!--<TextBlock x:Name="ExpanderIcon" Foreground="{TemplateBinding Foreground}" Text="" Style="{StaticResource FIcon}" />-->
</ToggleButton.Content>
</ToggleButton>
<!--内容-->
<ContentPresenter x:Name="PART_Header" Grid.Column="1" ContentSource="Header"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</Border>
<ItemsPresenter Margin="18,0,0,0" x:Name="ItemsHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" />
<Setter TargetName="ExpanderIcon" Property="Source" Value="pack://application:,,,/images/expand-right.png" />
</Trigger>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="ExpanderIcon" Property="Source" Value="pack://application:,,,/images/expand-down.png" />
</Trigger>
<Trigger Property="HasItems" Value="False">
<Setter TargetName="ExpanderIcon" Property="Visibility" Value="Hidden" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource ItemMouseOverBackground}" />
<Setter Property="Foreground" Value="{StaticResource ItemMouseOverForeground}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{StaticResource ItemSelectedBackground}" />
<Setter Property="Foreground" Value="{StaticResource ItemSelectedForeground}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="Selector.IsSelectionActive" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource ItemSelectedBackground}" />
<Setter Property="Foreground" Value="{StaticResource ItemSelectedForeground}" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
关于模板的概念参阅
1.https://www.cnblogs.com/zhili/p/WPFTemplate.html; 2.https://www.cnblogs.com/dingli/archive/2011/07/20/2112150.html#top;
ToggleButton 控件模板决定了Content展开收缩的外观均为居中,Content为一个Image控件来显示展开收缩的图标,图标切换的逻辑放模板触发器内;第二列为元素名称,使用ContentPresenter显示当前元素内容,ItemsPresenter显示子元素内容。
小结(本文主要内容为加深对WPF模版控件的理解):
1.使用HierarchicalDataTemplate继承与DataTemplate作为TreeViewItem和MenuItem的主要数据模板,这里设计显示当前目录的名称和图标;
2.使用ObjectDataProvider实现在xaml中实例化数据源,并可接受实例的方法、属性等数据绑定,这里利用构造函数将数据源(磁盘的集合)绑定到ItemSource;
- 未完待续。。。