WPF自定义控件,实现含有箭头的文本或内容控件

2024-07-03 1475阅读

文章目录

    • 背景
    • 效果预览
    • 方案设计
    • 分析
      • 基本布局
      • 添加控件自定义属性
      • 添加属性值监听
      • 获取点数据
      • 全部代码
        • HorizontalLineContent.xaml
        • HorizontalLineContent.xaml.cs
        • DirectionAlignment.cs
        • ContentDirectionAlignment.cs
        • 使用方法

          背景

          因为项目开发需要,要在WPF上绘制TCP的交互过程图,所以现在需要一个箭头控件,并且在箭头中间还可以加入文字,所以开发了这个HorizontalLineContent控件,后续可能还要开发垂直版本。😁

          WPF自定义控件,实现含有箭头的文本或内容控件

          效果预览

          WPF自定义控件,实现含有箭头的文本或内容控件

          WPF自定义控件,实现含有箭头的文本或内容控件

          方案设计

          一开始设计的实现逻辑其实是有两种的:

          1. Polygon x 1 + Label x 1

            然后通过设置层级Index,让文本控件覆盖在图形上,但是有个致命的缺点,就是文本控件不能设置背景颜色为透明色,不然图形就会和文字重叠,所以没有采用该方案。

          2. Polygon x 2 + Label x 1

            通过计算Label的渲染宽度,然后计算两边图形的点数据,然后绘制图形,虽然该方案比上一个要繁琐一点,但是效果会好很多,后面也可以方便扩展其它的属性,比如:整体旋转、单独图形旋转、单独文字旋转等。

          我这里目前只考虑了如下几个要素:

          • 直线大小
          • 控件颜色,这里没有做Text和箭头颜色的分开
          • 文本位置
          • 箭头方向
          • 箭头宽度和高度

            核心的几点逻辑是:

            1. 我分为了两个Polygon去绘制图形,一个在左边,一个在右边,通过设置箭头方向然后确定哪一个Polygon去绘制箭头,另一个去绘制直线。
            2. 文本控件在中间水平居中,通过监听自定义控件属性Text的变化,然后重新计算文本控件的宽度,然后绘制控件的点数据。
            3. 我这里将控件的Foreground属性绑定在了Polygon的Fill属性上,实现图形跟文字一起变色,如果需要单独控制文本和图形的颜色,可以拆开处理。

            分析

            基本布局

            首先我们肯定是需要一个Label控件对文本内容进行显示,其次是需要一个能调整上中下位置的Grid,还需要两个Polygon去绘制图形。

            所以基本样式就有了

                
                    
                    
                    
                
                
                    
                        
                        
                        
                        
                    
                
                
                    
                        
                        
                        
                        
                    
                
                
            
            

            在设计器中的效果如下图:

            WPF自定义控件,实现含有箭头的文本或内容控件

            添加控件自定义属性

            // 内容
            public new object Content
            {
                get { return (string)GetValue(ContentProperty); }
                set { SetValue(ContentProperty, value); }
            }
            public static new readonly DependencyProperty ContentProperty =
                DependencyProperty.Register("Content", typeof(object), typeof(HorizontalLineContent), new PropertyMetadata(string.Empty));
            // 箭头方向
            public DirectionAlignment Direction
            {
                get { return (DirectionAlignment)GetValue(DirectionProperty); }
                set { SetValue(DirectionProperty, value); }
            }
            public static readonly DependencyProperty DirectionProperty =
                DependencyProperty.Register("Direction", typeof(DirectionAlignment), typeof(HorizontalLineContent), new PropertyMetadata(DirectionAlignment.None));
            // 文本位置
            public ContentDirectionAlignment ContentDirection
            {
                get { return (ContentDirectionAlignment)GetValue(ContentDirectionProperty); }
                set { SetValue(ContentDirectionProperty, value); }
            }
            public static readonly DependencyProperty ContentDirectionProperty =
                DependencyProperty.Register("ContentDirection", typeof(ContentDirectionAlignment), typeof(HorizontalLineContent), new PropertyMetadata(ContentDirectionAlignment.Center));
            // 直线宽度
            public double LineSize
            {
                get { return (double)GetValue(LineSizeProperty); }
                set { SetValue(LineSizeProperty, value); }
            }
            public static readonly DependencyProperty LineSizeProperty =
                DependencyProperty.Register("LineSize", typeof(double), typeof(HorizontalLineContent), new PropertyMetadata(4.0));
            // 箭头宽度
            public double ArrowWidth
            {
                get { return (double)GetValue(ArrowWidthProperty); }
                set { SetValue(ArrowWidthProperty, value); }
            }
            public static readonly DependencyProperty ArrowWidthProperty =
                DependencyProperty.Register("ArrowWidth", typeof(double), typeof(HorizontalLineContent), new PropertyMetadata(10.0));
            // 箭头高度
            public double ArrowHeight
            {
                get { return (double)GetValue(ArrowHeightProperty); }
                set { SetValue(ArrowHeightProperty, value); }
            }
            public static readonly DependencyProperty ArrowHeightProperty =
                DependencyProperty.Register("ArrowHeight", typeof(double), typeof(HorizontalLineContent), new PropertyMetadata(12.0));
            

            添加属性值监听

            // 监听整体控件宽度属性变化
            ActualWidthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(Label));
            ActualWidthPropertyDescriptor.AddValueChanged(txt_text, (o, e) =>
            {
                RefreshUI();
            });
            // 监听文本内容属性变化
            ContentPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ContentProperty, typeof(HorizontalLineContent));
            ContentPropertyDescriptor.AddValueChanged(this, (o, e) =>
            {
                RefreshUI();
            });
            // 监听箭头方向属性变化
            DirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DirectionProperty, typeof(HorizontalLineContent));
            DirectionPropertyDescriptor.AddValueChanged(this, (o, e) =>
            {
                RefreshUI();
            });
            // 监听文本位置属性变化
            ContentDirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ContentDirectionProperty, typeof(HorizontalLineContent));
            ContentDirectionPropertyDescriptor.AddValueChanged(this, (o, e) =>
            {
                RefreshUI();
            });
            // 监听直线宽度属性变化
            LineSizePropertyDescriptor = DependencyPropertyDescriptor.FromProperty(LineSizeProperty, typeof(HorizontalLineContent));
            LineSizePropertyDescriptor.AddValueChanged(this, (o, e) =>
            {
                RefreshUI();
            });
            // 监听箭头高度属性变化
            ArrowHeightPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ArrowHeightProperty, typeof(HorizontalLineContent));
            ArrowHeightPropertyDescriptor.AddValueChanged(this, (o, e) =>
            {
                RefreshUI();
            });
            // 监听箭头宽度属性变化
            ArrowWidthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ArrowWidthProperty, typeof(HorizontalLineContent));
            ArrowWidthPropertyDescriptor.AddValueChanged(this, (o, e) =>
            {
                RefreshUI();
            });
            

            获取点数据

            public List GetPoints(DirectionAlignment direction, double left, double mid_left, double mid_right, double right)
            {
                var points = new List();
                if (ContentDirection != ContentDirectionAlignment.Center && ContentDirection != ContentDirectionAlignment.None)
                {
                    mid_left = mid_right = this.ActualWidth / 2.0;
                }
                var mid_width = this.ActualWidth / 2.0;
                var lineSize = LineSize  
            

            全部代码

            共4个文件

            HorizontalLineContent.xaml

                
                    
                        
                        
                    
                    
                    
                    
                    
                        
                            
                                
                                
                                    
                                    
                                    
                                
                            
                        
                    
                
            
            

            HorizontalLineContent.xaml.cs

            using System.Collections.Generic;
            using System.ComponentModel;
            using System.Windows;
            using System.Windows.Controls;
            using System.Windows.Data;
            using System.Windows.Shapes;
            #region 文件版本信息
            /*
             * 创建者:Zekang Hao
             * 邮箱:admin@haozekang.com
             * 创建时间:2024/6/27 15:11:07
             * 版本:1.0.0.0
             * 描述:HorizontalLineContent.xaml 的交互逻辑
             */
            #endregion
            namespace WpfApp3
            {
                public partial class HorizontalLineContent : UserControl
                {
                    private DependencyPropertyDescriptor ActualWidthPropertyDescriptor;
                    private DependencyPropertyDescriptor ContentPropertyDescriptor;
                    private DependencyPropertyDescriptor DirectionPropertyDescriptor;
                    private DependencyPropertyDescriptor ContentDirectionPropertyDescriptor;
                    private DependencyPropertyDescriptor LineSizePropertyDescriptor;
                    private DependencyPropertyDescriptor ArrowWidthPropertyDescriptor;
                    private DependencyPropertyDescriptor ArrowHeightPropertyDescriptor;
                    public new object Content
                    {
                        get { return (string)GetValue(ContentProperty); }
                        set { SetValue(ContentProperty, value); }
                    }
                    public static new readonly DependencyProperty ContentProperty =
                        DependencyProperty.Register("Content", typeof(object), typeof(HorizontalLineContent), new PropertyMetadata(string.Empty));
                    public DirectionAlignment Direction
                    {
                        get { return (DirectionAlignment)GetValue(DirectionProperty); }
                        set { SetValue(DirectionProperty, value); }
                    }
                    // Using a DependencyProperty as the backing store for Direction.  This enables animation, styling, binding, etc...
                    public static readonly DependencyProperty DirectionProperty =
                        DependencyProperty.Register("Direction", typeof(DirectionAlignment), typeof(HorizontalLineContent), new PropertyMetadata(DirectionAlignment.None));
                    public ContentDirectionAlignment ContentDirection
                    {
                        get { return (ContentDirectionAlignment)GetValue(ContentDirectionProperty); }
                        set { SetValue(ContentDirectionProperty, value); }
                    }
                    // Using a DependencyProperty as the backing store for TextDirection.  This enables animation, styling, binding, etc...
                    public static readonly DependencyProperty ContentDirectionProperty =
                        DependencyProperty.Register("ContentDirection", typeof(ContentDirectionAlignment), typeof(HorizontalLineContent), new PropertyMetadata(ContentDirectionAlignment.Center));
                    public double LineSize
                    {
                        get { return (double)GetValue(LineSizeProperty); }
                        set { SetValue(LineSizeProperty, value); }
                    }
                    // Using a DependencyProperty as the backing store for LineSize.  This enables animation, styling, binding, etc...
                    public static readonly DependencyProperty LineSizeProperty =
                        DependencyProperty.Register("LineSize", typeof(double), typeof(HorizontalLineContent), new PropertyMetadata(4.0));
                    public double ArrowWidth
                    {
                        get { return (double)GetValue(ArrowWidthProperty); }
                        set { SetValue(ArrowWidthProperty, value); }
                    }
                    // Using a DependencyProperty as the backing store for ArrowWidth.  This enables animation, styling, binding, etc...
                    public static readonly DependencyProperty ArrowWidthProperty =
                        DependencyProperty.Register("ArrowWidth", typeof(double), typeof(HorizontalLineContent), new PropertyMetadata(10.0));
                    public double ArrowHeight
                    {
                        get { return (double)GetValue(ArrowHeightProperty); }
                        set { SetValue(ArrowHeightProperty, value); }
                    }
                    // Using a DependencyProperty as the backing store for ArrowHeight.  This enables animation, styling, binding, etc...
                    public static readonly DependencyProperty ArrowHeightProperty =
                        DependencyProperty.Register("ArrowHeight", typeof(double), typeof(HorizontalLineContent), new PropertyMetadata(12.0));
            
                    public HorizontalLineContent()
                    {
                        this.DataContext = this;
                        InitializeComponent();
                        txt_text.SetBinding(
                            Label.ContentProperty,
                            new Binding
                            {
                                Path = new PropertyPath(nameof(Content)),
                                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                                Mode = BindingMode.OneWay
                            }
                        );
                        txt_text.SetBinding(
                            ForegroundProperty,
                            new Binding
                            {
                                Path = new PropertyPath(nameof(Foreground)),
                                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                                Mode = BindingMode.OneWay
                            }
                        );
                        pol_left.SetBinding(
                            Polygon.FillProperty,
                            new Binding
                            {
                                Path = new PropertyPath(nameof(Foreground)),
                                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                                Mode = BindingMode.OneWay
                            }
                        );
                        pol_right.SetBinding(
                            Polygon.FillProperty,
                            new Binding
                            {
                                Path = new PropertyPath(nameof(Foreground)),
                                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                                Mode = BindingMode.OneWay
                            }
                        );
                        ActualWidthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(Label));
                        ActualWidthPropertyDescriptor.AddValueChanged(txt_text, (o, e) =>
                        {
                            RefreshUI();
                        });
                        ContentPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ContentProperty, typeof(HorizontalLineContent));
                        ContentPropertyDescriptor.AddValueChanged(this, (o, e) =>
                        {
                            RefreshUI();
                        });
                        DirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DirectionProperty, typeof(HorizontalLineContent));
                        DirectionPropertyDescriptor.AddValueChanged(this, (o, e) =>
                        {
                            RefreshUI();
                        });
                        ContentDirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ContentDirectionProperty, typeof(HorizontalLineContent));
                        ContentDirectionPropertyDescriptor.AddValueChanged(this, (o, e) =>
                        {
                            RefreshUI();
                        });
                        LineSizePropertyDescriptor = DependencyPropertyDescriptor.FromProperty(LineSizeProperty, typeof(HorizontalLineContent));
                        LineSizePropertyDescriptor.AddValueChanged(this, (o, e) =>
                        {
                            RefreshUI();
                        });
                        ArrowHeightPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ArrowHeightProperty, typeof(HorizontalLineContent));
                        ArrowHeightPropertyDescriptor.AddValueChanged(this, (o, e) =>
                        {
                            RefreshUI();
                        });
                        ArrowWidthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ArrowWidthProperty, typeof(HorizontalLineContent));
                        ArrowWidthPropertyDescriptor.AddValueChanged(this, (o, e) =>
                        {
                            RefreshUI();
                        });
                    }
                    private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
                    {
                        RefreshUI();
                    }
                    private void RefreshUI()
                    {
                        if (pol_left == null || pol_right == null || txt_text == null)
                        {
                            return;
                        }
                        double control_middle = this.ActualWidth / 2.0;
                        double txt_width_middle = txt_text.ActualWidth / 2.0;
                        double mid_left = control_middle - txt_width_middle;
                        double mid_right = control_middle + txt_width_middle;
                        if (Direction != DirectionAlignment.None && ContentDirection == ContentDirectionAlignment.Center)
                        {
                            if (mid_left  
            

            DirectionAlignment.cs

            #region 文件版本信息
            /*
             * 创建者:Zekang Hao
             * 邮箱:admin@haozekang.com
             * 创建时间:2024/6/27 17:01:16
             * 版本:1.0.0.0
             * 描述:
             */
            #endregion
            namespace WpfApp3
            {
                /// 
                /// 描述
                /// 
                public enum DirectionAlignment
                {
                    None,
                    Left,
                    Right,
                }
            }
            

            ContentDirectionAlignment.cs

            #region 文件版本信息
            /*
             * 创建者:Zekang Hao
             * 邮箱:admin@haozekang.com
             * 创建时间:2024/6/28 11:17:07
             * 版本:1.0.0.0
             * 描述:
             */
            #endregion
            namespace WpfApp3
            {
                /// 
                /// 描述
                /// 
                public enum ContentDirectionAlignment
                {
                    None,
                    Top,
                    Center,
                    Bottom,
                    TopLeft,
                    TopRight,
                    BottomLeft,
                    BottomRight,
                }
            }
            

            使用方法

                
                    
                        
                        
                    
                    
                    
                    
                    
                        
                            
                                
                                
                                    
                                    
                                    
                                
                            
                        
                    
                
            
            
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]