UWP开发入门(五)——自定义Panel

  各位好,终于讲到自定义Panel了。当系统自带的几个Panel比如Gird,StackPanel,RelativePanel不能满足我们的特定要求时(其实不常见啦),自定义Panel就显得非常必要,而且因为是针对性的处理,效果也会非常好。更何况自定义Panel其实并不复杂,今天俺们就来学习一下。

  记得上一篇自定义CommandBar在增加占位控件AppBarEmpty时,采用的是通过Page的SizeChanged事件中计算页面Width,减去CommandBar中其他控件Width后再赋值Width给AppBarEmpty的方法。就可行性而言是绝对没问题的,代码复杂度也很低,不失为一个好方法。但是复用性不太好,需要在每个Page都写上一小段代码。而我们的初衷是希望AppBarEmpty能够自动撑开,计算自身所需的Width。

  遇到的困难来自StackPanel这个控件,StackPanel在计算自身所需空间时,会非常吝啬按children元素所需的最小尺寸来申请。就好比申请经费时按最下限申请,这种精神ZF部门应该学习,而公司组织TeamBuilding就应该排斥……)。说到这里,本篇的主题有了,就是通过自定义一个StackPanelEx来实现让AppBarEmpty自动撑开的效果。

  前面说了,自定义Panel其实并不复杂。只有两个方法需要override:

        //
        // Summary:(根据子元素测量控件本身需要的空间)
        //     Provides the behavior for the Measure pass of the layout cycle. Classes can override
        //     this method to define their own Measure pass behavior.
        //
        // Parameters:
        //   availableSize:(控件本身的可用空间。如指定无穷大值,表示控件的大小将调整为内容的可用大小)
        //     The available size that this object can give to child objects. Infinity can be
        //     specified as a value to indicate that the object will size to whatever content
        //     is available.
        //
        // Returns:(控件根据子元素大小计算得出的所需大小)
        //     The size that this object determines it needs during layout, based on its calculations
        //     of the allocated sizes for child objects or based on other considerations such
        //     as a fixed container size.
        protected virtual Size MeasureOverride(Size availableSize);
        //
        // Summary:(根据上面测量的结果,来对子元素进行布局)
        //     Provides the behavior for the Arrange pass of layout. Classes can override this
        //     method to define their own Arrange pass behavior.
        //
        // Parameters:
        //   finalSize:(控件用来排列自身及其子元素的最终确定的空间)
        //     The final area within the parent that this object should use to arrange itself
        //     and its children.
        //
        // Returns:(使用的实际大小)
        //     The actual size that is used after the element is arranged in layout.
        protected virtual Size ArrangeOverride(Size finalSize);

  听上去是不是挺绕的?我们来看StackPanelEx的实际代码:

    public class StackPanelEx : Panel
    {
        protected override Size MeasureOverride(Size availableSize)
        {
         double height = 0;
            foreach (var child in Children)
            {
                // Tell the child control to determine the size needed
                child.Measure(availableSize);
                height = child.DesiredSize.Height > height ? child.DesiredSize.Height : height;
            }

            return new Size(availableSize.Width,height);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            int count = Children.Count(_ => _ is AppBarEmpty2);
            double totalLength = Children.Where(_ => _ is AppBarEmpty2 == false).Sum(_ => (_ as FrameworkElement).Width);
            var emptyWidth = (finalSize.Width - totalLength) / count;

            double x = 0;
            foreach (var child in Children)
            {
                if (child is AppBarEmpty2)
                {
                    child.Arrange(new Rect(x, 0, emptyWidth, 0));
                    x += emptyWidth;
                }
                else
                {
                    child.Arrange(new Rect(x, 0, child.DesiredSize.Width, child.DesiredSize.Height));
                    x += child.DesiredSize.Width;
                }
            }
            return finalSize;
        }
    }

  简单解释一下,首先在MeasureOverride方法里,告诉每个子元素(这里是AppBarButton和AppBarEmpty2)去算自己需要多少空间,一会要分地了。然后直接告诉上头,Width我全要了,Height按我们村里最高的人给就行了。

  紧接着到了ArrangeOverride方法,上级领导比较大方,告诉该控件Width全给你,还有你Height要的太少,拖了上级Panel的后腿,多给一些Height免得挤到同级的其他控件……

  然后村长及开始分地了。AppBarButton就按他自己申请的给,AppBarEmpty2感觉是村长亲戚,剩下的Width全给他们家承包了……

  最后向上头汇报一下分地的情况,这事就算完了。

  AppBarEmpty2还是没变,和上次一样。

    public class AppBarEmpty2 : FrameworkElement, ICommandBarElement
    {
        public bool IsCompact { get; set; }
    }

  既然新的StackPanelEx写好了,就该替换CommandBar原有的StackPanel了,找到CommandBar模板中关于PrimaryItemsControl的部分

                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <ContentControl x:Name="ContentControl" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsTabStop="False" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                    <ItemsControl x:Name="PrimaryItemsControl" Grid.Column="0"
                                                  HorizontalAlignment="Stretch"  IsTabStop="False" MinHeight="{ThemeResource AppBarThemeMinHeight}">
                                        <ItemsControl.ItemsPanel>
                                            <ItemsPanelTemplate>
                                                <!--<StackPanel Orientation="Horizontal"/>-->
                                                <local:StackPanelEx ></local:StackPanelEx>
                                            </ItemsPanelTemplate>
                                        </ItemsControl.ItemsPanel>
                                    </ItemsControl>
                                </Grid>

  将ItemsPanelTemplate中的StackPanel替换成StackPanelEx,同时将ItemsControl的HorizontalAlignment="Right"改成HorizontalAlignment="Stretch",大功告成,再来看一下Page中的XAML部分:

       <CommandBar x:Name="commandBar" Grid.Row="3" Style="{StaticResource CommandBarStyle2}" >
            <AppBarButton x:Name="appbarButton" Icon="Accept" Label="fdsfdsf" ></AppBarButton>
            <local:AppBarEmpty2  ></local:AppBarEmpty2>
            <AppBarButton Icon="Accept" Label="fdsfdsf"></AppBarButton>
            <local:AppBarEmpty2  ></local:AppBarEmpty2>
            <AppBarButton Icon="Accept" Label="fdsfdsf" ></AppBarButton>
        </CommandBar>

  是不是非常的清爽,再也不用去写什么SizeChanged事件了。实际效果如下图:

时间: 2024-08-01 10:46:13

UWP开发入门(五)——自定义Panel的相关文章

UWP开发入门(十六)——常见的内存泄漏的原因

本篇借鉴了同事翔哥的劳动成果,在巨人的肩膀上把稿子又念了一遍. 内存泄漏的概念我这里就不说了,之前<UWP开发入门(十三)——用Diagnostic Tool检查内存泄漏>中提到过,即使有垃圾回收机制,写C#还是有可能发生内存泄漏. 一般来说,以下两种情况会导致内存泄漏: 对象用完了但是没有释放资源 对象本身是做了清理内存的操作,但是对象内部的子对象没有成功释放资源 下面就UWP开发中具体的实例来说明需要避免的写法 从static/global的对象上注册了事件 FakeService.Ins

UWP开发入门(七)——下拉刷新

本篇意在给这几天Win10 Mobile负面新闻不断的某软洗地,想要证明实现一个简单的下拉刷新并不困难.UWP开发更大的困难在于懒惰,缺乏学习的意愿.而不是“某软连下拉刷新控件都没有”这样的想法. 之前我也没有进行过下拉刷新的研究.于是先去google了几篇blog学习了一下,然后再看了某软官方的Sample.(同学们啊官方有下拉刷新的Sample啊!就在Git上啊!不要钱无门槛啊!)学习之后发现实现的方式大体分为两类. 一类是以某软Sample和博客园MS-UAP封装的PullToRefres

UWP开发入门(一)——SplitView

接下来会写一个UWP(Universal Windows Platform)开发入门的系列,自己学习到哪里,有什么心得总结,就会写到哪里.本篇对适用于顶层导航的SplitView控件展开讨论. 首先SplitView是Win10 UWP新增的控件,以前虽然可以通过DockPanel模拟出类似的效果,但又哪里及得上M$原生支持的SplitView快捷方便呢. 至于为什么说SplitView适合顶层导航,可以参考目前尚为数不多的UWP APP,比如微博.QQ和网易等Win10 APP,基本都是通过S

UWP开发入门(十一)——Attached Property的简单应用

UWP中的Attached Property即附加属性,在实际开发中是很常见的,比如Grid.Row: <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition>

UWP开发入门(二十一)——保持Ui线程处于响应状态

GUI的程序有时候会因为等待一个耗时操作完成,导致界面卡死.本篇我们就UWP开发中可能遇到的情况,来讨论如何优化处理. 假设当前存在点击按钮跳转页面的操作,通过按钮打开的新页面,在初始化过程中存在一些耗时的操作. public void OnNavigatedTo(object obj) { var watch = new Stopwatch(); Debug.WriteLine("---------------Start"); watch.Start(); //假设耗时1秒 DoBu

UWP开发入门(二十三)——WebView

本篇讨论在UWP开发中使用WebView控件时常见的问题,以及一些小技巧. WebView是实际开发中常用的控件,很多大家抱怨的套网页的应用都是通过WebView来实现的.这里要澄清一个问题,套网页的应用并不一定是差的应用,很多网页采用了响应式设计,假设网页不存在复杂的交互,提取网页的正文部分嵌入WebView,可以说方便快捷省时省力.比如亚马逊.驴妈妈这些UWP APP都还挺不错的,京东那个网页就套的比较差了…… WebView最为简单的用法如下: <WebView Source="ht

UWP开发入门(十五)——在FlipView中通过手势操作图片

本篇的最终目的,是模拟系统的照片APP中可以左右滑动,缩放图片的操作.在实现的过程中,我们会逐步分析UWP编写UI的一些思路和技巧. 首先我们先实现一个横向的可以浏览图片的功能,也是大部分APP中的实现.最简单的方式是使用FlipView,再将FlipView的ItemTemplate设置成Image.大体代码如下: <FlipView ItemsSource="{Binding Photos,Mode=OneTime}"> <FlipView.ItemTemplat

UWP 开发: 妙用自定义 Action 以简化并重用代码

相信每一个 App 开发者,在开发过程中,都会有一些代码被反复用到,比如:复制文本,打电话,发短信,发邮件,给应用添加评论等等.在项目之间复制这些代码段,实在不是一个好办法,所以大家可能会把这些代码放到一个类似 Utility 类中,或者一个库(Class Library)中.本文也是帮你完成同样的事情,不过本文是通过 Action 来完成.这两种方法的目的相同,区别是前者尽管实现了代码段封装,还是需要写代码(通常在 ViewModel 中)来调用,而后者,则只要在 XAML 上添加几行代码即可

UWP开发入门系列笔记之(一):UWP初览

随着微软Build2015带来的好消息,Win10正式版发布的日子已经离我们越来越近了,我们也终于欣喜地看到:一个统一的Windows平台对于开发人员来说充满了吸引力,这局棋下的好大的说……于是顺其自然的,又开始了新一轮追随微软的脚步. 1. What Universal Windows Platform(UWP)是什么呢?首先的说一说什么叫Device Family. 微软的产品布局主要有以下这么几个方面:传统的PC和新秀平板电脑2合1(苏菲婆),手机(特洛伊木马攻陷诺基亚现在脱胎换骨成Mic