UWP开发入门(四)——自定义CommandBar
各位好,再次回到UWP开发入门系列,刚回归可能有些不适应,所以今天我们讲个简单的,自定义CommandBar,说通俗点就是自定义类似AppBarButton的东西,然后扔到CommandBar中使用。
话说为了在公司次世代平台级战略层产品上实现与水果和机器人一致的用户体验,美工把Win10 APP的AppBar也画成左右分开的了,好看是好看了,问题原生的ComandBar控件不支持这么排列啊……头疼归头疼,只能再次展开山寨之路……
初步思路是在CommandBar中塞进占位用的空白元素,我管它叫AppBarEmpty。那么能放进CommandBar中的元素需要符合什么样的规则呢?首先看一下CommandBar的用法:
<CommandBar>
<AppBarToggleButton Icon="Shuffle" Label="Shuffle" Click="AppBarButton_Click" />
<AppBarToggleButton Icon="RepeatAll" Label="Repeat" Click="AppBarButton_Click"/>
<AppBarSeparator/>
<AppBarButton Icon="Back" Label="Back" Click="AppBarButton_Click"/>
<AppBarButton Icon="Stop" Label="Stop" Click="AppBarButton_Click"/>
<AppBarButton Icon="Play" Label="Play" Click="AppBarButton_Click"/>
<AppBarButton Icon="Forward" Label="Forward" Click="AppBarButton_Click"/> <CommandBar.SecondaryCommands>
<AppBarButton Icon="Like" Label="Like" Click="AppBarButton_Click"/>
<AppBarButton Icon="Dislike" Label="Dislike" Click="AppBarButton_Click"/>
</CommandBar.SecondaryCommands> <CommandBar.Content>
<TextBlock Text="Now playing..." Margin="12,14"/>
</CommandBar.Content>
</CommandBar>
上面这个常规的Command显示效果如下图:

规律还是比较明显的,菜单“like”,“Dislike”均属于SecondaryCommands这个集合,和我们今天的主题关系不大。而最左侧的文字描述是放到CommandBar的Content属性中,也无需理睬。我们需要实现的是将AppButton左右分开,这部分的按钮均属于PrimaryCommands这个集合,XAML没有看到是因为简化写法的缘故。
既然属于PrimaryCommands集合的元素会被呈现在按钮区域,那么我们就检查一下该集合的类型定义:
public IObservableVector<ICommandBarElement> PrimaryCommands { get; }
很明显该集合的元素需要实现接口ICommandBarElement,而该接口又非常简单:
//
// Summary:
// Defines the compact view for command bar elements.
[ContractVersion(typeof(UniversalApiContract), )]
[GuidAttribute(, , , , , , , , , , )]
[WebHostHidden]
public interface ICommandBarElement
{
//
// Summary:
// Gets or sets a value that indicates whether the element is shown with no label
// and reduced padding.
//
// Returns:
// true if the element is shown in its compact state; otherwise, false. The default
// is false.
System.Boolean IsCompact { get; set; }
}
这样我们的AppBarEmpty就好写了,仅仅需要实现一个IsCompact属性即可,而占位用的AppBarEmpty其实是没有内容的,连IsCompact的效果其实都省了……
public class AppBarEmpty : FrameworkElement, ICommandBarElement
{
public bool IsCompact { get; set; }
}
是不是有种骗钱的感觉?别走啊,这里还是有讲究的,首先为什么是继承自FrameworkElement呢?
1.AppEmpty是一个占位控件,基本不具备功能,应该从轻量级的基类继承
2.FrameworkElement提供了占位所有的Width和Height等属性,更基础的UIElement则没有这些
3.从FrameworkElement才开始支持的Binding
搞清楚了以上这些之后,我们兴冲冲的把XAML补完了:
<CommandBar>
<AppBarButton Icon="Accept" Label="Accept"></AppBarButton>
<AppBarButton Icon="Accept" Label="Accept"></AppBarButton>
<local:AppBarEmpty></local:AppBarEmpty>
<AppBarButton Icon="Accept" Label="Accept"></AppBarButton>
</CommandBar>
行云流水编译一次通过,嗷嗷的按下F5运行……结果傻眼了……什么效果也没有……介个是为什么呢?
只能通过Live Tree View来检查了(后面会写一篇介绍Live Tree View,这货简直是神器,现在没有它都不知道怎么调试了……),检查发现AppBarEmpty的Widht是0,而PrimaryCommands集合里的这些按钮又都是放在StackPanel里横向顺序排开的,悲剧的是StackPanle的Width就是几个AppBarButton的Width总和,完全没有留给AppBarEmpty一丝丝的宽度……设置AppBarEmpty的HorizontalAlignment=Stretch对StackPanel是然并卵……
CommanBar模板PrimaryCommands部分节选:
<ItemsControl
x:Name="PrimaryItemsControl"
HorizontalAlignment="Right"
MinHeight="{ThemeResource AppBarThemeMinHeight}"
IsTabStop="False"
Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
悲剧啊!既然HorizontalAlignment不管用,无法自行撑开。那我们就只有给AppBarEmpty绑定一个确实的Width了。对应的XAML如下:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition x:Name="RowBottom" Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="1"> </TextBox>
<CommandBar x:Name="commandBar" Grid.Row="2">
<AppBarButton x:Name="appbarButton" Icon="Accept" Label="Accept"></AppBarButton>
<AppBarButton Icon="Accept" Label="Accept"></AppBarButton>
<local:AppBarEmpty Width="{x:Bind TestWidth,Mode=OneWay}"></local:AppBarEmpty>
<AppBarButton Icon="Accept" Label="Accept"></AppBarButton>
</CommandBar>
</Grid>
为AppBarEmpty做了XBind到TestWidth属性上,相应的Page的代码里定义了该属性,并在Page的SizeChanged事件中计算了AppBarEmpty实际需要的宽度。
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public double TestWidth { get; set; } public MainPage()
{
this.InitializeComponent();
this.SizeChanged += MainPage_SizeChanged;
} public event PropertyChangedEventHandler PropertyChanged; private void MainPage_SizeChanged(object sender, SizeChangedEventArgs e)
{
int count = this.commandBar.PrimaryCommands.Count-1;
double width = (this.commandBar.PrimaryCommands[0] as AppBarButton).ActualWidth;
TestWidth = e.NewSize.Width - count* width -48;
this.OnPropertyChanged("TestWidth");
} public void OnPropertyChanged([CallerMemberName]string name = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
代码中减掉的48是CommandBar最右边“…”按钮的宽度。实际显示如下图:

这里仍然存在几个问题:
1.有童鞋说为什么不自己写个RelativePanel,然后里面的按钮就可以随便对齐定位了。
该方法确实可行,但等于抛弃了CommandBar,也无法集成到Page的TopAppBar、BottomAppBar中使用。有点偏离了我们自定义CommadBar的初衷。并且要想完整实现一套CommandBar的工作量还是不小的
2.实现的方式不够优美,竟然使用了SizeChanged事件来计算Width。简直返古到了WinForm的时代。
哼哼,兄弟我不想好解决方案敢写这篇被你们骂?当然是已经准备好了完美的解决方案才来写这篇钓鱼咯,乖乖给我点个推荐,然后我们下周见……
UWP开发入门(四)——自定义CommandBar的更多相关文章
- UWP开发入门(十六)——常见的内存泄漏的原因
本篇借鉴了同事翔哥的劳动成果,在巨人的肩膀上把稿子又念了一遍. 内存泄漏的概念我这里就不说了,之前<UWP开发入门(十三)——用Diagnostic Tool检查内存泄漏>中提到过,即使有 ...
- UWP开发入门系列笔记之(一):UWP初览
标签: 随着微软Build2015带来的好消息,Win10正式版发布的日子已经离我们越来越近了,我们也终于欣喜地看到:一个统一的Windows平台对于开发人员来说充满了吸引力,这局棋下的好大的说--于 ...
- UWP开发入门(十)——通过继承来扩展ListView
本篇之所以起这样一个名字,是因为重点并非如何自定义控件,不涉及创建CustomControl和UserControl使用的Template和XAML概念.而是通过继承的方法来扩展一个现有的类,在继承的 ...
- UWP开发入门(25)——通过Radio控制Bluetooth, WiFi
回顾写了许久的UWP开发入门,竟然没有讲过通过Windows.Devices.Radios.Radio来控制Bluetooth和WiFi等功能的开关.也许是因为相关的API设计的简单好用,以至于被我给 ...
- UWP开发入门(五)——自定义Panel
各位好,终于讲到自定义Panel了.当系统自带的几个Panel比如Gird,StackPanel,RelativePanel不能满足我们的特定要求时(其实不常见啦),自定义Panel就显得非常必要,而 ...
- UWP开发入门(十一)——Attached Property的简单应用
UWP中的Attached Property即附加属性,在实际开发中是很常见的,比如Grid.Row: <Grid Background="{ThemeResource Applica ...
- UWP开发入门(七)——下拉刷新
本篇意在给这几天Win10 Mobile负面新闻不断的某软洗地,想要证明实现一个简单的下拉刷新并不困难.UWP开发更大的困难在于懒惰,缺乏学习的意愿.而不是“某软连下拉刷新控件都没有”这样的想法. 之 ...
- UWP开发入门(一)——SplitView
接下来会写一个UWP(Universal Windows Platform)开发入门的系列,自己学习到哪里,有什么心得总结,就会写到哪里.本篇对适用于顶层导航的SplitView控件展开讨论. 首先S ...
- UWP开发入门(二十三)——WebView
本篇讨论在UWP开发中使用WebView控件时常见的问题,以及一些小技巧. WebView是实际开发中常用的控件,很多大家抱怨的套网页的应用都是通过WebView来实现的.这里要澄清一个问题,套网页的 ...
随机推荐
- [译]作为一个web开发人员,哪些技术细节是在发布站点前你需要考虑到的
前日在cnblogs上看到一遍文章<每个程序员都必读的12篇文章>,其中大多数是E文的. 先译其中一篇web相关的”每个程序员必知之WEB开发”. 原文: http://programme ...
- 如何优化Java垃圾回收-zz
为什么需要优化GC 或者说的更确切一些,对于基于Java的服务,是否有必要优化GC?应该说,对于所有的基于Java的服务,并不总是需要进行GC优化,但前提是所运行的基于Java的系统,包含了如下参数或 ...
- Gitlab 社区版安装部署和维护指南
因为我的个人网站 restran.net 已经启用,博客园的内容已经不再更新.这篇文章是在 Gitlab 7.4 的环境下配置的,相关内容可能已经过时. 后续做了一次迁移,将 Gitlab 升级到了 ...
- jeesite快速开发平台(七)----代码生成原理
转自:https://blog.csdn.net/u011781521/article/details/79322942
- delphi 实体类 JSON 数组
delphi 实体类 与JSON转换,序列化 TJson REST.JSON.pas TJson.JsonToObjectTJson.ObjectToJsonString JsonEncode O ...
- Android gralloc 模块实例
本文实例为借鉴 http://www.ixueyi.com/jingyan/1865079.html 该文档后所写.主要是android的gralloc操作显存的模块实例,如有不正确的地方欢迎指出谢谢 ...
- k8s 1.10 关于rbac的坑
apiserver 启动加上--authorization-mode=RBAC 开启rbac 会生成默认role,最高权限位cluster-admin的cluster role 再关闭rbac(不加 ...
- 119. Pascal's Triangle II (Graph; WFS)
Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3, Return [1,3 ...
- WebSocket 资料搜索
http://jwebsocket.org/ http://zh.wikipedia.org/wiki/WebSocket http://www.infoq.com/cn/news/2013/07/e ...
- Java的类名与文件名必须一致
1.Java保存的文件名必须与类名一致:2.如果文件中只有一个类,文件名必须与类名一致:3.一个Java文件中只能有一个public类:4.如果文件中不止一个类,文件名必须与public类名一致:5. ...