zoukankan      html  css  js  c++  java
  • Win10 UWP开发系列:开发一个自定义控件——带数字徽章的AppBarButton

    最近有个项目有一个这样的需求,在文章浏览页底部有几个AppBarButton,其中有一个是评论按钮,需要在评论按钮上显示一个红色数字,类似微信的新消息提醒:

    image

    这种设计在iOS和Android平台都是很常见的,但UWP上并没有提供现成的控件。所以只能自己实现一个了。

    做出来效果是这样的:

     

    分析一下实现的思路。首先这还是一个AppBarButton,只是其中增加了一个数字徽章的显示,这个属性应该是可以绑定到其他属性的,如果绑定的值不为0,则显示数字,如果为0则隐藏数字。因此我们可以通过继承AppBarButton,并修改其模板来实现这个控件。

     

    下面动手实践一下吧。我已经安装了最新的VS2017,不过使用VS2015的话也是差不多的。首先创建一个名为AppBarBadgeButtonSample的UWP项目:

    image

    image

    选择在Blend中打开:

    image

    在页面上添加一个AppBar:

    image

    添加后是这样的结构:

    image

    image

    然后需要将默认AppBarButton的模板导出来,给我们自定义的控件用。在按钮上右键选择编辑模板,编辑副本:

    image

    image

    输入一个名字,为了简单起见我们就在当前文件中创建这个样式好了,点击确定。

    切换到XAML模式,可以看到页面的XAML代码中添加了这样的资源:

    image

    这个就是AppBarButton的控件模板了。我们需要在这个默认模板的基础上,实现自定义的AppBarBadgeButton。

    现在返回到VS2017,在当前项目中添加一个名为Controls的文件夹,再添加一个名为AppBarBadgeButton的文件夹,然后添加一个模板控件:

    image

    确定后,可以看到项目中添加了两个文件,一个是cs文件,一个是一个xaml文件:

    image

    这个Generic.xaml即控件的模板,但是如果每个控件的模板都写在这里的话,会显得比较乱。最好是每个控件的cs文件和xaml文件都放在一块。所以在AppBarBadgeButton目录下添加一个名为AppBarBadgeButton.xaml的样式文件,这样方便管理。同时把控件的命名空间从AppBarBadgeButtonSample.Controls.AppBarBadgeButton改为AppBarBadgeButtonSample.Controls。

    image

    现在可以把之前生成的默认的AppBarButton的模板复制过来了:

    注意,要把key删掉,TargetType都要改成AppBarBadgeButton:

    image

    现在需要把这个模板导入到Generic.xaml中,把Generic.xaml中自动生成的删掉,改为下面这样:

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="ms-appx:///Controls/AppBarBadgeButton/AppBarBadgeButton.xaml" />
    </ResourceDictionary.MergedDictionaries>

    这样所有的控件的模板都跟自己的cs文件放在一块,Generic.xaml文件中只放引用位置,项目结构就清晰多了。

    现在回到AppBarBadgeButton.cs,把控件的基类从Control改成AppBarButton。这样控件的所有行为都和默认的AppBarButton一致了。

    我们需要一个数字属性,和一个控制数字是否可见的属性。添加以下代码:

    public string Count
    {
        get { return (string)GetValue(CountProperty); }
        set { SetValue(CountProperty, value); }
    }
    
    // Using a DependencyProperty as the backing store for Count.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CountProperty =
        DependencyProperty.Register("Count", typeof(string), typeof(AppBarBadgeButton), new PropertyMetadata("0", null));

    这是一个依赖属性。注意为什么数字的类型是string呢?为什么不设置成int?

    卖个关子,先做完再说。

    现在可以修改控件的模板了。仔细分析模板就能找到需要改的地方,有一个名为ContentRoot的Grid,应该就是这里了,在名为Content的ContentPresenter后面添加一个Border和一个TextBlock来显示数字:

    <Border CornerRadius="8" Margin="8" HorizontalAlignment="Right" VerticalAlignment="Top" 
                                        Background="Red"  Grid.Row="0" MinWidth="16" Height="16">
                                    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
                                               FontSize="10" Text="{TemplateBinding Count}" Foreground="Yellow" />
                                </Border>

    注意,这里要使用TemplateBinding将文本绑定到Count属性。

    现在来试试。我们设置Page的x:Name为rootPage,把刚才在主页面添加的AppBarButton改成自定义的AppBarBadgeButton,不要忘了引入命名空间:xmlns:localControls="using:AppBarBadgeButtonSample.Controls"

    再设置Page的DataContext,并添加一个Count属性,把AppBarBadgeButton的Count绑定到cs文件的Count属性上:

    xaml:

    <Page
        x:Class="AppBarBadgeButtonSample.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AppBarBadgeButtonSample"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:localControls="using:AppBarBadgeButtonSample.Controls"
        mc:Ignorable="d"
        x:Name="rootPage">
        <Page.BottomAppBar>
            <CommandBar>
                <CommandBar.Content>
                    <Grid/>
                </CommandBar.Content>
                <localControls:AppBarBadgeButton Icon="Accept" Count="{Binding Count}" Label="AppBarButton"/>
                <AppBarButton Icon="Cancel" Label="AppBarButton"/>
            </CommandBar>
        </Page.BottomAppBar>
    
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    
        </Grid>
    </Page>

    cs:

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.Count = 10;
            this.rootPage.DataContext = this;
        }
    
        public int Count { get; set; }
    }

    我这里没有用标准的MVVM,只是简单演示一下。现在运行下看看:

    image

    效果出来了。

    现在思考刚才提到的问题,为什么在DataContext里的属性可以设置为int,而控件本身的依赖属性必须设置为string呢?如果有兴趣可以将依赖属性改为int试试,结果就是按钮上什么也显示不出来:

    image

    这是因为,在使用TemplateBinding的时候,必须保证source属性和target属性的值的类型是匹配的,TextBlock的Text属性是string,那么用于绑定的属性的类型也必须是string。在使用TemplateBinding时,没有机会进行自动的类型转换。如果类型不匹配,则无法绑定。可参考:https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/templatebinding-markup-extension

    现在可以显示数字了,但是还需要做的更完善一点。有数字的时候显示,如果为0的时候就隐藏掉。所以再做一点小小的工作:

    添加一个控制是否显示数字的属性:

    public Visibility BadgeVisibility
    {
        get { return (Visibility)GetValue(BadgeVisibilityProperty); }
        set { SetValue(BadgeVisibilityProperty, value); }
    }
    
    // Using a DependencyProperty as the backing store for BadgeVisibility.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty BadgeVisibilityProperty =
        DependencyProperty.Register("BadgeVisibility", typeof(Visibility), typeof(AppBarBadgeButton), new PropertyMetadata(Visibility.Collapsed, null));

    当Count值变化时,将其设置为隐藏:

    public string Count
            {
                get { return (string)GetValue(CountProperty); }
                set { SetValue(CountProperty, value); }
            }
    
            // Using a DependencyProperty as the backing store for Count.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty CountProperty =
                DependencyProperty.Register("Count", typeof(string), typeof(AppBarBadgeButton), new PropertyMetadata("0", OnCountChanged));
    
            private static void OnCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                int count = 0;
                int.TryParse(e.NewValue.ToString(), out count);
                if (count != 0)
                {
                    ((AppBarBadgeButton)d).SetValue(BadgeVisibilityProperty, Visibility.Visible);
                }
                else
                {
                    ((AppBarBadgeButton)d).SetValue(BadgeVisibilityProperty, Visibility.Collapsed);
                }
            }
    
     

    最后将数字的Visibility属性绑定到这个值上:

    <Border CornerRadius="8" Margin="8" HorizontalAlignment="Right" VerticalAlignment="Top" 
                                        Background="Red" Visibility="{TemplateBinding BadgeVisibility}" Grid.Row="0" MinWidth="16" Height="16">

    代码已开源:https://github.com/yanxiaodi/UWP-AppBarBadgeButton

  • 相关阅读:
    数据挖掘实践(9):基础理论(九)数学基础(九)概率(五)⻉叶斯公式
    数据挖掘实践(8):基础理论(八)数学基础(八)概率(四)参数估计(二)
    数据挖掘实践(7):基础理论(七)数学基础(七)概率(三)参数估计(一)
    数据挖掘实践(6):基础理论(六)数学基础(六)概率(二)随机变量
    Linux上快速入门英特尔Optane DC Persistent Memory Module的配置与使用
    ovs 流表
    Cannot get thread event message: debugger service failed
    gdb ovs
    rror while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory
    glibc
  • 原文地址:https://www.cnblogs.com/yanxiaodi/p/6599098.html
Copyright © 2011-2022 走看看