• 【WPF】实现QQ中的分组面板


    要实现的面板的效果如下图所示:

    clip_image002 clip_image004

    一个面板打开了,其它的面板会自动收起。而且打开的面板会填充所有可用空间。那么这样的效果在WPF里应该如何实现呢?

    1. 多个面板,排成一排,感觉可以用ListBox。

    2. 然后里面的东西,点一下打开,再点一下收起。感觉就是一个Expander嘛。

    3. 一个打开,其它所有的收起。可以把Expander的IsExpanded与SelectedItem绑定。

    第一步:ListBox + Expander + Style

    上面所有的功能都可以用现有的控件做到,没有必要做个自定义控件,一个Style就可以搞定了。

    为了让代码窄一点。所以分成了几个部分。Style部分如下所示。

    <ControlTemplate x:Key="ExpandListItemTemplate" TargetType="{x:Type ListBoxItem}">
        <Border BorderThickness="1">
            <Border CornerRadius="3" Padding="2,1,2,2" BorderThickness="1" BorderBrush="#FF666670">
                <Expander Header="Header" Content="{TemplateBinding Content}" 
                          IsExpanded="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}}"/>
            </Border>
        </Border>
    </ControlTemplate>
    <Style x:Key="ExpandListItemStyle" TargetType="{x:Type ListBoxItem}">
        <Setter Property="Template" Value="{StaticResource ExpandListItemTemplate}"/>
    </Style>
    <Style x:Key="MostSimple" TargetType="{x:Type ListBox}">
        <Setter Property="SelectionMode" Value="Single"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
        <Setter Property="ItemContainerStyle" Value="{StaticResource ExpandListItemStyle}"/>
    </Style>

    主体部分如下所示。

    <ListBox Style="{StaticResource MostSimple}">
        <ListBoxItem>
            <ListBox>
                <Button Content="OK"/>
                <Button Content="Cancel"/>
                <Button Content="Yes"/>
                <Button Content="No"/>
            </ListBox>
        </ListBoxItem>
        <ListBoxItem>
            <ListBox>
                <CheckBox Content="Close"/>
                <CheckBox Content="Open"/>
                <CheckBox Content="Copy"/>
                <CheckBox Content="Paste"/>
            </ListBox>
        </ListBoxItem>
        <ListBoxItem>
            <ListBox>
                <RadioButton Content="Stay"/>
                <RadioButton Content="Leave"/>
                <RadioButton Content="Stay"/>
                <RadioButton Content="Leave"/>
            </ListBox>
        </ListBoxItem>
    </ListBox>

    我们来看一下效果。

    clip_image006

    看上去差不多了,点一下如何?

    clip_image008

    完了,打开的Expander没有填充。由于ListBoxItem默认是放在VirtualizedStackPanel中的,所以里面的Item都是向上对齐,不填充。我的第一感觉就是用个会填充的当ItemsPanel就可以了。记得DockPanel有LastChildFill的功能。试试。

    <ItemsPanelTemplate x:Key="ExpandListItemsPanelTemplate">
        <DockPanel/>
    </ItemsPanelTemplate>
    <Style x:Key="ExpandListItemStyle" TargetType="{x:Type ListBoxItem}">
        <Setter Property="DockPanel.Dock" Value="Top"/>
        <Setter Property="Template" Value="{StaticResource ExpandListItemTemplate}"/>
    </Style>
    <Style x:Key="MostSimple" TargetType="{x:Type ListBox}">
        <Setter Property="SelectionMode" Value="Single"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
        <Setter Property="ItemContainerStyle" Value="{StaticResource ExpandListItemStyle}"/>
        <Setter Property="ItemsPanel" Value="{StaticResource ExpandListItemsPanelTemplate}"/>
    </Style>

    看看效果。

    clip_image010

    寒,还没有点就展开了。早该想到的。

    想了半天如何动态地改变Last Child是哪个。但是没有想出啥好办法,一个方法是把Item一个个取出来重新放回去。这也太变态了。再想想好像也没有可用的Panel了。完了,又要写一个自定义Panel了。还好这个Panel的功能不复杂。

    这个面板要做的就是把其中的一个item填充。其它的就按其期望的最小大小放上去。顺序不能乱就可以了。

    想了一个简单的逻辑。就是LargestChildFill,找出最大的Child,让它填充剩下的所有空间。

    /// <summary>
    /// The logic is very simple. Make the largest child fill.
    /// </summary>
    public class GroupPanel : Panel
    {
        private UIElement largestChild;
        private double totalHeight;
    
        protected override Size MeasureOverride(Size availableSize)
        {
            totalHeight = 0;
            double width = 0;
    
            largestChild = null;
            foreach (UIElement child in Children)
            {
                child.Measure(availableSize);
    
                if (largestChild == null || child.DesiredSize.Height >= largestChild.DesiredSize.Height)
                {
                    largestChild = child;
                }
    
                totalHeight += child.DesiredSize.Height;
                if (child.DesiredSize.Width > width)
                {
                    width = child.DesiredSize.Width;
                }
            }
    
            return new Size(width, totalHeight);
        }
    
        protected override Size ArrangeOverride(Size finalSize)
        {
            double yOffset = 0;
            foreach (UIElement child in Children)
            {
                if (child == largestChild)
                {
                    double finalHeight = child.DesiredSize.Height + finalSize.Height - totalHeight;
                    child.Arrange(new Rect(0, yOffset, finalSize.Width, finalHeight));
                    yOffset += finalHeight;
                }
                else
                {
                    child.Arrange(new Rect(0, yOffset, finalSize.Width, child.DesiredSize.Height));
                    yOffset += child.DesiredSize.Height;
                }
            }
    
            return finalSize;
        }
    }

    然后把ItemsPanelTemplate改一下。

    <ItemsPanelTemplate x:Key="ExpandListItemsPanelTemplate"> 
        <c:GroupPanel/> 
    </ItemsPanelTemplate> 
    

    搞定。看下效果。

    clip_image012clip_image014

    就是想要的效果。目前这些面板可以全部折叠起来,如果你想让至少一个展开。一个简单的方法就是。当展开后,把Expander的Header里的ToggleButton禁用。

    再常见一些的需求就是这个面板的展开过程要有动画什么的。这里就不再演示了。

    目前为到止,写在博客里的自定义Panel就有仨了。其实我想说的是,WPF自带的几个Panel的功能实在是很基本的。 了解如何实现自定义Panel,对于实现一些常见的功能还是很有帮助的。

  • 相关阅读:
    debian安装后sudo不能用的问题
    分组筛选数据,某个数据字段比如说患者就诊超过一次的,身份证号会重复出现的,sql语句,备忘用的
    《精通Git》翻译系列(二)
    配置mysql允许远程连接
    raspberry pi下使用mp3blaster播放mp3音乐
    MSSQL常用语句备注1
    设计的四大原则
    在树莓派下安装mysql的相关查询记录
    查看数据库日志,收缩数据库
    芒果功效与作用
  • 原文地址:https://www.cnblogs.com/nankezhishi/p/expandablePanel.html
Copyright © 2020-2023  润新知