ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

WPF实现新手提示功能

2021-06-27 16:32:48  阅读:231  来源: 互联网

标签:控件 提示 hintWin new var 新手 WPF guide 气泡


一、概要

本篇文章分享一个新手界面提示的案例,我们经常会在各种app中会遇到不断让你点下一步引导你使用客户端的提示,根据不同的参数配置显示不同提示气泡的样式。这里就分享一下在WPF中如何去实现,我们先看下面的效果。

文章中只出现了部分关键代码全部代码在,源码地址在Github上。 https://github.com/JusterZhu/2021PlanJ

二、思路

通过上图显示的内容我做了以下分析:

开始之前我们先明白几个概念,不然思路不清楚后面的事情很难做。

  • 目标控件 - 指的是我们需要解释提示的控件
  • 气泡 - 具体的提示内容,同时支持下一步
  • 线 - 将气泡和目标控件连接起来,达到视觉辅助
  • 位置、样式 - 通过简单算法计算出目标控件和提示气泡的位置并用线连接起来,且初始化线和气泡的样式。

1.半透明灰的遮罩层

<Window
     x:Name="gw"
     Title="GuideWin"
     AllowsTransparency="True"
     Background="#01FFFFFF"
     ShowInTaskbar="False"
     WindowStyle="None"
     mc:Ignorable="d">
    <Grid>
        <Border
        x:Name="border"
        BorderBrush="White"
        BorderThickness="2"
        CornerRadius="5"
        Opacity="0.8">
        <Border.Effect>
            <DropShadowEffect
                BlurRadius="8"
                ShadowDepth="0"
                Color="#FF414141" />
        </Border.Effect>
        <Border
            Margin="0"
            Background="Black"
            CornerRadius="5"
            Opacity="0.5" />
        </Border>
        <Canvas x:Name="canvas" />
    </Grid>
 </Window>

这里会定义一个window将它的样式设置透明等样式便于覆盖到其他窗体上,遮罩层使用的是border控件,为什么选择它因为有两个特性。

  • 第一个是因为它支持圆角和方角可以适应不同的窗体样式。
  • 第二个它支持阴影效果。

2.提示气泡

<UserControl
        d:DesignHeight="450"
        d:DesignWidth="800"
        Background="Transparent"
        mc:Ignorable="d">
    <Viewbox   MouseLeftButtonDown="Viewbox_MouseLeftButtonDown">
    <Grid>
        <Border
            x:Name="RootBrd"
            Background="Transparent"
            BorderThickness="1">
            <TextBlock
                x:Name="ContentTxb"
                Margin="3"
                Background="Transparent" />
        </Border>
     </Grid>
     </Viewbox>
</UserControl>

这里会定义一个UserControl,这个UserControl是用来当做气泡显示具体内容的,也方便"嵌入"到窗体中使用。后面如果需要一整套提示流程时创建一个集合装好这些一步步初始化好的提示气泡的样式和内容即可。

3.线和气泡出现的位置和样式

public void StartGuide(GuideModel guide)
    {
        var focusElemt = guide.UserControl;
        var point = focusElemt.TransformToAncestor(Window.GetWindow(focusElemt)).Transform(new Point(0, 0));
        var rectangleGeometry = new RectangleGeometry();
        rectangleGeometry.Rect = new Rect(0, 0, this.Width, this.Height);
        borderGeometry = Geometry.Combine(borderGeometry, rectangleGeometry, GeometryCombineMode.Union, null);
        border.Clip = borderGeometry;
        var rectangleGeometry1 = new RectangleGeometry();
        rectangleGeometry1.Rect = new Rect(point.X - 5, point.Y - 5, focusElemt.ActualWidth + 10, focusElemt.ActualHeight + 10);
        borderGeometry = Geometry.Combine(borderGeometry, rectangleGeometry1, GeometryCombineMode.Exclude, null);
        border.Clip = borderGeometry;
        InitHintControl(guide, point, focusElemt.ActualWidth, focusElemt.ActualHeight, guide.IsNear);
        currentStep++;
    }

画线的思路:线有两个端点,我在这里称为线的起始点和线的终点,线的起始点出现的位置通常是“目标控件”的高的二分之一处,所以通过TransformToAncestor方法拿到控件坐标之后从左上角的0,0点的位置在带入控件的宽高计算出线起始点的坐标。

/// <summary>
    /// 计算相对位置
    /// </summary>
    /// <param name="startPoint">已知起始点坐标</param>
    /// <param name="angle">相对位置角度</param>
    /// <param name="bevel">相对距离</param>
    /// <returns></returns>
    private Point CalculateRelativePoint(Point startPoint, int angle, double bevel)
    {
        var radian = angle * Math.PI / 180;
        var xMargin = Math.Cos(radian) * bevel;
        var yMargin = Math.Sin(radian) * bevel;
        return new Point(startPoint.X + xMargin, startPoint.Y + yMargin);
    }

气泡出现的位置实现思路: 1.知道起始位置,2.知道相对位置的角度,3.知道相对距离,4.计算出相对位置

公式为:

  • 相对半径 =(角度 * π / 180);
  • 相对位置X轴坐标 = Cos(相对半径)* 相对距离;
  • 相对位置Y轴坐标 = Sin(相对半径)* 相对距离;

最终得出X和Y的值,从而确认到气泡的初始位置和线的结束位置。

/// <summary>
    /// 初始化气泡内容及位置
    /// </summary>
    /// <param name="guide"></param>
    /// <param name="point"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="isNear"></param>
    private void InitHintControl(GuideModel guide, Point point, double width, double height, bool isNear)
    {
        hintWin = new Hint(guide);
        hintWin.NextHintEvent -= OnNextHintEvent;
        hintWin.NextHintEvent += OnNextHintEvent;
        canvas.Children.Add(hintWin);
        var startPoint = new Point { X = point.X + width + 5, Y = point.Y + height / 2 };
        var relativePoint = CalculateRelativePoint(startPoint, 45 + guide.Angle, 100);
        if (isNear)
        {
            var nearRelativePoint = CalculateRelativePoint(point, 45 + guide.Angle, 1);
            Canvas.SetLeft(hintWin, nearRelativePoint.X);
            Canvas.SetTop(hintWin, nearRelativePoint.Y);
        }
        else
        {
            Canvas.SetLeft(hintWin, relativePoint.X - 5);
            Canvas.SetTop(hintWin, relativePoint.Y - hintWin.Height / 2);
        }

        if (guide.Direct == Direct.Visible)
        {
            InitPath(startPoint, relativePoint, 3, guide.PathColor, guide.BulgeStyle);
        }
    }

    /// <summary>
    /// 初始化连接气泡的线样式及位置
    /// </summary>
    /// <param name="startPoint"></param>
    /// <param name="relativePoint"></param>
    /// <param name="pathThickness"></param>
    /// <param name="pathColor"></param>
    /// <param name="bulgeStyle"></param>
    private void InitPath(Point startPoint, Point relativePoint,
        int pathThickness = 1, string pathColor = "#000000", BulgeStyle bulgeStyle = BulgeStyle.Dotted)
    {
        var path = new Path();
        var pathGeometry = new PathGeometry();
        var pathFigure = new PathFigure();
        pathFigure.StartPoint = startPoint;
        var segmentCollection = new PathSegmentCollection();
        segmentCollection.Add(new LineSegment() { Point = relativePoint });
        pathFigure.Segments = segmentCollection;
        pathGeometry.Figures = new PathFigureCollection() { pathFigure };
        path.Data = pathGeometry;
        path.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(pathColor));
        if (bulgeStyle == BulgeStyle.Dotted)
        {
            path.StrokeDashArray = new DoubleCollection { 2, 4 };
        }
        path.StrokeThickness = pathThickness;
        canvas.Children.Add(path);
    }

4.下一步功能

下一步这个功能大致的思路就是,不断的从“步骤集合”中取出下一个元素。根据对应的model初始化好下一步提示气泡里的内容即可,当走到最后一个元素时通常会是最后一步那么直接关闭掉“遮罩层窗体”即可。

/// <summary>
    /// 下一步具体实现
    /// </summary>
    private void OnNextHintEvent()
    {
        if (_guideModels.Count == 0) return;
        var beforGuide = _guideModels[currentStep - 1];
        if (_guideModels.Count == currentStep)
        {
            if (beforGuide.Callback != null)
            {
                beforGuide.Callback.Invoke();
            }
            else
            {
                EndStep();
            }
            return;
        }
        var currentGuide = _guideModels[currentStep];
        if (beforGuide != null)
        {
            canvas.Children.Clear();
            if (beforGuide.Callback != null)
            {
                beforGuide.Callback.Invoke();
            }
        }
        if (currentGuide != null)
        {
            StartGuide(currentGuide);
        }
    }

 

发布于刚刚

标签:控件,提示,hintWin,new,var,新手,WPF,guide,气泡
来源: https://www.cnblogs.com/justzhuzhu/p/14940853.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有