WPF 简单实现面包屑
框架使用.NET4 至 .NET6
;
Visual Studio 2022
;
面包屑展示使用控件如下:
Button
做首页按钮,当点击时回到首页。ItemsControl
做面包屑Item
展示,DataTemplate
->Hyperlink >
做点击时回到当前Item
。
ListView
展示当前 Item
的子项,也可以换做 ListBox
控件或其他。
效果图
实现代码
1)创建 BreadCrumbBarExample.xaml
代码如下:
<wd:Window x:Class="WpfApp1.BreadCrumbBarExample" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers" | |
xmlns:local="clr-namespace:WpfApp1" | |
mc:Ignorable="d" | |
Title="BreadCrumbBarExample - 面包屑" Height="450" Width="800"> | |
<Grid> | |
<Grid.RowDefinitions> | |
<RowDefinition Height="Auto"/> | |
<RowDefinition/> | |
</Grid.RowDefinitions> | |
<Grid Margin="0,5"> | |
<Grid.ColumnDefinitions> | |
<ColumnDefinition Width="Auto"/> | |
<ColumnDefinition/> | |
</Grid.ColumnDefinitions> | |
<Button Style="{StaticResource PathButton}" ToolTip="主页" | |
Click="btnHome_Click" Cursor="Hand"> | |
<Path Data="M804.571 566.857v274.286q0 14.857-10.857 25.714t-25.714 10.857h-219.429v-219.429h-146.286v219.429h-219.429q-14.857 0-25.714-10.857t-10.857-25.714v-274.286q0-0.571 0.286-1.714t0.286-1.714l328.571-270.857 328.571 270.857q0.571 1.143 0.571 3.429zM932 527.429l-35.429 42.286q-4.571 5.143-12 6.286h-1.714q-7.429 0-12-4l-395.429-329.714-395.429 329.714q-6.857 4.571-13.714 4-7.429-1.143-12-6.286l-35.429-42.286q-4.571-5.714-4-13.429t6.286-12.286l410.857-342.286q18.286-14.857 43.429-14.857t43.429 14.857l139.429 116.571v-111.429q0-8 5.143-13.143t13.143-5.143h109.714q8 0 13.143 5.143t5.143 13.143v233.143l125.143 104q5.714 4.571 6.286 12.286t-4 13.429z" | |
Stretch="Uniform" Width="15" | |
Fill="{StaticResource PrimaryTextSolidColorBrush}"/> | |
</Button> | |
<ScrollViewer HorizontalScrollBarVisibility="Auto" | |
Grid.Column="1"> | |
<ItemsControl ItemsSource="{Binding BreadCrumbBars}"> | |
<ItemsControl.ItemsPanel> | |
<ItemsPanelTemplate> | |
<VirtualizingStackPanel Orientation="Horizontal"/> | |
</ItemsPanelTemplate> | |
</ItemsControl.ItemsPanel> | |
<ItemsControl.ItemTemplate> | |
<DataTemplate> | |
<TextBlock VerticalAlignment="Center"> | |
<Hyperlink Click="Hyperlink_Click" | |
Cursor="Hand"> | |
<Run Text="{Binding Name}"/> | |
</Hyperlink> | |
<Run Text=" > "/> | |
</TextBlock> | |
</DataTemplate> | |
</ItemsControl.ItemTemplate> | |
</ItemsControl> | |
</ScrollViewer> | |
</Grid> | |
<ListView ItemsSource="{Binding Files}" Grid.Row="1" | |
SelectedItem="{Binding FilesSelectedItem}" | |
Name="FilesListView"> | |
<ListView.View> | |
<GridView> | |
<GridViewColumn Header="Name" | |
DisplayMemberBinding="{Binding Name}" | |
Width="200"/> | |
</GridView> | |
</ListView.View> | |
</ListView> | |
</Grid> | |
</wd:Window> |
2) BreadCrumbBarExample.xaml.cs
代码如下:
BreadCrumbBars
记录面包屑的值。Files
记录当前的子项。- 当点击面包屑则删除当前至末尾的所有数据
List.RemoveRange(0, List.Count - 0);
。
using System; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.ComponentModel; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
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; | |
namespace WpfApp1 | |
{ | |
/// <summary> | |
/// BreadCrumbBarExample.xaml 的交互逻辑 | |
/// </summary> | |
public partial class BreadCrumbBarExample : INotifyPropertyChanged | |
{ | |
private ObservableCollection<FolderItem> files; | |
public ObservableCollection<FolderItem> Files | |
{ | |
get { return files; } | |
set { files = value; OnPropertyChanged(); } | |
} | |
private ObservableCollection<FolderItem> breadCrumbBars; | |
public ObservableCollection<FolderItem> BreadCrumbBars | |
{ | |
get { return breadCrumbBars; } | |
set { breadCrumbBars = value; OnPropertyChanged(); } | |
} | |
private FolderItem filesSelectedItem; | |
public FolderItem FilesSelectedItem | |
{ | |
get { return filesSelectedItem; } | |
set { filesSelectedItem = value; OnPropertyChanged(); } | |
} | |
public BreadCrumbBarExample() | |
{ | |
InitializeComponent(); | |
FilesListView.SelectionChanged += FilesListView_SelectionChanged; | |
DataContext = this; | |
BreadCrumbBars = new ObservableCollection<FolderItem>(); | |
Files = new ObservableCollection<FolderItem>(); | |
Loaded += BreadCrumbBarExample_Loaded; | |
} | |
private void BreadCrumbBarExample_Loaded(object sender, RoutedEventArgs e) | |
{ | |
GetHome(); | |
} | |
private void btnHome_Click(object sender, RoutedEventArgs e) | |
{ | |
Files.Clear(); | |
GetHome(); | |
var array = BreadCrumbBars.ToList(); | |
array.RemoveRange(0, BreadCrumbBars.Count - 0); | |
BreadCrumbBars = new ObservableCollection<FolderItem>(array); | |
} | |
private void Hyperlink_Click(object sender, RoutedEventArgs e) | |
{ | |
var hyperlink = (Hyperlink)sender; | |
if (hyperlink == null) return; | |
var model = (FolderItem)hyperlink.DataContext; | |
if (model == null) return; | |
LinkFolder(model); | |
} | |
private void FilesListView_SelectionChanged(object sender, SelectionChangedEventArgs e) | |
{ | |
if (FilesSelectedItem == null) return; | |
BreadCrumbBars.Add(FilesSelectedItem); | |
AddFolder(FilesSelectedItem); | |
} | |
private void LinkFolder(FolderItem folderItem) | |
{ | |
var index = BreadCrumbBars.IndexOf(folderItem); | |
if (index < BreadCrumbBars.Count) | |
{ | |
var array = BreadCrumbBars.ToList(); | |
if(index == 0) | |
{ | |
index += 1; | |
array.RemoveRange(index, BreadCrumbBars.Count - index); | |
} | |
else | |
array.RemoveRange(index, BreadCrumbBars.Count - index); | |
BreadCrumbBars = new ObservableCollection<FolderItem>(array); | |
} | |
AddFolder(folderItem); | |
} | |
void AddFolder(FolderItem folderItem) | |
{ | |
try | |
{ | |
if (folderItem == null) return; | |
FilesListView.SelectedIndex = -1; | |
FilesListView.SelectedItem = null; | |
Files.Clear(); | |
var list = new ObservableCollection<FolderItem>(); | |
if (File.Exists(folderItem.FullName)) return; | |
var directory = new DirectoryInfo(folderItem.FullName + @"\"); | |
if (directory.GetDirectories() == null) return; | |
foreach (var item in directory.GetDirectories()) | |
{ | |
var fileInfo = new FolderItem() { Name = item.Name, FullName = item.FullName }; | |
if (list.Contains(fileInfo)) continue; | |
Files.Add(fileInfo); | |
} | |
foreach (var item in directory.GetFiles()) | |
{ | |
var fileInfo = new FolderItem() { Name = item.Name, FullName = item.FullName }; | |
if (list.Contains(fileInfo)) continue; | |
Files.Add(fileInfo); | |
} | |
} | |
catch (Exception) | |
{ | |
throw; | |
} | |
} | |
void GetHome() | |
{ | |
DriveInfo[] allDrives = DriveInfo.GetDrives(); | |
foreach (DriveInfo drive in allDrives) | |
{ | |
if (drive.IsReady == true) | |
{ | |
var fileInfo = new FolderItem() { Name = drive.Name, FullName = drive.Name }; | |
Files.Add(fileInfo); | |
} | |
} | |
} | |
public event PropertyChangedEventHandler PropertyChanged; | |
protected void OnPropertyChanged([CallerMemberName] string propertyName = "") | |
{ | |
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | |
} | |
} | |
public class FolderItem | |
{ | |
public string Name { get; set; } | |
public string FullName { get; set; } = string.Empty; | |
} | |
} |