WPF Adorner+附加属性 实现控件友好提示
标题太空泛,直接上图
无论是在验证啊,还是提示方面等一些右上角的角标之类的效果,我们会怎么做?
这里介绍一种稍微简单一些的方法,利用附加属性和Adorner来完成。
例如WPF自带的控件上要加这样的效果,首先继承自原控件然后重写是可以的,但是控件类型太多,重写不过来。这个时候我们唯一能添加的只有附加属性了。
利用附加属性的属性变更事件PropertyChangedCallBack,我们可以获取到宿主对象即Button,然后就可以往Button上加入我们自定义的Adorner了。再添加一个附加属性控制Adorner的显示/隐藏,那么就很完美了,这样每个控件只用设置两个附加属性就能拥有上面的效果。下面是核心代码,
附加属性
public class AdornerHelper { #region 是否有Adorner public static bool GetHasAdorner(DependencyObject obj) { return (bool)obj.GetValue(HasAdornerProperty); } public static void SetHasAdorner(DependencyObject obj, bool value) { obj.SetValue(HasAdornerProperty, value); } // Using a DependencyProperty as the backing store for HasAdorner. This enables animation, styling, binding, etc... public static readonly DependencyProperty HasAdornerProperty = DependencyProperty.RegisterAttached("HasAdorner", typeof(bool), typeof(AdornerHelper), new PropertyMetadata(false, PropertyChangedCallBack)); private static void PropertyChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) { var element = d as Visual; if (element != null) { var adornerLayer = AdornerLayer.GetAdornerLayer(element); if (adornerLayer!=null) { adornerLayer.Add(new NotifyAdorner(element as UIElement)); } } } } #endregion #region 是否显示Adorner public static bool GetIsShowAdorner(DependencyObject obj) { return (bool)obj.GetValue(IsShowAdornerProperty); } public static void SetIsShowAdorner(DependencyObject obj, bool value) { obj.SetValue(IsShowAdornerProperty, value); } // Using a DependencyProperty as the backing store for IsShowAdorner. This enables animation, styling, binding, etc... public static readonly DependencyProperty IsShowAdornerProperty = DependencyProperty.RegisterAttached("IsShowAdorner", typeof(bool), typeof(AdornerHelper), new PropertyMetadata(false,IsShowChangedCallBack)); private static void IsShowChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e) { var element = d as UIElement; if (element != null) { var adornerLayer = AdornerLayer.GetAdornerLayer(element); if (adornerLayer!=null) { var adorners = adornerLayer.GetAdorners(element); if (adorners != null && adorners.Count() != 0) { var adorner = adorners.FirstOrDefault() as NotifyAdorner; if (adorner == null) { return; } if ((bool)e.NewValue) { adorner.ShowAdorner(); } else { adorner.HideAdorner(); } } } } } #endregion }
然后是我们自定义的Adorner效果
public class NotifyAdorner : Adorner { private VisualCollection _visuals; private Canvas _grid; private Image _image; public NotifyAdorner(UIElement adornedElement) : base(adornedElement) { _visuals = new VisualCollection(this); _image=new Image() { Source = new BitmapImage(new Uri("Notify.png",UriKind.RelativeOrAbsolute)), Width = 25, Height = 25 }; _grid = new Canvas(); _grid.Children.Add(_image); _visuals.Add(_grid); } public void ShowAdorner() { _image.Visibility = Visibility.Visible; } public void HideAdorner() { _image.Visibility = Visibility.Collapsed; } protected override int VisualChildrenCount { get { return _visuals.Count; } } protected override Visual GetVisualChild(int index) { return _visuals[index]; } protected override Size MeasureOverride(Size constraint) { return base.MeasureOverride(constraint); } protected override Size ArrangeOverride(Size finalSize) { _grid.Arrange(new Rect(finalSize)); _image.Margin=new Thickness(finalSize.Width-12.5,-12.5,0,0); return base.ArrangeOverride(finalSize); } }
这里是源码https://files.cnblogs.com/HelloMyWorld/AdornerSample.rar
大家看代码就能懂了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
2012-09-11 c#自定义控件小结