天天看点

基于WPF图像处理Demo软件(一)WPF控件模版TreeView

WPF控件模版TreeView

  1. 整个Demo效果图。
    基于WPF图像处理Demo软件(一)WPF控件模版TreeView
  2. 前言

    最近准备接触图像处理领域的东西,想想还是从opencv开始学习,看到模板匹配的时候忍不住想动手实现之间接触的一个项目,因某些原因图像的来源是从CSharp程序里实现的,所以得转到C++里面调用opencv处理,但是到选取模板区域有点麻烦了,wpf自带的image控件并不支持选区的编辑,而且还有坐标、RGB等重要信息输出,就动手Do It!

  3. 感谢

    首先这里参阅了好多大神的例子,包括这个主界面(论坛大神用纯C/C++实现基础的图像处理功能,简直是我等楷模!),站在别人的肩膀上果然不腰疼。。。

  4. 正题

    框架介绍下,左边是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="&#xe62c;"  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图像处理Demo软件(一)WPF控件模版TreeView

小结(本文主要内容为加深对WPF模版控件的理解):

1.使用HierarchicalDataTemplate继承与DataTemplate作为TreeViewItem和MenuItem的主要数据模板,这里设计显示当前目录的名称和图标;

2.使用ObjectDataProvider实现在xaml中实例化数据源,并可接受实例的方法、属性等数据绑定,这里利用构造函数将数据源(磁盘的集合)绑定到ItemSource;

基于WPF图像处理Demo软件(一)WPF控件模版TreeView
  • 未完待续。。。

继续阅读