前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >win10 uwp 气泡 WPF 气泡

win10 uwp 气泡 WPF 气泡

作者头像
林德熙
发布于 2018-09-18 09:43:10
发布于 2018-09-18 09:43:10
1.5K00
代码可运行
举报
文章被收录于专栏:林德熙的博客林德熙的博客
运行总次数:0
代码可运行

如果做聊天工具,需要气泡。 本文,如果写一个气泡控件需要如何做

WPF 气泡

先说如何在 WPF 做一个气泡。

可以看到,气泡就是一个和 Grid 差不多的东西,只是有边框,边框是一个气泡

如何去写一个外框?

可以新建一个类,继承 Decorator ,就可以啦

现在的难点是如何获得子元素的大小。

可以看到一个气泡是尖的气泡和一个矩形组成

我做了一些修改,先做一个像这样的气泡

气泡分为两部分,一个是尖头一个矩形

可以看到,尖头大小可以固定,但是矩形必须使用子控件的大小

于是先假如子元素的宽度是100,高度 50 ,这样来画一个气泡。

如何画一个三角?

假设尖头宽度 10 高度 5 ,那么可以看到第一个点是 (0,5) 第二个点是 (5,0) 第三个点是 (10,5)

需要知道, WPF 使用的布局不是和以前课本说的一样

但是除了尖头,还需要添加矩形的距离

添加的距离是矩形宽度的一半减去5,看到这里一般不会觉得有困难。

于是添加到实际的值,这里矩形宽度为 100 于是最后的值就是 (45,5) ,第二个点是 (50,0) 第三个点是 (55,5)

接下来就是计算矩形的值,矩形的值就是 x=0 y=5 ,宽度 100 高度 50

计算出来,就需要画出来。

PathFigure 可以画线,也就是通过他给他三个点就好

把上面的几个点写出来

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                            new PathFigure
                            {
                                IsClosed = false,
                                StartPoint = new Point(45, 5),
                                Segments = new PathSegmentCollection()
                                {
                                    new LineSegment(new Point(50), 0), true),
                                    new LineSegment(new Point(55, 5), true)
                                }
                            }

但需要把三角加到 PathGeometry 才可以显示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                    Geometry1 = new PathGeometry()
                    {
                        Figures = new PathFigureCollection()
                        {
                            new PathFigure
                            {
                                IsClosed = false,
                                StartPoint = new Point(45, 5),
                                Segments = new PathSegmentCollection()
                                {
                                    new LineSegment(new Point(50), 0), true),
                                    new LineSegment(new Point(55, 5), true)
                                }
                            }
                        }
                    }

这样写在界面path,可以看到显示出来三角形,因为没有设置线条,所以没有把鼠标移到三角是看不到的

接着需要画矩形

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                        Geometry2 = new RectangleGeometry(new Rect(0, 5, 100,
                            50)
                        , 0, 0)

但是如何直接把两个显示,看起来是不对的,因为是一个矩形和三角,不是气泡

所以组合一下图形就好

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                 var cg = new CombinedGeometry
                {
                    Geometry1 ,
                    Geometry2 ,
                    GeometryCombineMode = GeometryCombineMode.Xor
                };

但是实际需要获得子元素的大小,也需要显示,那么显示可以先重写 OnRender

从 OnRender 画出的方法很简单

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                GuidelineSet guideLines = new GuidelineSet();
                drawingContext.PushGuidelineSet(guideLines);
                drawingContext.DrawGeometry(brush, pen, cg);

其中的 颜色自己定义,cg就是上面的图形。

但是这样的自定义控件需要设置宽高,如何使用子元素的宽高加上自己的padding?

如果只是重新显示,那么界面是不知道气泡的大小,所以得到的是没显示,为了让气泡可以显示,先给他一个宽高,这样就可以演示。

但是我需要直接就写如何获取子元素的大小,把他作为气泡的大小。

获取子元素可以通过重写 MeasureOverride

第一步,测量子元素,通过子元素可以获得高度宽度

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                    Child.Measure(constraint);

定义自己的 padding ,这个值先随意给,表示气泡离元素距离

那么计算得到自己的大小就是 子元素的宽高加上 padding 加上气泡需要的外框

因为对于高度,需要加上气泡的高度 5 才可以,代码很容易就看懂,我就不说啦

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        protected override Size MeasureOverride(Size constraint)
        {
            Thickness padding = Padding;
            Size result = new Size();
            if (Child != null)
            {
                //测量子控件的大小
                Child.Measure(constraint);

                result.Width = Child.DesiredSize.Width + padding.Left + padding.Right;
                result.Height = Child.DesiredSize.Height + padding.Top + padding.Bottom + 5;
            }
            return result;
        }

拿到了子控件的高度,还需要重写自己的布局

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            protected override Size ArrangeOverride(Size arrangeSize)
        {
            Thickness padding = Padding;
            if (Child != null)
            {
                Size result = new Size();
                Child.Arrange(new Rect(new Point(padding.Left, 5 + padding.Top), Child.DesiredSize));
                result.Width = Child.DesiredSize.Width + padding.Left + padding.Right;
                result.Height = Child.DesiredSize.Height + padding.Top + padding.Bottom + 5;
                return result;
            }
            return arrangeSize;
        }

这里出现很多个 5 ,意思就是气泡高度,为了设置气泡高度,所以给他一个属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
         public double HeadHeight
        {
            get
            {
                return (double)GetValue(HeadHeightProperty);
            }
            set
            {
                SetValue(HeadHeightProperty, value);
            }
        }

         /// <summary>
        ///     标识 <see cref="HeadHeight" /> 的依赖项属性。
        /// </summary>
        public static readonly DependencyProperty HeadHeightProperty = DependencyProperty.Register(
            "HeadHeight", typeof(double), typeof(PeakedAdorner), new PropertyMetadata(5d));

现在可以修改一下代码,让他可以自动适应

矩形的宽高可以通过自己的大小计算

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Geometry2 = new RectangleGeometry(new Rect(0, HeadHeight, ActualWidth,
                            ActualHeight - HeadHeight)
                        , 0, 0)

可以看到 坐标没有变化,有变化的是高度,宽度,可以通过获得自己的大小设置,因为在计算大小已经从子元素加上自己的需要大小,所以得到的大小可以设置

那么现在的 OnRender 可以写为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            protected override void OnRender(DrawingContext drawingContext)
        {
           
            
                Pen pen = new Pen();
                pen.Brush = BorderBrush;
                pen.Thickness = BorderThickness;

                var leftpad = (ActualWidth - HeadWidth) / 2;
                var cg = new CombinedGeometry
                {
                    Geometry1 = new PathGeometry()
                    {
                        Figures = new PathFigureCollection()
                        {
                            new PathFigure
                            {
                                IsClosed = false,
                                StartPoint = new Point(leftpad, HeadHeight),
                                Segments = new PathSegmentCollection()
                                {
                                    new LineSegment(new Point(leftpad + (HeadWidth / 2), 0), true),
                                    new LineSegment(new Point(leftpad + HeadWidth, HeadHeight), true)
                                }
                            }
                        }
                    },
                    Geometry2 = new RectangleGeometry(new Rect(0, HeadHeight, ActualWidth,
                            ActualHeight - HeadHeight)
                        , 0, 0),
                    GeometryCombineMode = GeometryCombineMode.Xor
                };


                GuidelineSet guideLines = new GuidelineSet();
                drawingContext.PushGuidelineSet(guideLines);
                drawingContext.DrawGeometry(Background, pen, cg);
            
        }

BorderBrush 也是自己设置的 ,BorderThickness 也是,于是继续设置背景色 Background 和其它的如圆角

现在看起来的属性是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
         public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.Register("Background", typeof(Brush), typeof(PeakedAdorner)
                , new PropertyMetadata(new SolidColorBrush(Color.FromRgb(255, 255, 255))));

        public static readonly DependencyProperty PaddingProperty =
            DependencyProperty.Register("Padding", typeof(Thickness), typeof(PeakedAdorner)
                , new PropertyMetadata(new Thickness(10, 5, 10, 5)));

        public static readonly DependencyProperty BorderBrushProperty =
            DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(PeakedAdorner)
                , new PropertyMetadata(new SolidColorBrush(Colors.Black)));

        public static readonly DependencyProperty BorderThicknessProperty =
            DependencyProperty.Register("BorderThickness", typeof(double), typeof(PeakedAdorner),
                new PropertyMetadata(1d));


        /// <summary>
        ///     标识 <see cref="HeadWidth" /> 的依赖项属性。
        /// </summary>
        public static readonly DependencyProperty HeadWidthProperty = DependencyProperty.Register(
            "HeadWidth", typeof(double), typeof(PeakedAdorner), new PropertyMetadata(0d));


        /// <summary>
        ///     标识 <see cref="HeadHeight" /> 的依赖项属性。
        /// </summary>
        public static readonly DependencyProperty HeadHeightProperty = DependencyProperty.Register(
            "HeadHeight", typeof(double), typeof(PeakedAdorner), new PropertyMetadata(5d));

        public static readonly DependencyProperty CornerRadiusProperty =
            DependencyProperty.Register("CornerRadius", typeof(CornerRadius)
                , typeof(PeakedAdorner), new PropertyMetadata(new CornerRadius(0)));

        /// <summary>
        ///     背景色
        /// </summary>
        public Brush Background
        {
            get
            {
                return (Brush)GetValue(BackgroundProperty);
            }
            set
            {
                SetValue(BackgroundProperty, value);
            }
        }

        /// <summary>
        ///     内边距
        /// </summary>
        public Thickness Padding
        {
            get
            {
                return (Thickness)GetValue(PaddingProperty);
            }
            set
            {
                SetValue(PaddingProperty, value);
            }
        }

        /// <summary>
        ///     边框颜色
        /// </summary>
        public Brush BorderBrush
        {
            get
            {
                return (Brush)GetValue(BorderBrushProperty);
            }
            set
            {
                SetValue(BorderBrushProperty, value);
            }
        }

        /// <summary>
        ///     边框大小
        /// </summary>
        public double BorderThickness
        {
            get
            {
                return (double)GetValue(BorderThicknessProperty);
            }
            set
            {
                SetValue(BorderThicknessProperty, value);
            }
        }

        /// <summary>
        ///     获取或设置尖角width
        /// </summary>
        public double HeadWidth
        {
            get
            {
                return (double)GetValue(HeadWidthProperty);
            }
            set
            {
                SetValue(HeadWidthProperty, value);
            }
        }

        /// <summary>
        ///     获取或设置尖角
        /// </summary>
        public double HeadHeight
        {
            get
            {
                return (double)GetValue(HeadHeightProperty);
            }
            set
            {
                SetValue(HeadHeightProperty, value);
            }
        }

        /// <summary>
        ///     边框大小
        /// </summary>
        public CornerRadius CornerRadius
        {
            get
            {
                return (CornerRadius)GetValue(CornerRadiusProperty);
            }
            set
            {
                SetValue(CornerRadiusProperty, value);
            }
        }

如何使用?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            <local:PeakedAdorner CornerRadius="5" Margin="0,0,0.4,-0.2" >
            <TextBlock Text="林德熙"></TextBlock>
        </local:PeakedAdorner>

这样就好了,里面的控件可以是任何的,你想要的,如Grid ,textBox

现在看起来就是这样

这样就做好气泡,如果需要气泡显示在其他的,那么可以通过自己计算,所有的值需要放在哪


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
洛谷------P1036 [NOIP2002 普及组] 选数
这里可以把问题转化一下,变为:求解出从n个数中任意选k个数构成的所有组合(组合不看元素之间的顺序,与排列区分开来),而我们这里只需要在找到一种组合时,计算当前组合的和,看是否等于素数,额外用一个变量sum,记录和为素数的个数,如果当前和为素数,那么sum++;
大忽悠爱学习
2021/11/15
4270
2018 蓝桥杯省赛 B 组模拟赛(五)题目及解析
A. 结果填空:矩阵求和 给你一个从 n×n 的矩阵,里面填充 1 到 n×n 。例如当 n 等于 3 的时候,填充的矩阵如下。 1 2 3 4 5 6 7 8 9 现在我们把矩阵中的每条边的中点连起来,这样形成了一个新的矩形,请你计算一下这个新的矩形的覆盖的数字的和。比如,n = 3 的时候矩形覆盖的数字如下。 2 4 5 6 8 那么当 n 等于 101 的时候,矩阵和是多少? 题目解析: 这题画一个7×7的矩阵观察起来会比较直观。矩形边中点的连线包括边上的 元素和所有处于边界点之间的元素。在找
Woodson
2018/06/29
5520
ACM札记四
输入一个正整数n(1 <n≤10),再输入n 个整数,将最小值与第一个数交换,最大值与最后一个数交换,然后输出交换后的n 个数。
慕白
2018/08/03
1K0
ACM札记四
2019年安徽大学ACM/ICPC实验室新生赛
A.素数分布函数\pi (n)π(n)表示小于或等于n的素数的数目。例如\pi (10)=4π(10)=4(2,3,5,7是素数)。这个函数涉及到许多高等数论的内容,甚至和黎曼猜想挂钩,目前还有很多数学家正在不断探索其中的奥秘。千里之行始于足下,现在你开始关心一个问题:在正整数域中素数的分布是怎么样的。为了探索这个问题,你需要计算出一些\pi (n)π(n)的值。
杨鹏伟
2020/09/11
6580
C语言经典编程题100例 31~40
31、题目:请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续判断第二个字母。
C you again
2022/08/22
1.4K0
递归函数及例题_递归树求解递归式例题
大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说递归函数及例题_递归树求解递归式例题,希望能够帮助大家进步!!!
Java架构师必看
2022/07/19
6970
递归函数及例题_递归树求解递归式例题
华北水利水电大学第十届ACM/ICPC程序设计新生赛题解
语言使用的C++;题目总体来说考察的是思维,和基础的编码能力,并没有使用到什么算法,只要认真写应该问题不大;
啊QQQQQ
2025/01/20
230
华北水利水电大学第十届ACM/ICPC程序设计新生赛题解
求大于整数m且紧靠m的k个素数 及 判断一个数是否为素数的方法
题目:   请编写一个函数void fun(int m,int k ,int xx[]),该函数的功能是:将大于整数m且紧靠m的k个素数存入xx所指的数组中。   例如,若输入:17,5,则应输出:19,23,29,31,37。 质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。 1 #include<stdio.h> 2 #include<math.h> 3 4 bool isPrime(int n) 5 { 6 for(int i = 2
猿人谷
2018/01/17
1.4K0
基础知识_算法笔记
1342. Number of Steps to Reduce a Number to Zero
yifei_
2022/11/14
1.6K0
基础知识_算法笔记
你还不会暴力搜索吗,dfs(深度优先搜索)详解,看这一篇就够啦
🚀欢迎互三👉: 2的n次方_💎💎 🚀所属专栏:数据结构与算法学习⭐⭐
2的n次方
2024/10/15
1.6K0
你还不会暴力搜索吗,dfs(深度优先搜索)详解,看这一篇就够啦
基础数论总结
从右往左。可以一直递推,然后到最后一项,然后快速幂求矩阵,矩阵最终的结果就是所求结果。更新:java的矩阵通用乘法可以表示为,可以将下列代码替换道ac代码中:
bigsai
2019/09/24
7460
基础数论总结
0x02|递推与递归 排列组合题型合集
排列组合问题是算法中比较常见的问题,这种题型的难点在于组合的数据量通常比较大,朴素写法的复杂度往往达到指数级别,一般都需要优化处理。看题之前,我们先来回顾一下排列和组合的定义。
ACM算法日常
2021/01/28
6310
今天发疯,写一下学校的函数作业
快到期末了,学校作业应该就是考试原题吧hhhh 抱个佛jio啥的,各位客官走过路过不要错过!
用户11039545
2024/03/28
1050
今天发疯,写一下学校的函数作业
菜鸟刷题Day1
自守数是指一个数的平方的尾数等于该数自身的自然数。例如:25^2 = 625,76^2 = 5776,9376^2 = 87909376。请求出n(包括n)以内的自守数的个数
始终学不会
2023/03/28
2170
C语言程序设计之小系统
本系统是一个综合管理系统,旨在将本学期学习的各类小程序整合在一起,提供一个用户友好的界面,方便用户选择并解决不同类别的问题。系统通过菜单展示可用的功能,用户可以根据自己的需求选择相应的类别,系统将显示具体的问题内容并提供解答。
LucianaiB
2025/01/20
530
C语言程序设计之小系统
C语言入门经典题目及其答案
【程序1】 题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 1.程序分析:可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。 2.程序源代码:
风骨散人Chiam
2020/10/28
2.9K0
牛客刷题系列之初阶版(自守数,返回小于 N 的质数个数,第一个只出现一次的字符)
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情
雪芙花
2022/10/31
3080
牛客刷题系列之初阶版(自守数,返回小于 N 的质数个数,第一个只出现一次的字符)
C语言经典例题100
题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
C语言与CPP编程
2021/04/02
2.9K0
C语言经典例题100
C++数学与算法系列之初等数论
在日常生活中,数通常出现在标记(如公路、电话和门牌号码)、序列号和编码上。在数学里,数的定义延伸至包含如分数、负数、无理数、超越数及复数等抽象化的概念。
一枚大果壳
2022/12/20
3930
C++数学与算法系列之初等数论
练习10—素数判断
题目 编写一个判断素数的函数,在主函数输入一个整数,输出该数是否为素数的信息。 解题步骤 (1)函数思想; (2)素数定义; (3)变量定义; (4)接收用户输入; (5)判断输出; Java import java.util.Scanner; public class Demo { public static boolean isPrime(int input) { int n = (int) Math.sqrt(input); if (input =
攻城狮杰森
2022/06/03
2880
推荐阅读
相关推荐
洛谷------P1036 [NOIP2002 普及组] 选数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档