UWP开发入门(四)——自定义CommandBar

简介: 原文:UWP开发入门(四)——自定义CommandBar  各位好,再次回到UWP开发入门系列,刚回归可能有些不适应,所以今天我们讲个简单的,自定义CommandBar,说通俗点就是自定义类似AppBarButton的东西,然后扔到CommandBar中使用。
原文: UWP开发入门(四)——自定义CommandBar

  各位好,再次回到UWP开发入门系列,刚回归可能有些不适应,所以今天我们讲个简单的,自定义CommandBar,说通俗点就是自定义类似AppBarButton的东西,然后扔到CommandBar中使用。

  话说为了在公司次世代平台级战略层产品上实现与水果和机器人一致的用户体验,美工把Win10 APPAppBar也画成左右分开的了,好看是好看了,问题原生的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这个集合,和我们今天的主题关系不大。而最左侧的文字描述是放到CommandBarContent属性中,也无需理睬。我们需要实现的是将AppButton左右分开,这部分的按钮均属于PrimaryCommands这个集合,XAML没有看到是因为简化写法的缘故。

  既然属于PrimaryCommands集合的元素会被呈现在按钮区域,那么我们就检查一下该集合的类型定义:

public IObservableVector<ICommandBarElement> PrimaryCommands { get; }

  很明显该集合的元素需要实现接口ICommandBarElement,而该接口又非常简单:

    //
    // Summary:
    //     Defines the compact view for command bar elements.
    [ContractVersion(typeof(UniversalApiContract), 65536)]
    [GuidAttribute(1737592347, 62165, 17617, 139, 132, 146, 184, 127, 128, 163, 80)]
    [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提供了占位所有的WidthHeight等属性,更基础的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,这货简直是神器,现在没有它都不知道怎么调试了……),检查发现AppBarEmptyWidht0,而PrimaryCommands集合里的这些按钮又都是放在StackPanel里横向顺序排开的,悲剧的是StackPanleWidth就是几个AppBarButtonWidth总和,完全没有留给AppBarEmpty一丝丝的宽度……设置AppBarEmptyHorizontalAlignment=StretchStackPanel是然并卵……

  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做了XBindTestWidth属性上,相应的Page的代码里定义了该属性,并在PageSizeChanged事件中计算了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));
        }
    }

  代码中减掉的48CommandBar最右边“”按钮的宽度。实际显示如下图:

 

这里仍然存在几个问题:

 

1.有童鞋说为什么不自己写个RelativePanel,然后里面的按钮就可以随便对齐定位了。

  该方法确实可行,但等于抛弃了CommandBar,也无法集成到PageTopAppBarBottomAppBar中使用。有点偏离了我们自定义CommadBar的初衷。并且要想完整实现一套CommandBar的工作量还是不小的

 

2.实现的方式不够优美,竟然使用了SizeChanged事件来计算Width。简直返古到了WinForm的时代。

哼哼,兄弟我不想好解决方案敢写这篇被你们骂?当然是已经准备好了完美的解决方案才来写这篇钓鱼咯,乖乖给我点个推荐,然后我们下周见……

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

目录
相关文章
|
前端开发 Ubuntu Linux
【.NET6+Avalonia】开发支持跨平台的仿WPF应用程序以及基于ubuntu系统的演示
随着跨平台越来越流行,.net core支持跨平台至今也有好几年的光景了。但是目前基于.net的跨平台,大多数还是在使用B/S架构的跨平台上;至于C/S架构,大部分人可能会选择QT进行开发,或者很早之前还有一款Mono可以支持.NET开发者进行开发跨平台应用。
1086 0
【.NET6+Avalonia】开发支持跨平台的仿WPF应用程序以及基于ubuntu系统的演示
|
3月前
|
开发者 iOS开发 C#
Uno Platform 入门超详细指南:从零开始教你打造兼容 Web、Windows、iOS 和 Android 的跨平台应用,轻松掌握 XAML 与 C# 开发技巧,快速上手示例代码助你迈出第一步
【8月更文挑战第31天】Uno Platform 是一个基于 Microsoft .NET 的开源框架,支持使用 C# 和 XAML 构建跨平台应用,适用于 Web(WebAssembly)、Windows、Linux、macOS、iOS 和 Android。它允许开发者共享几乎全部的业务逻辑和 UI 代码,同时保持原生性能。选择 Uno Platform 可以统一开发体验,减少代码重复,降低开发成本。安装时需先配置好 Visual Studio 或 Visual Studio for Mac,并通过 NuGet 或官网下载工具包。
328 0
|
XML Java 数据格式
HarmonyOS学习路之开发基础——快速入门(创建另一个页面)
在上一节中,我们用XML的方式编写了一个包含文本和按钮的页面。为了帮助开发者熟悉在代码中创建布局的方式,接下来我们使用代码的方式编写第二个页面。
|
XML Java 数据格式
HarmonyOS学习路之开发基础——快速入门(编写第一个页面)
在Java UI框架中,提供了两种编写布局的方式:在XML中声明UI布局和在代码中创建布局。这两种方式创建出的布局没有本质差别,为了熟悉两种方式,我们将通过XML的方式编写第一个页面,通过代码的方式编写第二个页面。
|
存储 图形学
Unity3D基本入门及功能介绍
第一,界面入门Unity3D 最经典 2 by 3 结构界面,上面呈现了 Unity3D 最为常用的几个面板,下面为各个面板的详细说明。1.Scene【场景面板】:该面板为 Unity3D 的编辑面板;你可以将你所有的模型、灯光、以及其他材质对象拖放到该场景中。
2818 0
|
开发工具 git
(2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
原文:(2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
948 0
|
XML 物联网 开发工具
(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
原文:(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
1266 0
|
Windows
[UWP开发]NavigationView基础使用方法
原文:[UWP开发]NavigationView基础使用方法 [UWP开发]NavigationView基础使用方法 NavigationView是秋季创意者更新(16299)引入的新控件,用于生成Windows特色的导航栏。
2315 0
|
C# 容器 开发框架
MEF 插件式开发 - WPF 初体验
原文:MEF 插件式开发 - WPF 初体验 目录 MEF 在 WPF 中的简单应用 加载插件 获取元数据 依赖注入 总结 MEF 在 WPF 中的简单应用 MEF 的开发模式主要适用于插件化的业务场景中,C/S 和 B/S 中都有相应的使用场景,其中包括但不限于 ASP.NET MVC 、ASP WebForms、WPF、UWP 等开发框架。
1171 0
|
传感器 自然语言处理 C#
[UWP]本地化入门
原文:[UWP]本地化入门 1. 前言 上一篇文章介绍了各种WPF本地化的入门知识,这篇文章介绍UWP本地化的入门知识。 2. 使用resw资源文件实现本地化 在以前的XAML平台,resx资源文件是一种很方便的本地化方案,但在UWP中微软又再次推荐x:Uid方案,默认的资源文件也变成resw资源文件。
1365 0
下一篇
无影云桌面