WPF 实现面包屑控件
- 框架使用
.NET4 至 .NET6
Visual Studio 2022
- 创建
BreadCrumbBar.xaml
设置VirtualizingStackPanel
为水平方向显示Orientation="Horizontal"
。 - 创建
BreadCrumbBarItem.xaml
设置ContentPresenter
前显示符号>
,通过BreadCrumbBarConvertr
转换器判断如果是当前是第0
个则隐藏显示符>
。 - 创建
BreadCrumbBar.cs
继承ListBox
当选中时获取当前的SelectedIndex
大于当前的设置IsEnabled
设置false
反之则为true
。
实现代码
1)创建 BreadCrumbBar.cs
代码如下:
using System.Collections; using System.Collections.Specialized; using System.Windows; using System.Windows.Controls; namespace WPFDevelopers.Controls { public class BreadCrumbBar : ListBox { static BreadCrumbBar() { DefaultStyleKeyProperty.OverrideMetadata(typeof(BreadCrumbBar), new FrameworkPropertyMetadata(typeof(BreadCrumbBar))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); } protected override void OnSelectionChanged(SelectionChangedEventArgs e) { base.OnSelectionChanged(e); for (int i = 0; i <= SelectedIndex; i++) { var item = (ListBoxItem)ItemContainerGenerator.ContainerFromIndex(i); if (item == null) continue; if (!item.IsEnabled) item.IsEnabled = true; } for (int i = Items.Count - 1; i > SelectedIndex; i--) { var item = (ListBoxItem)ItemContainerGenerator.ContainerFromIndex(i); if (item == null) continue; if(item.IsEnabled) item.IsEnabled = false; } } protected override bool IsItemItsOwnContainerOverride(object item) { return item is BreadCrumbBarItem; } protected override DependencyObject GetContainerForItemOverride() { return new BreadCrumbBarItem(); } } }
2)创建 BreadCrumbBarItem.cs
代码如下:
using System; using System.Windows; using System.Windows.Controls; namespace WPFDevelopers.Controls { public class BreadCrumbBarItem : ListBoxItem { private static readonly Type _typeofSelf = typeof(BreadCrumbBarItem); static BreadCrumbBarItem() { DefaultStyleKeyProperty.OverrideMetadata(_typeofSelf, new FrameworkPropertyMetadata(_typeofSelf)); } } }
3)创建 BreadCrumbBar.xaml
代码如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:WPFDevelopers.Controls" xmlns:converts="clr-namespace:WPFDevelopers.Converts"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Basic/ControlBasic.xaml" /> </ResourceDictionary.MergedDictionaries> <converts:BreadCrumbBarConvertr x:Key="WD.BreadCrumbBarConvertr" /> <Style x:Key="WD.BreadCrumbBarItem" BasedOn="{StaticResource WD.ControlBasicStyle}" TargetType="{x:Type controls:BreadCrumbBarItem}"> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="Padding" Value="6,0" /> <Setter Property="FontWeight" Value="SemiBold" /> <Setter Property="FontSize" Value="{StaticResource WD.TitleFontSize}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type controls:BreadCrumbBarItem}"> <StackPanel Orientation="Horizontal"> <Path Name="PART_PathSymbol" Width="9" Height="9" Data="{StaticResource WD.BreadCrumbBarGeometry}" Fill="{DynamicResource WD.PlaceholderTextSolidColorBrush}" IsHitTestVisible="False" Stretch="Uniform"> <Path.Visibility> <MultiBinding Converter="{StaticResource WD.BreadCrumbBarConvertr}"> <Binding RelativeSource="{RelativeSource AncestorType=ListBoxItem}" /> <Binding Path="SelectedIndex" RelativeSource="{RelativeSource AncestorType=ListBox}" /> </MultiBinding> </Path.Visibility> </Path> <ContentPresenter x:Name="PART_ContentPresenter" Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" TextElement.Foreground="{TemplateBinding Foreground}" /> </StackPanel> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Foreground" Value="{DynamicResource WD.PrimaryMouseOverSolidColorBrush}" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="WD.BreadCrumbBar" BasedOn="{StaticResource WD.ControlBasicStyle}" TargetType="{x:Type controls:BreadCrumbBar}"> <Setter Property="Height" Value="40" /> <Setter Property="Margin" Value="5" /> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </Setter.Value> </Setter> <Setter Property="ItemContainerStyle" Value="{StaticResource WD.BreadCrumbBarItem}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type controls:BreadCrumbBar}"> <Border Margin="{TemplateBinding Margin}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <ScrollViewer Grid.Column="1" HorizontalScrollBarVisibility="Auto"> <ItemsPresenter x:Name="ItemsHost" /> </ScrollViewer> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style BasedOn="{StaticResource WD.BreadCrumbBar}" TargetType="{x:Type controls:BreadCrumbBar}" /> </ResourceDictionary>
4)创建 BreadCrumbBarConverter.cs
代码如下:
using System; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; namespace WPFDevelopers.Converts { public class BreadCrumbBarConvertr : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { var item = values[0] as ListBoxItem; var listBox = ItemsControl.ItemsControlFromItemContainer(item) as ListBox; if (listBox == null) return Visibility.Collapsed; var arrayIndex = listBox.ItemContainerGenerator.IndexFromContainer(item); if (arrayIndex == 0) return Visibility.Collapsed; return Visibility.Visible; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
5)创建 BreadCrumbBarExample.xaml
代码如下:
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <wd:BreadCrumbBar DisplayMemberPath="Text" ItemsSource="{Binding BreadcrumbItems, RelativeSource={RelativeSource AncestorType=UserControl}}" SelectedItem="{Binding BreadcrumbItem, RelativeSource={RelativeSource AncestorType=UserControl}}" SelectionChanged="BreadCrumbBar_SelectionChanged" /> <Frame Name="myFrame" Grid.Row="1" NavigationUIVisibility="Hidden" /> <Button Grid.Row="2" Width="80" Click="btnNext_Click" Content="Next" Style="{StaticResource WD.SuccessPrimaryButton}" /> </Grid>
6) BreadCrumbBarExample.xaml.cs
代码如下:
using System.Collections.Generic; using System; using System.Windows.Controls; using System.Collections.ObjectModel; using System.Windows; using System.Reflection; namespace WPFDevelopers.Samples.ExampleViews { /// <summary> /// StepExample.xaml 的交互逻辑 /// </summary> public partial class BreadCrumbBarExample : UserControl { private int index = 0; private List<BreadcrumbItem> Breadcrumbs = new List<BreadcrumbItem>(); public ObservableCollection<BreadcrumbItem> BreadcrumbItems { get { return (ObservableCollection<BreadcrumbItem>)GetValue(BreadcrumbItemsProperty); } set { SetValue(BreadcrumbItemsProperty, value); } } public static readonly DependencyProperty BreadcrumbItemsProperty = DependencyProperty.Register("BreadcrumbItems", typeof(ObservableCollection<BreadcrumbItem>), typeof(BreadCrumbBarExample), new PropertyMetadata(null)); public BreadcrumbItem BreadcrumbItem { get { return (BreadcrumbItem)GetValue(BreadcrumbItemProperty); } set { SetValue(BreadcrumbItemProperty, value); } } public static readonly DependencyProperty BreadcrumbItemProperty = DependencyProperty.Register("BreadcrumbItem", typeof(BreadcrumbItem), typeof(BreadCrumbBarExample), new PropertyMetadata(null)); public BreadCrumbBarExample() { InitializeComponent(); Loaded += BreadCrumbBarExample_Loaded; } private void BreadCrumbBarExample_Loaded(object sender, RoutedEventArgs e) { var breadcrumbItems = new List<BreadcrumbItem>() { new BreadcrumbItem() { Text = "主页" , Uri=new Uri("pack://application:,,,/WPFDevelopers.Samples;component/ExampleViews/DrawerMenu/HomePage.xaml",UriKind.Absolute ) }, new BreadcrumbItem() { Text = "Edge", Uri=new Uri("pack://application:,,,/WPFDevelopers.Samples;component/ExampleViews/DrawerMenu/EdgePage.xaml" ,UriKind.Absolute ) }, new BreadcrumbItem() { Text = "邮件", Uri=new Uri("pack://application:,,,/WPFDevelopers.Samples;component/ExampleViews/DrawerMenu/EmailPage.xaml" ,UriKind.Absolute ) }, }; Breadcrumbs = breadcrumbItems; myFrame.Navigate(Breadcrumbs[index].Uri); BreadcrumbItems = new ObservableCollection<BreadcrumbItem>() { Breadcrumbs[index] }; } private void BreadCrumbBar_SelectionChanged(object sender, SelectionChangedEventArgs e) { myFrame.Navigate(BreadcrumbItem.Uri); index = BreadcrumbItems.IndexOf(BreadcrumbItem); } private void btnNext_Click(object sender, RoutedEventArgs e) { index += 1; if (index >= Breadcrumbs.Count) return; var model = Breadcrumbs[index]; if (BreadcrumbItems.Contains(model)) { BreadcrumbItem = model; return; } BreadcrumbItems.Add(model); BreadcrumbItem = model; } } public class BreadcrumbItem { public string Text { get; set; } public Uri Uri { get; set; } } }
效果图