8,750
社区成员
发帖
与我相关
我的任务
分享
我在尝试制作一个数独的小游戏,我自定义了三个控件,分别是:SudokuPanel,SudokuGroup和SudokuItem。
SudokuPanel:作为数独表格的主面板区域,继承ItemsControl,限制了Item项为SudokuGroup。
SudokuGroup:数独9*9的区域,继承ItemsControl,限制了Item项为SudokuItem。
SudokuItem:数独的单元格,继承Button。
在ViewModel定义了属性ObservableCollection<Unit> Units,Unit对象含有ObservableCollection<Cell> Children属性,两个类均实现了INotifyPropertyChanged接口。
<ctrl:SudokuPanel ItemsSource="{Binding Units}" Margin="5">
<ctrl:SudokuPanel.ItemTemplate>
<DataTemplate>
<ctrl:SudokuGroup ItemsSource="{Binding Children}">
<ctrl:SudokuGroup.ItemTemplate>
<DataTemplate>
<ctrl:SudokuItem Content="2"/>
</DataTemplate>
</ctrl:SudokuGroup.ItemTemplate>
</ctrl:SudokuGroup>
</DataTemplate>
</ctrl:SudokuPanel.ItemTemplate>
</ctrl:SudokuPanel>
我给SudokuGroup添加了一个触发器,整个SudokuGroup背景色都变成了红色,这让我更加觉得是没有生成SudokuItem,而不是SudokuItem“藏”起来了。
<Trigger Property="HasItems" Value="False">
<Setter Property="Background" Value="Red"/>
</Trigger>
其他代码:
/// <summary>
/// 数独单元格样式选择
/// </summary>
public class SudokuItemStyleSelector : StyleSelector
{
/// <summary>
/// 当在派生类中重写返回 <see cref="Style"/> 基于自定义逻辑。
/// </summary>
/// <param name="item">使用的内容</param>
/// <param name="container">将向其应用样式的元素</param>
/// <returns></returns>
public override Style SelectStyle(object item, DependencyObject container)
{
ItemsControl group = ItemsControl.ItemsControlFromItemContainer(container);
int index = group.ItemContainerGenerator.IndexFromContainer(container);
return (Style)Application.Current.TryFindResource($"SudokuItem.{index}");
}
}
/// <summary>
/// 数独九宫格样式选择
/// </summary>
public class SudokuGroupStyleSelector : StyleSelector
{
/// <summary>
/// 当在派生类中重写返回 <see cref="Style"/> 基于自定义逻辑。
/// </summary>
/// <param name="item">使用的内容</param>
/// <param name="container">将向其应用样式的元素</param>
/// <returns></returns>
public override Style SelectStyle(object item, DependencyObject container)
{
ItemsControl panel = ItemsControl.ItemsControlFromItemContainer(container);
int index = panel.ItemContainerGenerator.IndexFromContainer(container);
return (Style)Application.Current.TryFindResource($"SudokuGroup.{index}");
}
}
/// <summary>
/// 按照参数将Thickness放大指定倍数
/// </summary>
public class ThicknessSplitConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Thickness thickness)
{
if (parameter is string str)
{
string[] array = str.Split(',');
if (array.Length == 1)
array = new string[4] { array[0], array[0], array[0], array[0] };
else if (array.Length != 4)
return thickness;
Thickness result = new Thickness(thickness.Left, thickness.Top, thickness.Right, thickness.Bottom);
if (double.TryParse(array[0], out double leftTimes))
result.Left = leftTimes * thickness.Left;
if (double.TryParse(array[1], out double topTimes))
result.Top = topTimes * thickness.Top;
if (double.TryParse(array[2], out double rightTimes))
result.Right = rightTimes * thickness.Right;
if (double.TryParse(array[3], out double bottomTimes))
result.Bottom = bottomTimes * thickness.Bottom;
return result;
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
这是SudokuItem相关代码:
/// <summary>
/// 表示中选择的项目 <see cref="SudokuGroup"/>
/// </summary>
public class SudokuItem : Button
{
/// <summary>
/// 表示中选择的项目 <see cref="SudokuGroup"/>
/// </summary>
static SudokuItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SudokuItem), new FrameworkPropertyMetadata(typeof(SudokuItem)));
}
#region BorderThick
/// <summary>
/// 边框厚度
/// </summary>
/// <remarks>中间变量,使用<see cref="Border.BorderThickness"/></remarks>
public static readonly DependencyProperty BorderThickProperty =
DependencyProperty.Register("BorderThick", typeof(Thickness), typeof(SudokuItem), new PropertyMetadata(new Thickness()));
/// <summary>
/// 边框厚度
/// </summary>
/// <remarks>中间变量,使用<see cref="Border.BorderThickness"/></remarks>
public Thickness BorderThick
{
get { return (Thickness)GetValue(BorderThickProperty); }
set { SetValue(BorderThickProperty, value); }
}
#endregion
}
<convert:ThicknessSplitConverter x:Key="ThicknessSplitConverter"/>
<!--数独单元格 样式-->
<Style x:Key="SudokuItem.Base" TargetType="local:SudokuItem">
<Setter Property="Padding" Value="5"/>
<Setter Property="BorderBrush" Value="{DynamicResource Sudoku_Item_BorderBrush}"/>
<Setter Property="BorderThick" Value="{DynamicResource Sudoku_Item_BorderThickness}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource Sudoku_Item_Prefedined_Foreground}"/>
<Setter Property="TextBlock.FontSize" Value="{DynamicResource Sudoku_Item_Value_FontSize}"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SudokuItem">
<Grid>
<Border x:Name="border" Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<Rectangle x:Name="rectangle" Fill="Transparent" RadiusX="{DynamicResource Sudoku_Double_Radius}" RadiusY="{DynamicResource Sudoku_Double_Radius}" Visibility="Collapsed"/>
</Border>
<ContentPresenter Content="{TemplateBinding Content}" TextBlock.Foreground="{TemplateBinding TextBlock.Foreground}" TextBlock.FontSize="{TemplateBinding TextBlock.FontSize}" TextBlock.FontWeight="Black" HorizontalAlignment="Center" VerticalAlignment="Center" IsHitTestVisible="False"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Margin" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:SudokuItem},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='-2'}"/>
<Setter TargetName="border" Property="Border.CornerRadius" Value="{DynamicResource Sudoku_Radius}"/>
<Setter TargetName="border" Property="Border.BorderBrush" Value="#1a80b3"/>
<Setter TargetName="border" Property="Border.BorderThickness" Value="{DynamicResource Sudoku_Parent_BorderThickness}"/>
<Setter TargetName="rectangle" Property="Fill" Value="#6bab21"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource Sudoku_Item_Value_Foreground}"/>
<Setter TargetName="rectangle" Property="Visibility" Value="Visible"/>
<Setter TargetName="border" Property="Border.BorderBrush" Value="{DynamicResource Sudoku_Parent_BorderBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--<Style BasedOn="{StaticResource SudokuItem.Base}" TargetType="local:SudokuItem"/>-->
<!--数独单元格 左上角 样式-->
<Style x:Key="SudokuItem.0" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='0,0,1,1'}"/>
</Style>
<!--数独单元格 上部 样式-->
<Style x:Key="SudokuItem.1" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1,0,1,1'}"/>
</Style>
<!--数独单元格 右上角 样式-->
<Style x:Key="SudokuItem.2" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1,0,0,1'}"/>
</Style>
<!--数独单元格 左中角 样式-->
<Style x:Key="SudokuItem.3" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='0,1,1,1'}"/>
</Style>
<!--数独单元格 中部 样式-->
<Style x:Key="SudokuItem.4" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1'}"/>
</Style>
<!--数独单元格 右中角 样式-->
<Style x:Key="SudokuItem.5" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1,1,0,1'}"/>
</Style>
<!--数独单元格 左下角 样式-->
<Style x:Key="SudokuItem.6" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='0,1,1,0'}"/>
</Style>
<!--数独单元格 下部 样式-->
<Style x:Key="SudokuItem.7" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1,1,1,0'}"/>
</Style>
<!--数独单元格 右下角 样式-->
<Style x:Key="SudokuItem.8" TargetType="local:SudokuItem" BasedOn="{StaticResource SudokuItem.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1,1,0,0'}"/>
</Style>
这是SudokuGroup相关代码:
/// <summary>
/// 数独九宫格
/// </summary>
public class SudokuGroup : ItemsControl
{
/// <summary>
/// 数独九宫格
/// </summary>
static SudokuGroup()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SudokuGroup), new FrameworkPropertyMetadata(typeof(SudokuGroup)));
}
#region BorderThick
/// <summary>
/// 边框厚度
/// </summary>
/// <remarks>中间变量,使用<see cref="Border.BorderThickness"/></remarks>
public static readonly DependencyProperty BorderThickProperty =
DependencyProperty.Register("BorderThick", typeof(Thickness), typeof(SudokuGroup), new PropertyMetadata(new Thickness()));
/// <summary>
/// 边框厚度
/// </summary>
/// <remarks>中间变量,使用<see cref="Border.BorderThickness"/></remarks>
public Thickness BorderThick
{
get { return (Thickness)GetValue(BorderThickProperty); }
set { SetValue(BorderThickProperty, value); }
}
#endregion
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
base.OnItemsSourceChanged(oldValue, newValue);
}
/// <summary>
/// 确定指定的项 (或是否可以作为) 自己 ItemContainer。
/// </summary>
/// <param name="item">指定的项</param>
/// <returns> true 如果该项是其自己 ItemContainer; 否则为 false</returns>
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is SudokuItem;
}
/// <summary>
/// 创建或标识用于显示指定的项的元素
/// </summary>
/// <returns></returns>
protected override DependencyObject GetContainerForItemOverride()
{
return new SudokuItem();
}
}
<common:SudokuItemStyleSelector x:Key="itemStyleSelector"/>
<convert:ThicknessSplitConverter x:Key="ThicknessSplitConverter"/>
<!--数独九宫格 样式-->
<Style x:Key="SudokuGroup.Base" TargetType="local:SudokuGroup">
<Setter Property="BorderBrush" Value="{DynamicResource Sudoku_Parent_BorderBrush}"/>
<Setter Property="BorderThick" Value="{DynamicResource Sudoku_Parent_BorderThickness}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="ItemContainerStyleSelector" Value="{DynamicResource itemStyleSelector}"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<UniformGrid Rows="3" Columns="3"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SudokuGroup">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<ItemsPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--数独九宫格 左上角 样式-->
<Style x:Key="SudokuGroup.0" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='0,0,0,1'}"/>
</Style>
<!--数独九宫格 上部 样式-->
<Style x:Key="SudokuGroup.1" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1,0,1,1'}"/>
</Style>
<!--数独九宫格 右上角 样式-->
<Style x:Key="SudokuGroup.2" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='0,0,0,1'}"/>
</Style>
<!--数独九宫格 左中角 样式-->
<Style x:Key="SudokuGroup.3" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='0,0,0,1'}"/>
</Style>
<!--数独九宫格 中部 样式-->
<Style x:Key="SudokuGroup.4" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1,0,1,1'}"/>
</Style>
<!--数独九宫格 右中角 样式-->
<Style x:Key="SudokuGroup.5" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='0,0,0,1'}"/>
</Style>
<!--数独九宫格 左下角 样式-->
<Style x:Key="SudokuGroup.6" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
</Style>
<!--数独九宫格 下部 样式-->
<Style x:Key="SudokuGroup.7" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource Self},Path=BorderThick,Converter={StaticResource ThicknessSplitConverter},ConverterParameter='1,0,1,0'}"/>
</Style>
<!--数独九宫格 右下角 样式-->
<Style x:Key="SudokuGroup.8" TargetType="local:SudokuGroup" BasedOn="{StaticResource SudokuGroup.Base}">
</Style>
这是SudokuPanel相关代码:
/// <summary>
/// 数独面板
/// </summary>
public class SudokuPanel : ItemsControl
{
/// <summary>
/// 数独面板
/// </summary>
static SudokuPanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SudokuPanel), new FrameworkPropertyMetadata(typeof(SudokuPanel)));
}
/// <summary>
/// 确定指定的项 (或是否可以作为) 自己 ItemContainer。
/// </summary>
/// <param name="item">指定的项</param>
/// <returns> true 如果该项是其自己 ItemContainer; 否则为 false</returns>
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is SudokuGroup;
}
/// <summary>
/// 创建或标识用于显示指定的项的元素
/// </summary>
/// <returns></returns>
protected override DependencyObject GetContainerForItemOverride()
{
return new SudokuGroup();
}
}
<!--九宫格边框颜色-->
<SolidColorBrush x:Key="Sudoku_Parent_BorderBrush" Color="#0A3043"/>
<!--九宫格边框大小-->
<Thickness x:Key="Sudoku_Parent_BorderThickness">2</Thickness>
<!--九宫格圆角半径-->
<CornerRadius x:Key="Sudoku_Radius">10</CornerRadius>
<system:Double x:Key="Sudoku_Double_Radius">10</system:Double>
<!--数独单元格边框颜色-->
<SolidColorBrush x:Key="Sudoku_Item_BorderBrush" Color="#0A3043"/>
<!--数独单元格边框大小-->
<Thickness x:Key="Sudoku_Item_BorderThickness">0.5</Thickness>
<!--数独单元格字体大小-->
<system:Double x:Key="Sudoku_Item_Value_FontSize">25</system:Double>
<!--数独单元格 默认生成的字体颜色-->
<SolidColorBrush x:Key="Sudoku_Item_Prefedined_Foreground" Color="#8F9CA3"/>
<!--数独单元格 字体颜色-->
<SolidColorBrush x:Key="Sudoku_Item_Value_Foreground" Color="#0A3043"/>
<common:SudokuGroupStyleSelector x:Key="groupStyleSelector"/>
<!--数独面板 样式-->
<Style TargetType="local:SudokuPanel">
<Setter Property="BorderBrush" Value="{DynamicResource Sudoku_Parent_BorderBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource Sudoku_Parent_BorderThickness }"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Border.CornerRadius" Value="{DynamicResource Sudoku_Radius}"/>
<Setter Property="ItemContainerStyleSelector" Value="{DynamicResource groupStyleSelector}"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<UniformGrid Rows="3" Columns="3"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SudokuPanel">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" CornerRadius="{TemplateBinding Border.CornerRadius}">
<ItemsPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>