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;
- 未完待續。。。