c#WPF实现Windows资源管理器(附源码)
⽬录
第⼀部分:FileList
  第⼆部分:Navigation
  第三部分:BreadCrumbView
  今天我来写⼀篇关于利⽤WPF来实现Windows的资源管理器功能,当然只是局部实现这个功能,因为在很多时候我们需要来实现对本机资源的管理,当然我们可以使⽤OpenFileDialog dialog =
new OpenFileDialog()这个Microsoft.Win32命名空间下的这个类来实现⼀些资源查和导⼊的功能,但是在很多时候我们可能需要更多的功能,并且希望能够集成到我们⾃⼰的项⽬中,但是我们这个时
候就不得不⾃⼰来写⼀套来集成到我们的软件中去了,因为OpenFileDialog这个是⽆法作为⼀个UserControl加⼊到我们的项⽬中的,当然我们只是实现了其中的⼀部分功能,因为Windows的资源管理器
也是⼀个重量级的应⽤,也是⼗分庞⼤和复杂的,这⾥只是通过这个Demo来加深对WPF的MVVM模式及软件基本功的巩固。
  在正式介绍整体框架之前,⾸先来看看整体的结构,从⽽对其有⼀个⼤概的了解。
  整个界⾯从⼤的⽅⾯来说主要包括三个⽅⾯:1 ⽂件及⽂件夹显⽰区域、2 导航区域、3 路径显⽰区域,其实在整个界⾯中,2和3都是围绕1来进⾏操作的,三个区域之间的耦合性其实是⾮常⾼的,
所以常规的做法就是三个部分分为三个UserControl,并且同时绑定到⼀个ViewModel中,这样整个层次也就⽐较清晰了,缺点是⼀个ViewModel中代码太多,职责⾮常⼤,所以在这个DEMO中尝试将三
个部分分开,三个ViewModel来操作三个View⾥⾯的内容,整个实现下来其实也有⼀些不⾜之处,那就是容易将问题复杂化,很多在⼀个类中就能够完成的⼯作,最终要通过各种类与类之间的耦合来完
成,所以通过这个DEMO希望⾃⼰能够多⼀些思考,从⽽在软件的设计中能够再多⼀些经验,能够把握好软件粒度的问题,下⾯就软件的具体内容来深⼊分析⼀下。
第⼀部分:FileList
  这个部分是整个⽂件和⽂件夹的显⽰部分,再三权衡下,决定采⽤⾃定义DataGrid的⽅式来展现整个部分。
<UserControl x:Class="FileSelectorDemo.Views.FileList"
xmlns="schemas.microsoft/winfx/2006/xaml/presentation"
xmlns:x="schemas.microsoft/winfx/2006/xaml"
xmlns:mc="/markup-compatibility/2006"
xmlns:converter="clr-namespace:FileSelectorDemo.Converters"
xmlns:d="schemas.microsoft/expression/blend/2008"
xmlns:defines="clr-namespace:FileSelectorDemo.Defines"
xmlns:local="clr-namespace:FileSelectorDemo.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<converter:CountToVisibilityConverter x:Key="CountToVisibilityConverter"></converter:CountToVisibilityConverter>
<converter:TypeToVisibleConverter x:Key="TypeToVisibleConverter"></converter:TypeToVisibleConverter>
<converter:TypeToCollapsedConverter x:Key="TypeToCollapsedConverter"></converter:TypeToCollapsedConverter>
<converter:CollectionSelectedCountConverter x:Key="CollectionSelectedCountConverter"></converter:CollectionSelectedCountConverter>
</UserControl.Resources>
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<StackPanel Orientation="Vertical">
<DataGrid x:Name="fileList" Style="{StaticResource DefaultDataGrid}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" defines:MouseDoubleClick.Command="{Binding OpenCurrentDirectory}"
defines:MouseDoubleClick.CommandParameter="{Binding SelectedItem,RelativeSource={RelativeSource Self}}"  IsReadOnly="True"
defines:MouseLeftButtonUpClick.Command="{Binding SelectCurrentFileListItem}" ItemsSource="{Binding CurrentFileList}"  CanUserAddRows="False"  AutoGenerateColumns="False" GridLinesVisibility="None">
<DataGrid.Columns>
<DataGridTemplateColumn  MinWidth="60">
<DataGridTemplateColumn.Header>
<CheckBox Content="全选" Margin="2" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding DataContext.IsStateCheckAll,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=                                </DataGridTemplateColumn.
Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="不可选" FontSize="12" Foreground="Black" Visibility="{Binding CurrentType,Converter={StaticResource TypeToCollapsedConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center" ></TextBlock>
<CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" IsThreeState="False" IsHitTestVisible="False" Visibility="{Binding CurrentType,Converter={StaticResource TypeToVisibleCon                                        </Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="名称" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
<Image Source="{Binding Icon}" Margin="2"  HorizontalAlignment="Center" VerticalAlignment="Center"></Image>
<TextBlock Text="{Binding Name}" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="修改⽇期"  Binding="{Binding CreateTime}"/>
<DataGridTextColumn Header="类型"  Binding="{Binding CurrentType}"/>
<DataGridTextColumn Header="⼤⼩"  Binding="{Binding Size}"/>
</DataGrid.Columns>
</DataGrid>
<TextBlock Margin="2 5" HorizontalAlignment="Left" VerticalAlignment="Center" >
<Run>总共 </Run>
<Run Text="{Binding CurrentFileList.Count,Mode=OneWay}"></Run>
<Run> 个项⽬</Run>
免费平台源码资源网<Run>(已选中 </Run>
<Run Text="{Binding CurrentFileList,Converter={StaticResource CollectionSelectedCountConverter},Mode=OneWay}"></Run>
<Run> 个项⽬)</Run>
</TextBlock>
</StackPanel>
<TextBlock Text="该⽂件为空"  Visibility="{Binding CurrentFileList.Count,Converter={StaticResource CountToVisibilityConverter}}" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>
  这⾥都是⼀些常规的定义,这⾥就不再赘述,DataGrid样式是引⽤Themes⽂件夹下⾯的CustomDataGrid的样式,这⾥⾯也包括⾃定义的ScrollViewer样式和ScrollBar的样式,读者也可以进⾏思
考,另外需要注意设置下⾯的⼏个属性。
  1  IsReadOnly="True"这个属性能够保证在⿏标点击时候,不再显⽰内部的TextBox,从⽽使使⽤者不能够随意进⾏编辑。
  2  GridLinesVisibility="None" 这个能够使整个DataGrid不再显⽰分割线,从⽽使其样式更加接近Windows的原⽣样式。
  3  CanUserAddRows="False" 这个属性也⾮常重要,不然在整个显⽰区域的下⾯会多出⼀⾏,当⽤户点击它的时候会增加⾏。
  4  AutoGenerateColumns="False"这个就⽐较熟悉了,⼀般不让其⾃动增加列。
  5  SelectionUnit=“FullRow” 表⽰⿏标点击时选择的单位是整⾏,⽽不是其中的单元格或者其他,关于其它的⼏个枚举值,读者也可查阅相关了解。
  6  SelectionMode=“Extended”允许多选,当按下⿏标的Ctrl键进⾏点击的时候能够选中多个对象。
  7  最后⼀个就是关于设置DataGrid的虚拟化容器了,具体设置⽅法是:
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"></Setter>
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling" />
  WPF中的VirtualizingStackPanel.VirtualizationMode 附加属性指定 ItemsControl 中的⾯板如何虚拟化其⼦项。默认情况下,VirtualizingStackPanel 将为每个可见项创建⼀个项容器,并在不再需要
时(⽐如当项滚动到视图之外时)丢弃该容器。当 ItemsControl 包含多个项时,创建和废弃项容器的过程可能会对性能产⽣负⾯影响。如果 VirtualizingStackPanel.VirtualizationMode 设置
为 Recycling,VirtualizingStackPanel 将重⽤项容器,⽽不是每次都创建新的项容器,这个是摘录⾃MSDN的相关资料,由于我们加载的DataGrid的项不是很多,如果⾜够多的情况下,效果可能会更加明显,关于更多的“虚拟化”技术可以参考更多的资料。
  这⾥我们需要重点关注的是当我们双击DataGridRow时会打开对应的⼦⽂件夹,同时单击时会选中当前的DataGridRow,这⾥关于事件的绑定我们使⽤的不是System.Windows.Interactivity这种⽅式来绑定事件的,这⾥我们通过⾃定义⼀个附加属性来实现的,这⾥以⿏标左键双击为例来进⾏说明。
  这⾥需要进⾏说明的就是在OnMouseDoubleClick中,我们通过当前⿏标的点击的Point来查最终的DataGridRow 然后触发绑定的Command事件,在前台View中,我们只需要通过绑定到对应的ICommand即可。
public class MouseDoubleClick
{
public static DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command",
typeof(ICommand),
typeof(MouseDoubleClick),
new UIPropertyMetadata(CommandChanged));
public static DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter",
typeof(object),
typeof(MouseDoubleClick),
new UIPropertyMetadata(null));
public static void SetCommand(DependencyObject target, ICommand value)
{
target.SetValue(CommandProperty, value);
}
public static void SetCommandParameter(DependencyObject target, object value)
{
target.SetValue(CommandParameterProperty, value);
}
public static object GetCommandParameter(DependencyObject target)
{
return target.GetValue(CommandParameterProperty);
}
private static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Control control = target as Control;
if (control != null)
{
if ((e.NewValue != null) && (e.OldValue == null))
{
control.MouseDoubleClick += OnMouseDoubleClick;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
control.MouseDoubleClick -= OnMouseDoubleClick;
}
}
}
private static void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
DataGrid datagrid = sender as DataGrid;
Point point = e.GetPosition(datagrid);
IInputElement obj = datagrid.InputHitTest(point);
DependencyObject target = obj as DependencyObject;
while (target != null)
{
if (target is DataGridRow)
{
ICommand command = (ICommand)datagrid.GetValue(CommandProperty);
object commandParameter = datagrid.GetValue(CommandParameterProperty);
if (null != commandParameter)
{
command.Execute(commandParameter);
}
break;
}
target = VisualTreeHelper.GetParent(target);
}
}
defines:MouseDoubleClick.Command="{Binding OpenCurrentDirectory}"  defines:MouseDoubleClick.CommandParameter="{Binding SelectedItem,RelativeSource={RelativeSource Self}}"
  其中我们需要定义命名空间defines,这种⽅法为我们绑定View层中的各种事件提供能了⼀种新的⽅
式,这个是我们需要不断去总结和分析的地⽅。
  第⼆部分:Navigation
这⼀部分是我们的导航栏的部分,通过向前、向后、向上等快捷操作,我们能够快速切换⽂件夹,从⽽使切换路径变得更加容易,这⼀部分就是需要着重说⼀下最近浏览项⽬这个功能,它能够保存⽤户最近浏览的10个⽬录(开发者⾃定义),从⽽⽅便⽤户迅速切换不同的路径,这个是通过ToggleButton和Popup这⼀对经典的组合来实现的,具体实现请参考源代码。这⼀部分重点来说⼀下当⿏标移动到不同的位置时,能够变换绑定的图标是向前还是向后抑或选中状态,这个其实是通过绑定每⼀个Model的⼀个CurrentDirection来实现的,这⾥需要重点掌握DataTrigger的使⽤⽅法。
<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=NavigationPanel}" StaysOpen="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsOpen="{Binding IsChecked,ElementName=History,Mode=OneWay}" PopupAnimation="Slide">
<ItemsControl Background="#f5f5f5" BorderBrush="Gray" BorderThickness="1" Padding="0"
ItemsSource="{Binding AttachedDataContext.DirectoryHistory,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}}">
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border x:Name="outer" Padding="0 2" Background="#f5f5f5">
<StackPanel Orientation="Vertical" IsItemsHost="True"></StackPanel>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="radioButton" Command="{Binding AttachedDataContext.SwitchDirectory,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="bg" Padding="0" Background="#f5f5f5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
<Path x:Name="path" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"  Width="24" Height="24"
Opacity="0" StrokeThickness="2"  StrokeLineJoin="Round"  SnapsToDevicePixels="False">
</Path>
<Image Source="{Binding Icon}" Width="24" Height="24" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Image>
<TextBlock Text="{Binding Name}" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding CurrentDirection}" Value="选中">
<Setter Property="Opacity" Value="1" TargetName="path"></Setter>
<Setter Property="Data" Value="M 2,10 L 8,14 18,6" TargetName="path"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CurrentDirection}" Value="向前">
<Setter Property="Opacity" Value="0" TargetName="path"></Setter>
<Setter Property="Data" Value="M8,6 L1,11 8,16 M0,11 L15,11" TargetName="path"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CurrentDirection}" Value="向后">
<Setter Property="Opacity" Value="0" TargetName="path"></Setter>
<Setter Property="Data" Value="M8,6 L15,11 8,16 M0,11 L15,11" TargetName="path"></Setter>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="#91c9f7" TargetName="bg"></Setter>
<Setter Property="Opacity" Value="1" TargetName="path"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Popup>
  第三部分:BreadCrumbView
  这个部分是⽤来显⽰当前⽂件夹路径,并进⾏快速切换准备的,这个部分是⼀个组合控件,主要是通过ItemsControl和ToggleButton和Popup来实现的,这个⾥⾯需要注意的是这⾥⾯绑定的命令和⽅法都是在FileList的ViewModel中定义的,这⾥为了最⼤程度的实现代码的重⽤。很多时候我们会发现通过这种⽅式我们需要能够随时访问到FileListViewModel中的内容,这个是整个DEMO中最重要的部分,所以如何才能够引⽤到FileListViewModel⾥⾯的内容呢?
public partial class BreadCrumbView : UserControl
{
public BreadCrumbView()
{
InitializeComponent();
Loaded +=new RoutedEventHandler(BreadCrumbView_Loaded);
}
private void BreadCrumbView_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new ViewModels.BreadCrumbViewModel(AttachedDataContext);
}
/// <summary>
/
// 当前FileList的DataContext对象
/// </summary>
public object AttachedDataContext
{
get { return (object)GetValue(AttachedDataContextProperty); }
set { SetValue(AttachedDataContextProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding,
public static readonly DependencyProperty AttachedDataContextProperty =
DependencyProperty.Register("AttachedDataContext", typeof(object), typeof(BreadCrumbView), new PropertyMetadata(null));
}
    通过定义⼀个AttachedDataContext对象,我们能够将FileListViewModel中定义的属性分散到各个ViewModel中,这样在⼀定程度上能够保证避免FileListViewModel中代码过多同时职责过重的问题,但是同时我们也发现了,如果彼此之间的耦合过⼤,采⽤这种⽅式会加重代码之间的复杂度,因为有时不得不通过Action或者事件等⽅式来进⾏ViewModel之间的交互和通讯,所以降到这⾥我们不得不说⼀些较⼤较复杂的项⽬中使⽤框架的重要性了,⽐如Prism亦或是Caliburn.Micro等框架能够使整个软甲架构看起来更加清楚和明⽩,这也是为了更好地增加软件的模块化和灵活性。
  通过这个DEMO的分析,我们需要在不断的实践中去总结这类型的经验,从⽽使整个软件显得更加合理,最终使⾃⼰能够真正地对软件的架构的思想有⼀个⽐较深⼊的了解。
最后需要整个Demo的
以上就是c# WPF实现Windows资源管理器(附源码)的详细内容,更多关于c# WPF实现Windows资源管理器的资料请关注其它相关⽂章!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。