先把效果贴出来,大家看看。
代码下载地址:
http://download.csdn.net/detail/candyvoice/9730751
点击+,按钮上的红点提示会增加1,点击-,按钮上的红点提示会减去1;点击E,显示或者隐藏红点提示。
这里主要利用了自定义装饰件(Adorner)。
参考的主要文章是下面这个文章,这里由于我只需要在自己已有的button上添加一个红点提示,所以精简了下面的内容。
http://blog.csdn.net/kongxh_1981/article/details/48955693
我的理解是,PromptAdorner(继承Adroner)管理PromptChrome(继承Control)。说白了就是PromptAdorner负责装饰,装饰些什么东西则交给PromptChrome来完成。
具体的理论请参考下面
http://blog.csdn.net/kongxh_1981/article/details/48955497
说一下具体的做法。
首先添加PromptChrome.cs和PromptAdorner.cs。代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApplication2
{
internal class PromptChrome : Control
{
static PromptChrome()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PromptChrome), new FrameworkPropertyMetadata(typeof(PromptChrome)));
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
this.Width = 25;
this.Height = 25;
this.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
this.VerticalAlignment = System.Windows.VerticalAlignment.Top;
TranslateTransform tt = new TranslateTransform();
tt.X = 5;
tt.Y = 5;
this.RenderTransform = tt;
return base.ArrangeOverride(arrangeBounds);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
public class PromptAdorner : Adorner
{
public static readonly DependencyProperty PromptCountProperty =
DependencyProperty.RegisterAttached("PromptCount", typeof(int), typeof(PromptAdorner),
new FrameworkPropertyMetadata(0, new PropertyChangedCallback(PromptCountChangedCallBack), new CoerceValueCallback(CoercePromptCountCallback)));
public static int GetPromptCount(DependencyObject obj)
{
return (int)obj.GetValue(PromptCountProperty);
}
public static void SetPromptCount(DependencyObject obj, int value)
{
obj.SetValue(PromptCountProperty, value);
}
public static readonly DependencyProperty IsPromptEnabledProperty =
DependencyProperty.RegisterAttached("IsPromptEnabled", typeof(bool), typeof(PromptAdorner),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(IsPromptEnabledChangedCallBack), null));
public static bool GetIsPromptEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsPromptEnabledProperty);
}
public static void SetIsPromptEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsPromptEnabledProperty, value);
}
private static object CoercePromptCountCallback(DependencyObject d, object value)
{
int promptCount = (int)value;
promptCount = Math.Max(0, promptCount);
return promptCount;
}
public static void PromptCountChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e) { }
public static void IsPromptEnabledChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var source = d as FrameworkElement;
bool isEnabled = (bool)e.NewValue;
if (isEnabled)
{
//装饰件可用,添加装饰件
AdornerLayer layer = AdornerLayer.GetAdornerLayer(source);
if (layer != null)
{
//能够获取装饰层,说明已经load过了,直接生成装饰件
var adorner = new PromptAdorner(source);
layer.Add(adorner);
}
else
{
//layer为null,说明还未load过(整个可视化树中没有装饰层的情况不考虑)
//在控件的loaded事件内生成装饰件
source.Loaded += (s1, e1) =>
{
var adorner = new PromptAdorner(source);
AdornerLayer.GetAdornerLayer(source).Add(adorner);
};
}
}
else
{
//装饰件不可用,移除装饰件
AdornerLayer layer = AdornerLayer.GetAdornerLayer(source);
if (layer != null)
{
Adorner[] AllAdorners = layer.GetAdorners(source);
if (AllAdorners != null)
{
IEnumerable<Adorner> desAdorners = AllAdorners.Where(p => p is PromptAdorner);
if (desAdorners != null && desAdorners.Count() > 0)
{
desAdorners.ToList().ForEach(p => layer.Remove(p));
}
}
}
}
}
protected override int VisualChildrenCount
{
get { return 1; }
}
public PromptAdorner(UIElement adornedElement)
: base(adornedElement)
{
_chrome = new PromptChrome();
_chrome.DataContext = adornedElement;
this.AddVisualChild(_chrome);
}
protected override Visual GetVisualChild(int index)
{
return _chrome;
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
_chrome.Arrange(new Rect(arrangeBounds));
return arrangeBounds;
}
PromptChrome _chrome;
}
}
2 添加好之后,先编译一下程序,再 在MainWindow里拖上三个button,先拖一个PromptChrome控件到窗口的任意位置
新建一个Themes文件夹,添加资源词典,Generic.xaml
定义样式PromptChrome,Generic.xaml具体代码如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2">
<Style TargetType="{x:Type local:PromptChrome}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PromptChrome}">
<Grid x:Name="container">
<!--最外圈的白色圆框,并对其作阴影效果-->
<!--<Ellipse Fill="White">
<Ellipse.Effect>
<DropShadowEffect BlurRadius="6"
ShadowDepth="6"
Opacity="0.8"
Direction="270"
RenderingBias="Performance"/>
</Ellipse.Effect>
</Ellipse>-->
<!--内部的上半圆-->
<Ellipse Margin="3">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#FFF4AEB1"/>
<GradientStop Offset="0.5" Color="#FFE3313A"/>
<GradientStop Offset="1" Color="#FFE3313A"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<!--内部的下半圆,通过采用Exclude模式合并上下两个圆来完成-->
<Path HorizontalAlignment="Center" VerticalAlignment="Center">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude" >
<CombinedGeometry.Geometry1>
<EllipseGeometry Center="7 7" RadiusX="7" RadiusY="7" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry Center="7 0" RadiusX="9" RadiusY="7"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
<Path.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#FFDF151F"/>
<GradientStop Offset="1" Color="#FFBA0004"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
<Viewbox Stretch="Uniform" >
<!--绑定上文中的PromptCount属性-->
<Label Content="{Binding Path=(local:PromptAdorner.PromptCount)}"
x:Name="label"
Foreground="White"
FontWeight="Bold"
FontSize="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Viewbox>
</Grid>
<ControlTemplate.Triggers>
<!--使用数据触发器,当PromptCount为0时,隐藏提示-->
<DataTrigger Binding="{Binding Path=(local:PromptAdorner.PromptCount)}" Value="0">
<Setter TargetName="container" Property="Visibility" Value="Hidden"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
3编辑MainWindow.xaml中的代码
<Window x:Class="WpfApplication2.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="button" Content="按钮" HorizontalAlignment="Left" Margin="209,67,0,0" VerticalAlignment="Top" Width="75"
local:PromptAdorner.IsPromptEnabled="True"
local:PromptAdorner.PromptCount="0" Click="button_Click" IsHitTestVisible="True"/>
<Button x:Name="button1" Content="+" HorizontalAlignment="Left" Margin="89,201,0,0" VerticalAlignment="Top" Width="75" Click="button1_Click"/>
<Button x:Name="button2" Content="-" HorizontalAlignment="Left" Margin="214,202,0,0" VerticalAlignment="Top" Width="75" Click="button2_Click"/>
<Button x:Name="button3" Content="E" HorizontalAlignment="Left" Margin="339,202,0,0" VerticalAlignment="Top" Width="75" Click="button3_Click"/>
</Grid>
</Window>
实现+、-、E按钮的作用,MainWindow.xaml.cs代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
int promptCount = (int)button.GetValue(PromptAdorner.PromptCountProperty);
button.SetValue(PromptAdorner.PromptCountProperty, ++promptCount);
}
private void button2_Click(object sender, RoutedEventArgs e)
{
int promptCount = (int)button.GetValue(PromptAdorner.PromptCountProperty);
button.SetValue(PromptAdorner.PromptCountProperty, --promptCount);
}
private void button3_Click(object sender, RoutedEventArgs e)
{
bool isEnable = (bool)button.GetValue(PromptAdorner.IsPromptEnabledProperty);
button.SetValue(PromptAdorner.IsPromptEnabledProperty, !isEnable);
}
private void button_Click(object sender, RoutedEventArgs e)
{
}
}
}
编译通过后,删掉之前拖动到窗口任意位置的PromptChrome控件,再次编译通过,就可以实现在普通的button上添加红点提示信息。
中间犯过几个错误,就是在编辑Generic.xaml的时候,总是出现Binding="{Binding Path=(local:PromptAdorner.PromptCount)}无法找到的情况,这里应该是先拖一个自定义PromptChrome控件到窗口,后面删掉即可。
代码下载地址:
http://download.csdn.net/detail/candyvoice/9730751