【问题标题】:WPF: Change the CornerRadius for only the last item in a ListBoxWPF:仅更改 ListBox 中最后一项的 CornerRadius
【发布时间】:2021-02-16 03:18:28
【问题描述】:

我创建了一个带圆角的ListBox。除了最后一个之外,我还为所有ListBoxItems 添加了底部边框。

但是,ListBoxItems 具有正常的方形角,因此当将鼠标悬停在或选择第一个或最后一个项目时,您会看到圆形 ListBox 角和方形 ListBoxItem 角之间的重叠。

我无法像设置 BorderThickness 一样设置 CornerRadius - 我认为这是因为 CornerRadius 是 Border 属性的一个属性 (?)。

我可以强制所有ListBoxItems 具有所有圆角以修复重叠,但是所有ListBoxItems 都有圆形下划线和选择——我宁愿没有。我只想要最后一项底部的那些圆角(最终是第一项的顶部)

我想使用与设置BrushThickness 类似的触发器来设置CornerRadius

有没有办法设置ListBox 中最后一项的圆角半径? (最后是第一项)

在我的测试中,我使用的是 NuGet 的 MaterialDesignTheme 包。因为那是非标准的,所以我将在这里添加我的所有代码(另请注意:我是 WPF 的新手,所以请随意批评任何看起来不正常的东西):

App.xaml:

<Application . . .
    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

MainWindow.xaml:(注意,如果您取消注释注释部分,它会设置所有 ListBoxItems 的样式,就像我只想要最后一个 ListBoxItem 样式一样)

<Window . . .
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        TextElement.Foreground="{DynamicResource MaterialDesignBody}"
        TextElement.FontWeight="Regular"
        TextElement.FontSize="13"
        TextOptions.TextFormattingMode="Ideal" 
        TextOptions.TextRenderingMode="Auto"        
        Background="{DynamicResource MaterialDesignPaper}"
        FontFamily="{DynamicResource MaterialDesignFont}">
    <Window.Resources>
        <local:IsLastItemInContainerConverter x:Key="IsLastItemInContainerConverter" />
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="200*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="1" Margin="5">
            <ListBox x:Name="GameListBox" 
                     BorderBrush="{DynamicResource MaterialDesignDivider}" 
                     BorderThickness="1">
                <ListBox.Resources>
                    <Style TargetType="Border">
                        <Setter Property="CornerRadius" Value="10"/>
                    </Style>
                </ListBox.Resources>
                <ListBox.ItemContainerStyle>
                    <Style TargetType="ListBoxItem" BasedOn="{StaticResource MaterialDesignListBoxItem}">
                        <!--<Style.Resources>
                            <Style TargetType="Border">
                                <Setter Property="CornerRadius" Value="0 0 10 10"/>
                            </Style>
                        </Style.Resources>-->
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
                                                   Converter={StaticResource IsLastItemInContainerConverter}}" Value="False">
                                <Setter Property="BorderThickness" Value="0 0 0 1" />
                                <Setter Property="BorderBrush" Value="{DynamicResource MaterialDesignDivider}"/>
                            </DataTrigger>

                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
                                                   Converter={StaticResource IsLastItemInContainerConverter}}" Value="True">
                                <Setter Property="BorderThickness" Value="0" />
                                <Setter Property="BorderBrush" Value="Transparent"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ListBox.ItemContainerStyle>

                <ListBoxItem>
                    <TextBlock>  Plain
                    </TextBlock>
                </ListBoxItem>

                <ListBoxItem>
                    <TextBlock>  Old
                    </TextBlock>
                </ListBoxItem>

                <ListBoxItem>
                    <TextBlock>  ListBox
                    </TextBlock>
                </ListBoxItem>

                <ListBoxItem>
                    <TextBlock>  Full of junk
                    </TextBlock>
                </ListBoxItem>
            </ListBox>
        </StackPanel>
    </Grid>
</Window>

...在 MainWindow.xaml.cs 中,我定义了转换器来查找最后一项:

    public class IsLastItemInContainerConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
                              object parameter, CultureInfo culture)
        {
            DependencyObject item = (DependencyObject)value;
            ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);

            return ic.ItemContainerGenerator.IndexFromContainer(item)
                    == ic.Items.Count - 1;
        }

        public object ConvertBack(object value, Type targetType,
                                  object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

【问题讨论】:

标签: c# wpf xaml


【解决方案1】:

首先,请理解我的代码中没有使用MaterialDesignTheme包。

Items.cs

我没有使用Converter,而是添加了一个模型类。

 public class Items
    {
        public string Name { get; set; }
        public bool IsFirst { get; set; }
        public bool IsLast { get; set; }
    }

App.xaml

我定义了ListBoxItemListBox 的样式,如下所示。

        <Style TargetType="{x:Type ListBoxItem}" x:Key="listboxitem">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border x:Name="border"
                                Background="White"
                                BorderBrush="#AAAAAA"
                                BorderThickness="1 1 1 0"
                                CornerRadius="0">
                            <TextBlock Text="{Binding Name}" Foreground="Black" FontSize="13" FontWeight="Normal" 
                                       Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <DataTrigger Binding="{Binding IsFirst}" Value="True">
                                <Setter TargetName="border" Property="CornerRadius" Value="10 10 0 0"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding IsLast}" Value="True">
                                <Setter TargetName="border" Property="CornerRadius" Value="0 0 10 10"/>
                                <Setter TargetName="border" Property="BorderThickness" Value="1 1 1 1"/>
                            </DataTrigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="border" Property="Background" Value="#666666"/>
                            </Trigger>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="border" Property="Background" Value="#666666"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style TargetType="{x:Type ListBox}" x:Key="listbox">
            <Setter Property="Width" Value="200"/>
            <Setter Property="Height" Value="200"/>
            <Setter Property="ItemContainerStyle" Value="{StaticResource listboxitem}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBox}">
                        <Border Background="Transparent"
                                BorderBrush="#AAAAAA"
                                BorderThickness="0 0 0 0"
                                CornerRadius="10">
                            <ItemsPresenter/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <StackPanel/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Style>

MainWindow.xaml

 <ListBox x:Name="lbx" Style="{StaticResource listbox}"/>

MainWindow.xaml.cs

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            lbx.ItemsSource = GetItems();
        }

        private List<Items> GetItems()
        {
            List<Items> source = new List<Items>();
            source.Add(new Items { Name = "Plain", IsFirst = true });
            source.Add(new Items { Name = "Old" });
            source.Add(new Items { Name = "ListBox" });
            source.Add(new Items { Name = "Full of junk", IsLast = true }); ;

            return source;
        }
    }

会这样显示..

【讨论】:

  • 这确实有效,但需要您修改模型以包含第一个和最后一个属性,应该有更好的方法。
  • @AthulRaj 如果你不能改变模型,你将不得不寻找另一种方法,但这种方法看起来很自然也很好。另外,它基本上很难处理,因为您必须调整ListBox顶部和底部的CornerRadius,甚至在它处于Selection状态时填充它。因此,使用Trigger简单地通过模型属性来处理会更好。
  • 自然好?这是一个列表框。如果该空间中有 10 个项目而不是 4 个,那么您必须滚动会发生什么?
  • @Andy 是的,它是 ListBox。但是,它经常像 RadioButton 一样使用。
  • 如你所说,如果你需要滚动,你应该在ListBox的模板上固定ItemsPresenter顶部和底部的CornerRadius。但是这样做的话,ListBoxItem的Fill就有问题了。
猜你喜欢
  • 2015-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-01
  • 2011-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多