Это оказалось немного болезненным.Если вы хотите, чтобы радиус угла и толщина границы были правильно параметризованы, это потребует дополнительной работы: вам понадобятся конвертеры значений для создания или изменения значений CornerRadius
и Thickness
по мере необходимости.
Другой подход состоял бы в том, чтобы опустить триггеры и написать два больших мультиконвертера, для Thickness и CornerRadius, которые принимают те же параметры, что и тот, который я написал, плюс значения толщины границы и радиуса «по умолчанию», изатем верните Thickness
и CornerRadius
соответственно.
<Style TargetType="ListBox" x:Key="GridLineListBox">
<Style.Resources>
<local:CellTypeConverter x:Key="CellTypeConverter" />
</Style.Resources>
<Setter Property="AlternationCount" Value="{x:Static sys:Int32.MaxValue}" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="BorderBrush" Value="SteelBlue" />
<Setter Property="local:GridLineListBox.ColumnCount" Value="6" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="16"
ClipToBounds="True"
>
<ItemsPresenter Margin="-1" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<UniformGrid
Columns="{Binding (local:GridLineListBox.ColumnCount), RelativeSource={RelativeSource AncestorType=ListBox}}"
IsItemsHost="True"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<!-- Put this in an attached property so we don't have to copy/paste the whole binding for each trigger -->
<Setter Property="local:GridLineListBox.CellType">
<Setter.Value>
<MultiBinding Converter="{StaticResource CellTypeConverter}">
<Binding Path="Items.Count" RelativeSource="{RelativeSource AncestorType=ListBox}" />
<Binding Path="(ItemsControl.AlternationIndex)" RelativeSource="{RelativeSource Self}" />
<Binding Path="(local:GridLineListBox.ColumnCount)" RelativeSource="{RelativeSource AncestorType=ListBox}" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Margin" Value="0" />
<Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=ListBox}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<!--
Negative right/bottom margin because I'm getting a gap with
SnapToDevicePixels and I'm too lazy to figure out the real reason.
-->
<Border
x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0,0,2,2"
Background="{TemplateBinding Background}"
ClipToBounds="True"
Margin="-1"
>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="local:GridLineListBox.CellType" Value="TopLeft">
<Setter TargetName="Bd" Property="CornerRadius" Value="16,0,0,0" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="TopRight">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,0,2" />
<Setter TargetName="Bd" Property="CornerRadius" Value="0,16,0,0" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="Right">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,0,2" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="BottomRight">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,0,0" />
<Setter TargetName="Bd" Property="CornerRadius" Value="0,0,16,0" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="Bottom">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,2,0" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="BottomLeft">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,2,0" />
<Setter TargetName="Bd" Property="CornerRadius" Value="0,0,0,16" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="SingleRowLeft">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,2,0" />
<Setter TargetName="Bd" Property="CornerRadius" Value="16,0,0,16" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="SingleRowRight">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,0,0" />
<Setter TargetName="Bd" Property="CornerRadius" Value="0,16,16,0" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
C #
public enum CellType {
TopLeft, Top, TopRight, Right, BottomRight, Bottom, BottomLeft, Left,
SingleRowLeft, SingleRowRight, Inner
}
public class CellTypeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var itemsCount = System.Convert.ToInt32(values[0]);
var itemIndex = System.Convert.ToInt32(values[1]);
var columnCount = System.Convert.ToInt32(values[2]);
int rowCount = itemsCount / columnCount;
if (itemsCount % columnCount > 0)
++rowCount;
int lowerRightIndex = (rowCount * columnCount) - 1;
int lowerLeftIndex = (rowCount - 1) * columnCount;
if (itemIndex == 0)
{
return (rowCount == 1) ? CellType.SingleRowLeft : CellType.TopLeft;
}
else if (itemIndex == columnCount - 1)
{
return (rowCount == 1) ? CellType.SingleRowRight : CellType.TopRight;
}
else if (itemIndex < columnCount)
return CellType.Top;
else if (itemIndex == lowerRightIndex)
return CellType.BottomRight;
else if ((itemIndex + 1) % columnCount == 0)
return CellType.Right;
else if (itemIndex == lowerLeftIndex)
return CellType.BottomLeft;
else if (itemIndex > lowerLeftIndex)
return CellType.Bottom;
else if (itemIndex % columnCount == 0)
return CellType.Left;
return CellType.Inner;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public static class GridLineListBox
{
public static CellType GetCellType(ListBoxItem obj)
{
return (CellType)obj.GetValue(CellTypeProperty);
}
public static void SetCellType(ListBoxItem obj, CellType value)
{
obj.SetValue(CellTypeProperty, value);
}
public static readonly DependencyProperty CellTypeProperty =
DependencyProperty.RegisterAttached("CellType", typeof(CellType), typeof(GridLineListBox),
new PropertyMetadata((CellType)(-1)));
public static int GetColumnCount(ListBox obj)
{
return (int)obj.GetValue(ColumnCountProperty);
}
public static void SetColumnCount(ListBox obj, int value)
{
obj.SetValue(ColumnCountProperty, value);
}
public static readonly DependencyProperty ColumnCountProperty =
DependencyProperty.RegisterAttached("ColumnCount", typeof(int), typeof(GridLineListBox),
new PropertyMetadata(0, ColumnCount_PropertyChanged));
private static void ColumnCount_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as ListBox;
}
}
Пример:
<ListBox
ItemsSource="{Binding CollectionOfStrings}"
Style="{StaticResource GridLineListBox}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Label
Content="{Binding}"
HorizontalAlignment="Center"
/>
<Label
Content="{Binding (local:GridLineListBox.CellType), RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
HorizontalAlignment="Center"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>