SharedSizeGroup及Collapsed属性分析及窗口隐藏

来源:互联网 发布:惠普网络驱动官方下载 编辑:程序博客网 时间:2024/06/01 08:25

SharedSizeGroup及Collapsed属性分析及窗口隐藏

tags: wpf C# XAML


由于设计需要,要实现一个类似《WPF揭秘》里的一个布局实例,先把最终需要实现的效果图贴上
281458002154674.gif-685kB
图片来源:WPF布局 - ShengM - 博客园

这里面贴出了所有实例代码,但是没有具体分析,由于是新手,折腾了一上午才搞清楚里面的来龙去脉,这里记录一下。

起初需要实现的功能如图中紫色部分一样,当Toolbox窗口关闭后可以自动占据其布局位置,同时当Toolbox窗口出现后,其可以自动让出其布局位置。在网上搜了很多,大家都说把需要隐藏对的窗口的Visibility设置为Collapsed就可以实现窗口隐藏,其余窗口自动占位的效果。于是试了一下,发现不行……然后在网上找到了这个布局实例以及其源码,对其进行了分析,找到之前失败的原因。

首先,把之前失败的布局代码贴出来:

<Grid>    <Grid.RowDefinitions>        <RowDefinition/>        <RowDefinition/>        <RowDefinition/>        <RowDefinition/>    </Grid.RowDefinitions>    <Button Grid.RowSpan="1" Content="Button" Margin="0,0,0,0" />    <TabControl Grid.Row="1" Margin="0,0,0,0" TabStripPlacement="Right" Style="{DynamicResource TabControlStyleRotate}">        <TabItem Content="111111" Style="{DynamicResource TabItemStyleRotate}" Header="About"/>        <TabItem Header="About" Content="222222" Style="{DynamicResource TabItemStyleRotate2}"/>    </TabControl>    <Border Grid.Row="2" Background="AliceBlue">        <TextBlock Text="bbbbb&#13;bbb&#13;bbb&#13;bbb&#13;bbb&#13;bbbba&#13;bbbb"/>    </Border>    <Border Grid.Row="3" Visibility="Collapsed">        <TextBlock Text="aaaaa&#13;aaaaaa&#13;aaaaaaaaa&#13;aaaaaaa"/>    </Border></Grid>

本来期望第三行能自动填补第四行的空缺的,结果却是这样……
1506059368.png-26.6kB
如果是有经验的开发人员……应该一眼就能看出问题,这里主要是给像我一样的新手来说明一下。
首先看一下MSDN对Collapsed的解释:

成员名称 说明 Collapsed 不显示元素,并且不在布局中为它保留空间 Hidden 不显示元素,但是在布局中为元素保留空间 Visible 显示元素

所谓不在不居中为它保留空间,是指将该元素的尺寸当成了0,仅此而已。这里我之前就有一点误区,认为该元素的尺寸若为零,则其他元素会自动来对其进行补位,这显然是不成立的,因为在Grid布局中划分了网格区域后,所有空间将只会出现在对应的网格中,除非使用RowSpan这类属性来跨越多个网格部署。(若不指定网格,默认都出现在第一格中)
在布局中什么情况下才会补位?就目前我所知道的是只有在布局中,某个元素拥有“填充满剩余空间”这样的属性时才会自动补位,其实也就是执行其自身的“填充满剩余空间”这个属性。当然这个属性只是我这么称呼他。

目前来说,我常用到的元素中拥有“填充满剩余空间”这个属性的元素是“DockPanel”,还有没有其他的暂时不清楚。对于“StackPanel”这种布局,当把其中的某一个元素进行Collapsed后,其后面的元素会进行补位,但是不会进行伸缩填充,Collapsed前后效果如下图:
1506064053(1).png-8.2kB
20170922150824.png-7.8kB
这里将bbb进行Collapsed,这时ccc顶替了bbb的区域。但在“DockPanel”布局中,对于最后的那个元素的行为将是填充满剩余空间,所以想要实现本文第一个图中的效果必须使用“DockPanel”布局。下面将第一个图的XAML及C#源码贴出来,然后再对其实现进行分析。
首先是XAML代码:

<Window x:Class="VSUIDemo.MainWindow"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        Title="MainWindow">    <DockPanel>        <Menu DockPanel.Dock="Top">            <MenuItem Header="File" />            <MenuItem Header="Edit" />            <MenuItem Header="View" />            <MenuItem Header="Project" />            <MenuItem Header="Build" />            <MenuItem Header="Debug" />            <MenuItem Header="Team" />            <MenuItem Header="Tool" />            <MenuItem Header="Test" />            <MenuItem Header="Structure" />            <MenuItem Header="Analysis" />            <MenuItem Header="Window" />            <MenuItem Header="Help" />        </Menu>        <StackPanel DockPanel.Dock="Right" Orientation="Horizontal">            <StackPanel.LayoutTransform>                <RotateTransform Angle="90" />            </StackPanel.LayoutTransform>            <Button x:Name="toolboxButton"                    Content="Toolbox"                    MouseEnter="toolboxButton_MouseEnter" />            <Button x:Name="solutionButton"                    Margin="2,0"                    Content="Solution Explorer"                    MouseEnter="solutionButton_MouseEnter" />        </StackPanel>        <Grid Grid.IsSharedSizeScope="True">            <Grid x:Name="layer0Grid"                  Panel.ZIndex="0"                  MouseEnter="layer0Grid_MouseEnter">                <Grid.RowDefinitions>                    <RowDefinition />                    <RowDefinition />                    <RowDefinition />                    <RowDefinition />                </Grid.RowDefinitions>                <Grid.ColumnDefinitions>                    <ColumnDefinition Width="Auto" />                    <ColumnDefinition />                </Grid.ColumnDefinitions>                <Border Grid.ColumnSpan="2" Background="BlueViolet">                    <TextBlock HorizontalAlignment="Center"                               VerticalAlignment="Center"                               FontSize="36"                               Text="Start Page" />                </Border>                <GroupBox Grid.Row="1"                          Margin="2"                          BorderThickness="2"                          Header="Recent Projects">                    ...                </GroupBox>                <GroupBox Grid.Row="1"                          Grid.RowSpan="3"                          Grid.Column="1"                          Margin="2"                          BorderThickness="2"                          Header="Online Articles">                    <ListBox>                        <ListBoxItem Content="Article #1" />                        <ListBoxItem Content="Article #2" />                        <ListBoxItem Content="Article #3" />                        <ListBoxItem Content="Article #4" />                    </ListBox>                </GroupBox>                <GroupBox Grid.Row="2"                          Margin="2"                          BorderThickness="2"                          Header="Getting Started">                    ...                </GroupBox>                <GroupBox Grid.Row="3"                          Margin="2"                          BorderThickness="2"                          Header="Headlines">                    ...                </GroupBox>            </Grid>            <Grid x:Name="toolboxLayerGrid" Visibility="Collapsed">                <Grid.ColumnDefinitions>                    <ColumnDefinition />                    <ColumnDefinition Width="Auto" SharedSizeGroup="ToolboxGroup" />                </Grid.ColumnDefinitions>                <GridSplitter Grid.Column="1"                              Width="3"                              HorizontalAlignment="Left" />                <Grid x:Name="toolboxGrid"                      Grid.Column="1"                      Margin="3,0,0,0">                    <Grid.RowDefinitions>                        <RowDefinition Height="Auto" />                        <RowDefinition />                    </Grid.RowDefinitions>                    <Grid Background="LightBlue">                        <Grid.ColumnDefinitions>                            <ColumnDefinition />                            <ColumnDefinition Width="35" />                        </Grid.ColumnDefinitions>                        <TextBlock VerticalAlignment="Center"                                   FontSize="18"                                   Text="Toolbox"                                   TextTrimming="CharacterEllipsis" />                        <Button x:Name="toolboxLayerPinButton"                                Grid.Column="1"                                Click="toolboxLayerPinButton_Click">                            <Image x:Name="toolboxImage"                                   Width="24"                                   Height="24"                                   Source="Resource/Image/pin_float.png" />                        </Button>                    </Grid>                    <ListBox Grid.Row="1" FontSize="16">                        <ListBoxItem Content="Button" />                        <ListBoxItem Content="CheckBox" />                        <ListBoxItem Content="Label" />                        <ListBoxItem Content="ComboBox" />                        <ListBoxItem Content="ListBox" />                    </ListBox>                </Grid>            </Grid>            <Grid x:Name="solutionLayerGrid" Visibility="Collapsed">                <Grid.ColumnDefinitions>                    <ColumnDefinition />                    <ColumnDefinition Width="Auto" SharedSizeGroup="SolutionGroup" />                </Grid.ColumnDefinitions>                <GridSplitter Grid.Column="1"                              Width="3"                              HorizontalAlignment="Left" />                <Grid x:Name="solutionGrid"                      Grid.Column="1"                      Margin="3,0,0,0">                    <Grid.RowDefinitions>                        <RowDefinition Height="Auto" />                        <RowDefinition Height="Auto" />                        <RowDefinition />                    </Grid.RowDefinitions>                    <Grid Background="LightBlue">                        <Grid.ColumnDefinitions>                            <ColumnDefinition />                            <ColumnDefinition Width="35" />                        </Grid.ColumnDefinitions>                        <TextBlock VerticalAlignment="Center"                                   FontSize="18"                                   Text="Solution Explorer"                                   TextTrimming="CharacterEllipsis" />                        <Button x:Name="solutionLayerPinButton"                                Grid.Column="1"                                Click="solutionLayerPinButton_Click">                            <Image x:Name="solutionImage"                                   Width="24"                                   Height="24"                                   Source="Resource/Image/pin_float.png" />                        </Button>                    </Grid>                    <Border Grid.Row="1" Background="White">                        <ToolBar>                            <Image Source="Resource/Image/copy.png" />                            <Image Margin="2,0" Source="Resource/Image/paste.png" />                            <Image Margin="2,0" Source="Resource/Image/refresh.png" />                        </ToolBar>                    </Border>                    <TreeView Grid.Row="2">                        <TreeViewItem Header="My Solution">                            <TreeViewItem Header="Project #1" />                            <TreeViewItem Header="Project #2" />                            <TreeViewItem Header="Project #3" />                        </TreeViewItem>                    </TreeView>                </Grid>            </Grid>        </Grid>    </DockPanel></Window>

一段段来分析吧……
首先<Window></Window>这个就不说了……
然后是

<DockPanel>    <Menu></Menu>    <StackPanel></StackPanel>    <Grid></Grid></DockPanel>

这个里面就是一个基本布局了,布局完了之后大致形状应该是酱紫:
微信截图_20170922152501.png-36.4kB
其中1是目录菜单,2是侧边按钮,3是中间的内容,这样,整个内容部分将是一个具有“填充满剩余空间”属性的区域。
其中1和2区域比较简单,就不分析了。
主要来看看3号区域,3号区域代码展开:

<Grid Grid.IsSharedSizeScope="True">    <Grid x:Name="layer0Grid" Panel.ZIndex="0" MouseEnter="layer0Grid_MouseEnter"></Grid>    <Grid x:Name="toolboxLayerGrid" Visibility="Collapsed"></Grid>    <Grid x:Name="solutionLayerGrid" Visibility="Collapsed"></Grid><Grid/>

OK,玄机来了……起初一看,这不是操蛋的写法么?!!就我目前的认知水平,如果有一段这样的代码:

    <Grid>        <Grid>            <TextBlock Text="1111111111"/>        </Grid>        <Grid>            <TextBlock Text="222222222"/>        </Grid>        <Grid>            <TextBlock Text="33333333"/>        </Grid>    </Grid>

这是什么鬼……显示出来如下:
微信截图_20170922153810.png-10.7kB
可是上面的代码明明就是这样写的,为什么那几个框没有这样重叠?这里就是这个布局的一个技巧了,也是这个技巧造就了隐藏Toolbox窗口后,紫色界面立马填充满的“假象”。之所以说是“假象”,下面就来分析。
先来看layer0Grid这个布局:

<Grid x:Name="layer0Grid">    <Grid.RowDefinitions>        <RowDefinition />        <RowDefinition />        <RowDefinition />        <RowDefinition />    </Grid.RowDefinitions>    <Grid.ColumnDefinitions>        <ColumnDefinition Width="Auto" />        <ColumnDefinition />    </Grid.ColumnDefinitions>    <!--元素1-->    <Border Grid.ColumnSpan="2" Background="BlueViolet"></Border>    <!--元素2-->    <GroupBox Grid.Row="1"></GroupBox>    <!--元素3-->    <GroupBox Grid.Row="1" Grid.RowSpan="3" Grid.Column="1"></GroupBox>    <!--元素4-->    <GroupBox Grid.Row="2"></GroupBox>    <!--元素5-->    <GroupBox Grid.Row="3"></GroupBox></Grid>

这里只分析主要代码,具体内容比较简单就直接忽略了。这里把3号区域分成4行2列的布局,其中最上面的紫色区域占用2列,布局完后,3号区域应该如图所示:
微信截图_20170922155135.png-53.3kB
其中1,2,3,4,5对应的是代码中注释的“元素x”。这个时候原来的3号区域已经被占满了,如果再在3号区域增加toolboxLayerGrid和solutionLayerGrid两个布局,则都将与layer0Grid布局发生重叠,但这里所利用的恰好是布局重叠。再来分析toolboxLayerGrid和solutionLayerGrid,这里只分析toolboxLayerGrid,solutionLayerGrid与toolboxLayerGrid是一个道理。

<Grid x:Name="toolboxLayerGrid" Visibility="Collapsed">    <Grid.ColumnDefinitions>        <ColumnDefinition />        <ColumnDefinition Width="Auto" SharedSizeGroup="ToolboxGroup" />    </Grid.ColumnDefinitions>    <GridSplitter Grid.Column="1" Width="3" HorizontalAlignment="Left" />    <Grid x:Name="toolboxGrid" Grid.Column="1" Margin="3,0,0,0"></Grid></Grid>

在这里,可以看到toolboxLayerGrid里面其实将3号区域分成了1行2列的形状,在第二列中有两个东西,一个是GridSplitter,一个是toolboxGrid,toolboxGrid中的Margin左边留3,是为了绘制GridSplitter留的空间,GridSplitter是手动调整窗口大小用的,具体用法这里不单独说。从这里可以看出,toolboxLayerGrid这个布局分成两列的良苦用心,它让真正的toolboxGrid暂用最右边的位置,toolboxGrid的宽度根据toolboxGrid中实际内容的宽度而定,但是3号区域在“DockPanel”中必须填充满所有剩余区域,所以这里把本来1列就可以表述完的toolboxGrid封装成了一个两列的toolboxLayerGrid,toolboxLayerGrid中的第一列的作用就是占位。toolboxLayerGrid与layer0Grid叠加后应为如下视图:
微信截图_20170922160748.png-93.4kB
其中蓝色部分为toolboxLayerGrid。一开始,toolboxLayerGrid的属性被设备为了Collapsed,所以,整个蓝色区域没有显示。但一旦将toolbox固定在屏幕上时,蓝色区域将被显示出来,这时,问题来了,layer0Grid将有一部分被toolboxGrid挡住,而不是我们看在第一个图中看到额当toolbox显示后,紫色区域缩回去一截那个效果。如果要实现缩回去的效果就要对layer0Grid做些手脚,这也是前面所说的“假象”的原因。
考虑这样一个问题,若layer0Grid变成这样:
微信截图_20170922161355.png-75kB
将原来本是两列的layer0Grid,增加一列,变成3列,也就是增加了一个6号区域,这个时候原来的元素1、元素3都会都会自动变小。前提是增加的一列是有固定宽度值的,否则一个没有内容又没有宽度值得列将直接被不显示了。
增加了6号区域后,若6号区域的大小正好等于toolboxGrid,这样就可以让toolboxLayerGrid和layer0Grid叠加后互不干扰了!
微信截图_20170922161804.png-99.1kB
再考虑一个更有趣的情况,若6号区域是一个能动态存在的区域,即:当toolboxGrid存在时,layer0Grid才有6号区域,否则layer0Grid没有6号区域,这样不就同时解决了两个问题么?

  • 两个Grid叠加遮挡问题
  • 紫色区域自动伸缩问题(当有6号区域时,1号区域变小,没有6号区域时1号区域自动占用6号区域的位置)

那么现在唯一的问题就是,如何动态创建和删除6号区域了,这部分代码在C#代码里:

public partial class MainWindow : Window{    private ColumnDefinition _cloneToolboxGrid;    private ColumnDefinition _cloneSolutionGrid;    private ColumnDefinition _cloneToToolboxLayerGrid;    public MainWindow()    {        InitializeComponent();        _cloneToolboxGrid = new ColumnDefinition { SharedSizeGroup = "ToolboxGroup" };        _cloneSolutionGrid = new ColumnDefinition { SharedSizeGroup = "SolutionGroup" };        _cloneToToolboxLayerGrid = new ColumnDefinition { SharedSizeGroup = "SolutionGroup" };    }    private void toolboxButton_MouseEnter(object sender, MouseEventArgs e)    {        toolboxLayerGrid.Visibility = System.Windows.Visibility.Visible;        toolboxLayerGrid.SetValue(Grid.ZIndexProperty, 2);        if (solutionButton.Visibility == System.Windows.Visibility.Visible)            solutionLayerGrid.Visibility = System.Windows.Visibility.Collapsed;        else            solutionLayerGrid.SetValue(Grid.ZIndexProperty, 1);    }    private void solutionButton_MouseEnter(object sender, MouseEventArgs e)    {        solutionLayerGrid.Visibility = System.Windows.Visibility.Visible;        solutionLayerGrid.SetValue(Grid.ZIndexProperty, 2);        if (toolboxButton.Visibility == System.Windows.Visibility.Visible)            toolboxLayerGrid.Visibility = System.Windows.Visibility.Collapsed;        else            toolboxLayerGrid.SetValue(Grid.ZIndexProperty, 1);    }    private void layer0Grid_MouseEnter(object sender, MouseEventArgs e)    {        if (toolboxButton.Visibility == System.Windows.Visibility.Visible)            toolboxLayerGrid.Visibility = System.Windows.Visibility.Collapsed;        if (solutionButton.Visibility == System.Windows.Visibility.Visible)            solutionLayerGrid.Visibility = System.Windows.Visibility.Collapsed;    }    private void toolboxLayerPinButton_Click(object sender, RoutedEventArgs e)    {        if (toolboxButton.Visibility == System.Windows.Visibility.Visible)        {            toolboxImage.Source = new BitmapImage(new Uri("Resource/Image/k1.png", UriKind.Relative));            toolboxButton.Visibility = System.Windows.Visibility.Collapsed;            layer0Grid.ColumnDefinitions.Add(_cloneToolboxGrid);            if (solutionButton.Visibility == System.Windows.Visibility.Collapsed)                toolboxLayerGrid.ColumnDefinitions.Add(_cloneToToolboxLayerGrid);        }        else        {            toolboxImage.Source = new BitmapImage(new Uri("Resource/Image/k1.png", UriKind.Relative));            toolboxButton.Visibility = System.Windows.Visibility.Visible;            toolboxLayerGrid.Visibility = System.Windows.Visibility.Collapsed;            layer0Grid.ColumnDefinitions.Remove(_cloneToolboxGrid);            if (solutionButton.Visibility == System.Windows.Visibility.Collapsed)                toolboxLayerGrid.ColumnDefinitions.Remove(_cloneToToolboxLayerGrid);        }    }    private void solutionLayerPinButton_Click(object sender, RoutedEventArgs e)    {        if (solutionButton.Visibility == System.Windows.Visibility.Visible)        {            solutionImage.Source = new BitmapImage(new Uri("Resource/Image/k1.png", UriKind.Relative));            solutionButton.Visibility = System.Windows.Visibility.Collapsed;            layer0Grid.ColumnDefinitions.Add(_cloneSolutionGrid);            if (toolboxButton.Visibility == System.Windows.Visibility.Collapsed)                toolboxLayerGrid.ColumnDefinitions.Add(_cloneToToolboxLayerGrid);        }        else        {            solutionImage.Source = new BitmapImage(new Uri("Resource/Image/k1.png", UriKind.Relative));            solutionButton.Visibility = System.Windows.Visibility.Visible;            solutionLayerGrid.Visibility = System.Windows.Visibility.Collapsed;            layer0Grid.ColumnDefinitions.Remove(_cloneSolutionGrid);            if (toolboxButton.Visibility == System.Windows.Visibility.Collapsed)                toolboxLayerGrid.ColumnDefinitions.Remove(_cloneToToolboxLayerGrid);        }    }}

其中layer0Grid.ColumnDefinitions.Add(_cloneToolboxGrid)就是增加6号区域的代码,这里的代码逻辑比较简单,完全是根据布局来的,思路跟6号区域完全一致,这个代码就不再做分析了。

最后来说一下SharedSizeGroup属性。该属性的意思其实就是所有SharedSizeGroup属性值相同的区域拥有相同的大小,大小值为:所有SharedSizeGroup属性相同区域中大小最大的那个区域。
换句话说,若有A和B两个布局,如果他们两个布局的SharedSizeGroup属性相同,都为某值“aaa”(其实“aaa”没有规则,只要符合命名规则就行,你爱叫什么叫什么,只要希望共享size的区域的该名称是一样的就行),则A和B有相同的大小,两个布局谁大就用谁的大小。

所以,这里就可以回答刚刚的一个没有回答的问题,怎么使6号区域与toolboxGrid一样大?现在答案就很简单了,那就是让6号区域和toolboxGrid的SharedSizeGroup属性相同即可,所以layer0Grid.ColumnDefinitions.Add(_cloneToolboxGrid)中会带有 _cloneToolboxGrid参数,这个参数就是toolboxGrid的SharedSizeGroup属性值。

至此,应该差不多把第一个图中演示的实例的实现过程主脉络分析清楚了,剩下的东西就不再分析了。如果有哪里说得不对的,欢迎指正。

原创粉丝点击