各位好,再次回到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的更多相关文章

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

    本篇借鉴了同事翔哥的劳动成果,在巨人的肩膀上把稿子又念了一遍. 内存泄漏的概念我这里就不说了,之前<UWP开发入门(十三)——用Diagnostic Tool检查内存泄漏>中提到过,即使有 ...

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

    标签: 随着微软Build2015带来的好消息,Win10正式版发布的日子已经离我们越来越近了,我们也终于欣喜地看到:一个统一的Windows平台对于开发人员来说充满了吸引力,这局棋下的好大的说--于 ...

  3. UWP开发入门(十)——通过继承来扩展ListView

    本篇之所以起这样一个名字,是因为重点并非如何自定义控件,不涉及创建CustomControl和UserControl使用的Template和XAML概念.而是通过继承的方法来扩展一个现有的类,在继承的 ...

  4. UWP开发入门(25)——通过Radio控制Bluetooth, WiFi

    回顾写了许久的UWP开发入门,竟然没有讲过通过Windows.Devices.Radios.Radio来控制Bluetooth和WiFi等功能的开关.也许是因为相关的API设计的简单好用,以至于被我给 ...

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

    各位好,终于讲到自定义Panel了.当系统自带的几个Panel比如Gird,StackPanel,RelativePanel不能满足我们的特定要求时(其实不常见啦),自定义Panel就显得非常必要,而 ...

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

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

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

    本篇意在给这几天Win10 Mobile负面新闻不断的某软洗地,想要证明实现一个简单的下拉刷新并不困难.UWP开发更大的困难在于懒惰,缺乏学习的意愿.而不是“某软连下拉刷新控件都没有”这样的想法. 之 ...

  8. UWP开发入门(一)——SplitView

    接下来会写一个UWP(Universal Windows Platform)开发入门的系列,自己学习到哪里,有什么心得总结,就会写到哪里.本篇对适用于顶层导航的SplitView控件展开讨论. 首先S ...

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

    本篇讨论在UWP开发中使用WebView控件时常见的问题,以及一些小技巧. WebView是实际开发中常用的控件,很多大家抱怨的套网页的应用都是通过WebView来实现的.这里要澄清一个问题,套网页的 ...

随机推荐

  1. 普通方法调用,Invoke,begininvoke三者的区别总结及异步与同步的区别总结

    (1)普通方法调用(直接调用)与Invoke()方法调用方法 使用的线程Id是一样的 即属于同步. (2)BeginInvoke(<输入和输出变量>,AsyncCallback callb ...

  2. selenium webdriver——JS滚动到最底部

    JS控制滚动条的位置: window.scrollTo(x,y); 竖向滚动条置顶 window.scrollTo(0,0); 竖向滚动条置底 window.scrollTo(0,document.b ...

  3. Redis Windows环境安装

    1.下载Windows 版本 Redis: https://github.com/ServiceStack/redis-windows 2. 解压文件: F:\开源代码学习\01_Redis 打开 目 ...

  4. IOS 上传ipa文件失败

    ** No suitable application records were found. Verify your bundle identifier 'com.***' is correct. i ...

  5. CCS5配置使用GenCodecPkg生成CODEC SERVER

    1. 引言 CCS5中集成了算法生成工具的插件,使用XDAIS Tools条目里面的GenAlg命令生成的算法框架如下: 不过,我没找到如何在CCS中使用这个工程.难不成要把这个框架文件放在Linux ...

  6. MyBatis 提供的内置类型别名

  7. nice & renice

    [nice & renice & getpriority & setpriority] 1.nice & renice 参考:http://man.ddvip.com/ ...

  8. 【总结整理】关于挪车和虚拟号的思考-转载v2ex

    https://www.baidu.com/link?url=A7wiF1JpOkT6Juo0nNHKcum0OiQsnRj-EZkQfjc3xB-noUeLy3HEY-4plbFmPmuJ& ...

  9. 74款安卓和IOS app源码地址

    知乎专栏App https://github.com/bxbxbai/ZhuanLan WeChat高仿微信 项目地址: https://github.com/motianhuo/wechat Gan ...

  10. nslookup域名查询命令查询ip

    一.nslookup(name server lookup ) nslookup可以指定查询的类型,可以查到DNS记录的生存时间还可以指定使用哪个DNS服务器进行解释.在已安装TCP/IP协议的电脑上 ...