WPF 實(shí)現(xiàn) Badge 標(biāo)識(shí)
?WPF 實(shí)現(xiàn) Badge 標(biāo)識(shí)
控件名:Badge
作 ? 者:WPFDevelopersOrg - 驚鏵
原文鏈接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers
框架使用
.NET4 至 .NET6
Visual Studio 2022
新建
Badge.cs
繼承裝飾器Adorner
增加依賴屬性Text
用來展示文本FontSize
文本大小IsShow
為布爾值用來是否顯示Badge
IsShow = true
不設(shè)置其他屬性時(shí)候只顯示一個(gè)小紅點(diǎn)如果設(shè)置了
Text
則根據(jù)文本的寬度高度繪制小圓點(diǎn)的大小默認(rèn)將其繪制到控件右上角。

1)新建 Badge.cs
控件代碼如下:
using?System;
using?System.Windows;
using?System.Windows.Documents;
using?System.Windows.Media;
namespace?WPFDevelopers.Controls
{
????public?class?Badge?:?Adorner
????{
????????public?static?readonly?DependencyProperty?TextProperty?=
????????????DependencyProperty.Register("Text",?typeof(string),?typeof(Badge),?new?PropertyMetadata(string.Empty));
????????public?static?readonly?DependencyProperty?FontSizeProperty?=
????????????DependencyProperty.Register("FontSize",?typeof(double),?typeof(Badge),?new?PropertyMetadata(10.0d));
????????public?static?readonly?DependencyProperty?IsShowProperty?=
????????????DependencyProperty.RegisterAttached("IsShow",?typeof(bool),?typeof(Badge),
????????????????new?PropertyMetadata(false,?OnIsBadgeChanged));
????????private?static?FrameworkElement?oldFrameworkElement;
????????private?readonly?double?_size;
????????private?readonly?string?_text;
????????public?Badge(UIElement?adornedElement,?string?text?=?null,?double?size?=?0)
????????????:?base(adornedElement)
????????{
????????????_text?=?text;
????????????_size?=?size;
????????????ToolTip?=?text;
????????}
????????public?string?Text
????????{
????????????get?=>?(string)?GetValue(TextProperty);
????????????set?=>?SetValue(TextProperty,?value);
????????}
????????public?double?FontSize
????????{
????????????get?=>?(double)?GetValue(FontSizeProperty);
????????????set?=>?SetValue(FontSizeProperty,?value);
????????}
????????public?static?string?GetText(UIElement?element)
????????{
????????????if?(element?==?null)?throw?new?ArgumentNullException("Text");
????????????return?(string)?element.GetValue(TextProperty);
????????}
????????public?static?void?SetText(UIElement?element,?string?Text)
????????{
????????????if?(element?==?null)?throw?new?ArgumentNullException("Text");
????????????element.SetValue(TextProperty,?Text);
????????}
????????public?static?double?GetFontSize(UIElement?element)
????????{
????????????if?(element?==?null)?throw?new?ArgumentNullException("FontSize");
????????????return?(double)?element.GetValue(FontSizeProperty);
????????}
????????public?static?void?SetFontSize(UIElement?element,?string?Text)
????????{
????????????if?(element?==?null)?throw?new?ArgumentNullException("FontSize");
????????????element.SetValue(FontSizeProperty,?Text);
????????}
????????private?static?void?OnIsBadgeChanged(DependencyObject?d,?DependencyPropertyChangedEventArgs?e)
????????{
????????????if?(e.NewValue?is?bool?isBadge?&&?d?is?FrameworkElement?parent)
????????????{
????????????????if?(isBadge)
????????????????{
????????????????????if?(!parent.IsLoaded)
????????????????????????parent.Loaded?+=?Parent_Loaded;
????????????????????else
????????????????????????CreateBadge(parent);
????????????????}
????????????????else
????????????????{
????????????????????parent.Loaded?-=?Parent_Loaded;
????????????????????CreateBadge(parent,?true);
????????????????}
????????????}
????????}
????????private?static?void?Parent_Loaded(object?sender,?RoutedEventArgs?e)
????????{
????????????if?(sender?is?UIElement?element)
????????????????CreateBadge(element);
????????}
????????private?static?void?CreateBadge(UIElement?uIElement,?bool?isRemove?=?false)
????????{
????????????var?layer?=?AdornerLayer.GetAdornerLayer(uIElement);
????????????if?(layer?==?null)?return;
????????????if?(isRemove?&&?uIElement?!=?null)
????????????{
????????????????var?adorners?=?layer.GetAdorners(uIElement);
????????????????if?(adorners?!=?null)
????????????????????foreach?(var?item?in?adorners)
????????????????????????if?(item?is?Badge?container)
????????????????????????????layer.Remove(container);
????????????????return;
????????????}
????????????var?value?=?GetText(uIElement);
????????????var?size?=?GetFontSize(uIElement);
????????????var?badgeAdorner?=?new?Badge(uIElement,?value,?size);
????????????layer.Add(badgeAdorner);
????????}
????????public?static?bool?GetIsShow(DependencyObject?obj)
????????{
????????????return?(bool)?obj.GetValue(IsShowProperty);
????????}
????????public?static?void?SetIsShow(DependencyObject?obj,?bool?value)
????????{
????????????obj.SetValue(IsShowProperty,?value);
????????}
????????protected?override?void?OnRender(DrawingContext?drawingContext)
????????{
????????????var?adornedElement?=?AdornedElement?as?FrameworkElement;
????????????var?margin?=?adornedElement.Margin;
????????????var?desiredWidth?=?adornedElement.DesiredSize.Width?-?margin.Left?-?margin.Right;
????????????var?brush?=?new?SolidColorBrush((Color)?Application.Current.TryFindResource("WD.DangerColor"));
????????????brush.Freeze();
????????????var?radius?=?5.0;
????????????var?center?=?new?Point(desiredWidth,?0);
????????????FormattedText?formattedText?=?null;
????????????if?(!string.IsNullOrEmpty(_text))
????????????????formattedText?=?DrawingContextHelper.GetFormattedText(
????????????????????_text,
????????????????????Brushes.White,
????????????????????FlowDirection.LeftToRight,
????????????????????_size);
????????????var?pen?=?new?Pen(Brushes.White,?.3);
????????????pen.Freeze();
????????????drawingContext.PushTransform(new?MatrixTransform(Matrix.Identity));
????????????if?(formattedText?!=?null)
????????????{
????????????????var?height?=?formattedText.Height;
????????????????var?width?=?formattedText.Width?>?20???20?:?formattedText.Width;
????????????????var?isSingle?=?false;
????????????????if?(_text.Length?==?1)
????????????????{
????????????????????var?max?=?formattedText.Width?>?formattedText.Height???formattedText.Width?:?formattedText.Height;
????????????????????height?=?max;
????????????????????width?=?max;
????????????????????isSingle?=?true;
????????????????}
????????????????var?startPoint?=?new?Point(0,?0);
????????????????var?endPoint?=?new?Point(0,?0);
????????????????if?(!isSingle)
????????????????{
????????????????????startPoint?=?new?Point(center.X?-?width?/?1.4,?center.Y?-?height?/?1.8);
????????????????????endPoint?=?new?Point(center.X?+?width?/?1.4?+?6,?center.Y?+?height?/?1.8);
????????????????}
????????????????else
????????????????{
????????????????????startPoint?=?new?Point(center.X?-?width?/?2,?center.Y?-?height?/?2);
????????????????????endPoint?=?new?Point(center.X?+?width?/?2,?center.Y?+?height?/?2);
????????????????}
????????????????var?rect?=?new?Rect(startPoint,?endPoint);
????????????????drawingContext.DrawRoundedRectangle(brush,?pen,?rect,?8,?8);
????????????????formattedText.MaxTextWidth?=?width?+?10;
????????????????var?centerRect?=?new?Point(rect.Left?+?rect.Width?/?2,?rect.Top?+?rect.Height?/?2);
????????????????var?textPosition?=?new?Point(centerRect.X?-?formattedText.Width?/?2,
????????????????????centerRect.Y?-?formattedText.Height?/?2);
????????????????drawingContext.DrawText(formattedText,?textPosition);
????????????}
????????????else
????????????{
????????????????drawingContext.DrawEllipse(brush,?pen,?center,?radius,?radius);
????????????}
????????????drawingContext.Pop();
????????????RenderOptions.SetEdgeMode(this,?EdgeMode.Unspecified);
????????}
????}
}
2)新建 BadgeExample.xaml
使用 Badge
控件代碼如下:
<UserControl
????x:Class="WPFDevelopers.Samples.ExampleViews.BadgeExample"
????xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
????xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
????xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
????xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
????xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
????xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
????d:DesignHeight="450"
????d:DesignWidth="800"
????mc:Ignorable="d">
????????<Grid>
????????????<Grid.RowDefinitions>
????????????????<RowDefinition?Height="Auto"?/>
????????????????<RowDefinition?Height="Auto"?/>
????????????</Grid.RowDefinitions>
????????????<ToggleButton
????????????????x:Name="MyBadgeToggleButton"
????????????????Margin="10"
????????????????HorizontalAlignment="Center"
????????????????Content="顯示Badge"?/>
????????????<StackPanel
????????????????Grid.Row="1"
????????????????HorizontalAlignment="Center"
????????????????Orientation="Horizontal">
????????????????<Button
????????????????????wd:Badge.IsShow="{Binding?ElementName=MyBadgeToggleButton,?Path=IsChecked}"
????????????????????wd:Badge.Text="new"
????????????????????Content="Default"
????????????????????Style="{DynamicResource?WD.PrimaryButton}"?/>
????????????????<Button
????????????????????Margin="40,0"
????????????????????wd:Badge.FontSize="12"
????????????????????wd:Badge.IsShow="{Binding?ElementName=MyBadgeToggleButton,?Path=IsChecked}"
????????????????????wd:Badge.Text="3"
????????????????????Content="Success"
????????????????????Style="{DynamicResource?WD.SuccessDefaultButton}"?/>
????????????????<Button
????????????????????wd:Badge.FontSize="12"
????????????????????wd:Badge.IsShow="{Binding?ElementName=MyBadgeToggleButton,?Path=IsChecked}"
????????????????????wd:Badge.Text="10+"
????????????????????Content="Warning"
????????????????????Style="{DynamicResource?WD.WarningDefaultButton}"?/>
????????????????<Button
????????????????????Margin="40,0"
????????????????????wd:Badge.IsShow="{Binding?ElementName=MyBadgeToggleButton,?Path=IsChecked}"
????????????????????wd:Badge.Text="NEW"
????????????????????Content="Danger"
????????????????????Style="{DynamicResource?WD.DangerDefaultButton}"?/>
????????????????<Rectangle
????????????????????Width="100"
????????????????????Height="100"
????????????????????wd:Badge.IsShow="{Binding?ElementName=MyBadgeToggleButton,?Path=IsChecked}"
????????????????????Fill="Aqua"?/>
????????????</StackPanel>
????????</Grid>
</UserControl>
參考資料
[1]
原文鏈接: https://github.com/WPFDevelopersOrg/WPFDevelopers