WPF: измените cornerRadius только для последнего элемента в списке

#c# #wpf #xaml

#c# #wpf #xaml

Вопрос:

Я создал a ListBox с закругленными углами. Я также добавил нижнюю границу для всех ListBoxItem s, кроме последнего.

Однако буквы ListBoxItem s имеют нормальные квадратные углы, поэтому при наведении курсора мыши на первый или последний элемент или выборе первого или последнего элемента вы можете увидеть перекрытие между круглым ListBox углом и квадратными ListBoxItem углами.

Несоответствие ListBox CoprnerRadius

Я не могу установить cornerRadius так же, как я установил BorderThickness — я думаю, это потому, что cornerRadius является свойством свойства Border(?).

Я могу заставить ВСЕ ListBoxItem s иметь все круглые углы, что исправляет перекрытие, но тогда у ВСЕХ ListBoxItem s будут круглые подчеркивания и выделения, чего я бы предпочел не иметь. Мне нужны только эти круглые углы в нижней части последнего элемента (и, в конечном итоге, в верхней части первого элемента)

Я хотел бы использовать аналогичный триггер для настройки CornerRadius , который я делаю для настройки BrushThickness .

Есть ли способ установить радиус угла только для последнего элемента в a ListBox ? (и, в конечном итоге, для первого элемента)

В моем тесте я использую пакет MaterialDesignTheme от NuGet. Поскольку это нестандартно, я добавлю сюда весь свой код (также обратите внимание: я новичок в 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();
        }
    }
 

Комментарии:

1. Я бы попробовал маску непрозрачности и обрезал весь элемент управления. У списка есть граница вокруг него, поэтому я думаю, вы могли бы адаптировать этот подход: chriscavanagh.wordpress.com/2008/10/03 /…

Ответ №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

Я определил стили ListBoxItem , ListBox как показано ниже.

         <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;
        }
    }
 

Это будет показано следующим образом..

Результат

Комментарии:

1. это действительно работает, но требует, чтобы вы изменили модель, включив в нее свойства first и last, вероятно, должен быть лучший способ.

2. @AthulRaj Если вы не можете изменить модель, вам придется найти другой способ, но этот метод выглядит естественным и хорошим. Кроме того, это сложно обрабатывать в основном потому, что вам нужно настроить cornerRadius в верхней и нижней части списка и даже заполнить его, когда он находится в состоянии выбора. Поэтому было бы лучше использовать Trigger для обработки его просто через свойства модели.

3. Естественно и хорошо? Это список. Что происходит, когда в этом пространстве 10 элементов вместо 4, поэтому вам нужно прокручивать?

4. @Andy Да, это список. Однако он часто используется как RadioButton .

5. Как вы сказали, если вам нужно прокручивать, вы должны исправить cornerRadius в верхней и нижней части ItemsPresenter в шаблоне списка. Но если вы это сделаете, возникнет проблема с заполнением элемента списка.