Breadcrumb style with WPF-ListView

前端 未结 2 600
小蘑菇
小蘑菇 2020-12-13 07:16

I want to create a simple breadcrumb bar with ListView. Following a simple wireframe screenshot what I would like to archive in the future:

相关标签:
2条回答
  • 2020-12-13 08:01

    I made a custom shape that renders the arrow that you want. I used some code from the Rectangle class.

    As for the part of styling the first and last elements. You will need some sort of AttachedBehavior that adjusts some property based on the item index.

    using System;
    using System.Windows.Shapes;
    using System.Windows;
    using System.Windows.Media;
    
    namespace CustomShapes
    {
        public class SquaredArrow : Shape
        {
            protected Rect _rect = Rect.Empty;
    
            #region TipOffset
    
            /// <summary>
            /// TipOffset Dependency Property
            /// </summary>
            public static readonly DependencyProperty TipOffsetProperty =
                DependencyProperty.Register("TipOffset", typeof(double), typeof(SquaredArrow),
                    new FrameworkPropertyMetadata((double)10, FrameworkPropertyMetadataOptions.AffectsRender));
    
            /// <summary>
            /// Gets or sets the TipOffset property.  This dependency property 
            /// indicates ....
            /// </summary>
            [System.ComponentModel.Category("SquaredArrow")]
            public double TipOffset
            {
                get { return (double)GetValue(TipOffsetProperty); }
                set { SetValue(TipOffsetProperty, value); }
            }
    
            #endregion
    
            public SquaredArrow()
            {
                Rectangle r = new Rectangle();
                r.Measure(new Size(100, 100));
                r.Arrange(new Rect(0, 0, 100, 100));
            }
    
            static SquaredArrow()
            {
                StretchProperty.OverrideMetadata(typeof(SquaredArrow), new FrameworkPropertyMetadata(Stretch.Fill));
    
            }
    
            protected override Geometry DefiningGeometry
            {
                get { return CreateShape(); }
            }
    
            /// <summary>
            /// Return the transformation applied to the geometry before rendering
            /// </summary> 
            public override Transform GeometryTransform
            {
                get
                {
                    return Transform.Identity;
                }
            }
    
            /// <summary>
            /// This is where the arrow shape is created.
            /// </summary>
            /// <returns></returns>
            private Geometry CreateShape()
            {
                double width = _rect.Width;
                double height = _rect.Height;
    
                double borderOffset = GetStrokeThickness() / 2d;
    
                PathGeometry g = new PathGeometry();
    
                PathFigure figure = new PathFigure();
                figure.IsClosed = true;
                figure.StartPoint = new Point(borderOffset, borderOffset);
    
                figure.Segments.Add(new LineSegment(new Point(width - TipOffset + borderOffset, borderOffset), true));
                figure.Segments.Add(new LineSegment(new Point(width + borderOffset, height / 2d + borderOffset), true));
                figure.Segments.Add(new LineSegment(new Point(width + borderOffset - TipOffset, height + borderOffset), true));
                figure.Segments.Add(new LineSegment(new Point(borderOffset, height + borderOffset), true));
    
                g.Figures.Add(figure);
    
                return g;
            }
    
            /// <summary>
            /// Updates DesiredSize of the Rectangle.  Called by parent UIElement.  This is the first pass of layout. 
            /// </summary>
            /// <param name="constraint">Constraint size is an "upper limit" that Rectangle should not exceed.</param>
            /// <returns>Rectangle's desired size.</returns>
            protected override Size MeasureOverride(Size constraint)
            {
                if (Stretch == Stretch.UniformToFill)
                {
                    double width = constraint.Width;
                    double height = constraint.Height;
    
                    if (Double.IsInfinity(width) && Double.IsInfinity(height))
                    {
                        return GetNaturalSize();
                    }
                    else if (Double.IsInfinity(width) || Double.IsInfinity(height))
                    {
                        width = Math.Min(width, height);
                    }
                    else
                    {
                        width = Math.Max(width, height);
                    }
    
                    return new Size(width, width);
                }
    
                return GetNaturalSize();
            }
    
            /// <summary>
            /// Returns the final size of the shape and cachnes the bounds. 
            /// </summary>
            protected override Size ArrangeOverride(Size finalSize)
            {
                // Since we do NOT want the RadiusX and RadiusY to change with the rendering transformation, we
                // construct the rectangle to fit finalSize with the appropriate Stretch mode.  The rendering 
                // transformation will thus be the identity.
    
                double penThickness = GetStrokeThickness();
                double margin = penThickness / 2;
    
                _rect = new Rect(
                    margin, // X 
                    margin, // Y
                    Math.Max(0, finalSize.Width - penThickness),    // Width 
                    Math.Max(0, finalSize.Height - penThickness));  // Height
    
                switch (Stretch)
                {
                    case Stretch.None:
                        // A 0 Rect.Width and Rect.Height rectangle 
                        _rect.Width = _rect.Height = 0;
                        break;
    
                    case Stretch.Fill:
                        // The most common case: a rectangle that fills the box.
                        // _rect has already been initialized for that.
                        break;
    
                    case Stretch.Uniform:
                        // The maximal square that fits in the final box 
                        if (_rect.Width > _rect.Height)
                        {
                            _rect.Width = _rect.Height;
                        }
                        else  // _rect.Width <= _rect.Height
                        {
                            _rect.Height = _rect.Width;
                        }
                        break;
    
                    case Stretch.UniformToFill:
                        // The minimal square that fills the final box
                        if (_rect.Width < _rect.Height)
                        {
                            _rect.Width = _rect.Height;
                        }
                        else  // _rect.Width >= _rect.Height 
                        {
                            _rect.Height = _rect.Width;
                        }
                        break;
                }
    
                return finalSize;
            }
    
            /// <summary> 
            /// Get the natural size of the geometry that defines this shape
            /// </summary> 
            protected Size GetNaturalSize()
            {
                double strokeThickness = GetStrokeThickness();
                return new Size(strokeThickness, strokeThickness);
            }
    
            protected double GetStrokeThickness()
            {
                return this.StrokeThickness;
            }
    
            /// <summary> 
            /// Render callback.
            /// </summary> 
            protected override void OnRender(DrawingContext drawingContext)
            {
                Pen pen = new Pen(Stroke, GetStrokeThickness());
    
                drawingContext.DrawGeometry(Fill, pen, DefiningGeometry);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-13 08:20

    Now as I had to find an answer myself in short time, this is my current solution. Also if you do not need the "selectable" feature of ListBox, you can exchange it with ItemControl.

    Current solution for the problem

    Here's the code. Please be aware that I've commented out the "IsSelected" Triggers for the ItemStyleContainer...

     <ListBox Padding="0" DockPanel.Dock="Left" ItemsSource="{Binding TagList}"
                    MinWidth="300" Background="Transparent" BorderThickness="0"
                    ScrollViewer.HorizontalScrollBarVisibility="Hidden" 
                    ScrollViewer.VerticalScrollBarVisibility="Hidden">
                        <ListBox.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Margin="8,0,0,0" Orientation="Horizontal"></StackPanel>
                            </ItemsPanelTemplate>
                        </ListBox.ItemsPanel>
                        <ListBox.ItemContainerStyle>
                            <Style TargetType="{x:Type ListBoxItem}">
                                <Setter Property="Background" Value="{DynamicResource LXBarButtonBackgroundNormal}"/>
                                <Setter Property="BorderBrush" Value="{DynamicResource LXBarButtonBorderNormal}"/>
                                <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                                <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                                <Setter Property="Padding" Value="0"/>
                                <Setter Property="SnapsToDevicePixels" Value="true"/>
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="ListBoxItem">
    
                                            <DockPanel LastChildFill="True" Margin="-8,0,0,0">
                                                <Path DockPanel.Dock="Left"  Stroke="{DynamicResource LXBarButtonBorderNormal}" Fill="{DynamicResource LXBarButtonBackgroundNormal}" Data="F1 M 112,144L 104,144L 112,160L 104,176L 112,176" Stretch="Fill" Height="32" Width="8" />  
                                                <Path DockPanel.Dock="Right" Stroke="{DynamicResource LXBarButtonBorderNormal}" Fill="{DynamicResource LXBarButtonBackgroundNormal}" Data="F1 M 168,144L 176,160L 168,176" Stretch="Fill" Height="32" Width="8" />
                                                <Border Name="Border" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Padding="{TemplateBinding Padding}" BorderThickness="0,1" VerticalAlignment="Center">
                                                          <ContentPresenter />
                                                        <!--
                                                        <ControlTemplate.Triggers>
                                                          <Trigger Property="IsSelected" Value="true">
                                                            <Setter TargetName="Border" Property="Background"
                                                                    Value="Blue"/>
                                                          </Trigger>
                                                          <Trigger Property="IsEnabled" Value="false">
                                                            <Setter Property="Foreground"
                                                                    Value="Red"/>
                                                          </Trigger>
                                                        </ControlTemplate.Triggers>
                                                        -->
                                                </Border>
                                            </DockPanel>
    
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ListBox.ItemContainerStyle>
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                        <DockPanel VerticalAlignment="Center" Height="30">
                                            <local:LXImageButton BorderThickness="0" Style="{DynamicResource LXBarImageButton}" Padding="0"  DockPanel.Dock="Right" Background="Transparent" Command="{Binding RemoveTagBtn}" Height="16" Width="16"
                                            NormalImage="/com.example.Views;component/Resources/Icons/Buttons/btnCloseXS_normal.png"
                                            ActiveImage="/com.example.Views;component/Resources/Icons/Buttons/btnCloseXS_active.png"
                                            HoverImage="/com.example.Views;component/Resources/Icons/Buttons/btnCloseXS_hover.png"
                                            PressedImage="/com.example.Views;component/Resources/Icons/Buttons/btnCloseXS_hover.png"
                                            DisabledImage="/com.example.Views;component/Resources/Icons/Buttons/btnCloseXS_passive.png"
                                             />
                                            <Label DockPanel.Dock="Left"  FontSize="12" Content="{Binding Content, FallbackValue=Tagname n/a}" VerticalAlignment="Center"/>
                                        </DockPanel>   
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
    
    0 讨论(0)
提交回复
热议问题