WPF 自定义ItemsControl三层嵌套无法生成第三层控件

Dear200892 2023-11-07 10:19:54

我在尝试制作一个数独的小游戏,我自定义了三个控件,分别是:SudokuPanel,SudokuGroup和SudokuItem。

SudokuPanel:作为数独表格的主面板区域,继承ItemsControl,限制了Item项为SudokuGroup。

SudokuGroup:数独9*9的区域,继承ItemsControl,限制了Item项为SudokuItem。

SudokuItem:数独的单元格,继承Button。

在ViewModel定义了属性ObservableCollection<Unit> Units,Unit对象含有ObservableCollection<Cell> Children属性,两个类均实现了INotifyPropertyChanged接口。

现在运行程序,只会显示到SudokuGroup层级,不明白为什么会这样?该如何修改?

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

 

...全文
687 4 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
Dear200892 2023-11-07
  • 打赏
  • 举报
回复

其他代码:

/// <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();
        }
    }
Dear200892 2023-11-07
  • 打赏
  • 举报
回复

这是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>
Dear200892 2023-11-07
  • 打赏
  • 举报
回复

这是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>


Dear200892 2023-11-07
  • 打赏
  • 举报
回复

这是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>

8,755

社区成员

发帖
与我相关
我的任务
社区描述
WPF/Silverlight相关讨论
社区管理员
  • WPF/Silverlight社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧