How does WPF make a novice guidance interface?

How does WPF make a novice guidance interface?

The novice guidance function tells the user the order of operations on the page, what to do in the first step, what to do in the second step, and so on, and then finally closes the novice guidance page.

最后更新 5/20/2022 6:47 AM
眾尋
预计阅读 8 分钟
分类
WPF
标签
.NET WPF novice guide

This article is shared twice in an original way with the authorization of the original author. Welcome to reprint and share.

Original author: Zhong Xun

Original link: www.cnblogs.com/ZXdeveloper/p/8391864.html


I haven't been busy these two days, so I made a simple novice guide Demo. Because it is not applied on projects, it is very crude, which is just to give people who need it an idea.

The novice guidance function tells the user the order of operations on the page, what to do in the first step, what to do in the second step, and so on, and then finally closes the novice guidance page.

In my habit, I'd better show you the effect first.

The effect display is very simple, just to give a prompt to the control to tell the user to operate.

To achieve this functionalization, the idea is roughly the following:

1. Mask the form

将主窗体进行遮罩,半透明的效果,常用的做遮罩的话,一般是设置一个底色,然后设置透明度,类似于这篇博客 WPF 透明窗体制作,但是,在实际的操作用就会遇到问题,如果使用正常的半透明方式的话,黄色框部分,是不发透出白色的主窗体内容的,因为已经有底色了,所以,本文使用的半透明方法是 Clip 的擦除,效果如下图,参考的博客WPF 用 Clip 属性实现蒙板特效

First set up a transparent form

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

From the XAML code, we can see that the Background attribute is not "Transparent" but "#01FFFFFF", because if you use Transparent, it is really transparent and you can directly click on the control in the main form. This is what we don't want, so we set "#01FFFFFF", a nearly transparent color.

2. Display the control to be operated

Since you want to guide a certain control, you need to circle the control first. The primary task of circling is to obtain the coordinate position of the control in the current form.

Point point = fe.TransformToAncestor(Window.GetWindow(fe)).Transform(new Point(0, 0));

After obtaining the coordinates, you need to circle the control. My method is to take the current coordinates of-5, width and height +10to draw a blank area. In fact, this part should refer to erase.

RectangleGeometry rg1 = new RectangleGeometry();
rg1.Rect = new Rect(point.X - 5, point.Y - 5, fe.ActualWidth + 10, fe.ActualHeight + 10);
borGeometry = Geometry.Combine(borGeometry, rg1, GeometryCombineMode.Exclude, null);

3. Draw a guiding UC

Guide UC, it is more convenient to design, but it actually looks quite simple

Just use Path to draw a range, but the dotted box. The original idea was to use Line to do it, but it felt too troublesome, so I directly used the StrokeDashArray attribute. Stroke is the border line of the Path itself. Of course, it is really a border. Therefore, it is not easy to set Margin or Padding. Therefore, the final practice is to draw another area on the outer layer, except that this area does not contain the border line, and the filling color is the same

<Path Fill="#FF2FBEED">
  <Path.Data>
    <GeometryGroup>
      <PathGeometry Figures="M 8,22 A 12,12 0 1 1 22,8 L 102 8 L 102 62 L 8 62 Z" />
    </GeometryGroup>
  </Path.Data>
</Path>
<Path StrokeThickness="1" Stroke="White" StrokeDashArray="2,1" Fill="#FF2FBEED">
  <Path.Data>
    <GeometryGroup>
      <PathGeometry Figures="M 10,20 A 10,10 0 1 1 20,10 L 100 10 L 100 60 L 10 60 Z" />
    </GeometryGroup>
  </Path.Data>
</Path>

The part that displays the content is a Textblock. At that time, a problem was encountered, which was the problem of wrapping. The Textblock must have a Width before it can wrap. However, since the outermost layer is a Viewbox, I tried to obtain the Width or ActualWidth of the UC, but it was not possible. Therefore, the final solution was to pass in the width and height of a form instead of setting the width and height of this UC externally.

public HintUC(string xh, string content, Visibility vis = Visibility.Visible, int width = 260, int height = 160)
{
    InitializeComponent();
    this.Width = width;
    this.Height = height;
    this.tb_nr.Width = width / 4;

    this.tb_xh.Text = xh;
    this.tb_nr.Text = content;
    this.btn_next.Visibility = vis;
}

4. Trigger for the next step

Triggering the next step is equivalent to an event where the child control calls the main control. In this case, it is writing a delegate to implement the specific method in the main form.

private void show(int xh, FrameworkElement fe, string con, Visibility vis = Visibility.Visible)
{
    Point point = fe.TransformToAncestor(Window.GetWindow(fe)).Transform(new Point(0, 0));//获取控件坐标点

    RectangleGeometry rg = new RectangleGeometry();
    rg.Rect = new Rect(0, 0, this.Width, this.Height);
    borGeometry = Geometry.Combine(borGeometry, rg, GeometryCombineMode.Union, null);
    bor.Clip = borGeometry;

    RectangleGeometry rg1 = new RectangleGeometry();
    rg1.Rect = new Rect(point.X - 5, point.Y - 5, fe.ActualWidth + 10, fe.ActualHeight + 10);
    borGeometry = Geometry.Combine(borGeometry, rg1, GeometryCombineMode.Exclude, null);

    bor.Clip = borGeometry;

    HintUC hit = new HintUC(xh.ToString(), con, vis);
    Canvas.SetLeft(hit, point.X + fe.ActualWidth + 3);
    Canvas.SetTop(hit, point.Y + fe.ActualHeight + 3);
    hit.nextHintEvent -= Hit_nextHintEvent;
    hit.nextHintEvent += Hit_nextHintEvent;
    can.Children.Add(hit);

    index++;
}
private void Hit_nextHintEvent()
{
    if (list[index - 1] != null)
    {
        can.Children.Clear();
    }
    if (index == list.Count - 1)
        show(index + 1, list[index].Uc, list[index].Content, Visibility.Collapsed);
    else
        show(index + 1, list[index].Uc, list[index].Content);
}

We need to declare an index variable externally to record the index of the current List collection. First of all, we need to determine whether the current content is not empty. If so, we need to clear it. If not, we will see a bunch of prompt boxes. Then, we determine whether it is the last control in the List collection. If so, the "Next button" will no longer be displayed.

5. Expansion

Since it was a small Demo, some problems were found, but they were not solved. For example, if the main form is not borderless, there will be problems with positioning values.

This is because the pop-up boot form obtains the size of the main form, but when Point obtains the coordinate position of the control, the main form does not contain a head. Since the mask has no head, there is a positioning error. I haven't found a good solution for this. If there is a god who knows how to solve it, please enlighten me. Thank you.

The part that displays the guidance content can also be replaced with a Grid, so that UserControl can be passed in, and interested friends can modify it themselves.

源码:Demo

Webmaster experience

效果确实不错,站长通过原作者的源码改了一点(代码),需要遮罩的控件换成 Image 控件也是相同效果,nice:

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 9/13/2025

Migration from WPF to Avalonia series: Why I have to migrate WPF programs to Avalonia

In the past few years, our host computer software has been mainly developed using WPF and WinForm. These technologies are really easy to use on the Windows platform, and they have also accompanied us through the stage of small-scale trial production to today's large-scale delivery. However, with the development of business and changes in customer needs, the single Windows technology stack has gradually become a hurdle that we must overcome.

继续阅读
同分类 / 同标签 1/26/2025

WPF internationalizes with custom XML files

This article describes in detail the methods of using custom XML files to achieve internationalization in WPF programs, including installing the necessary NuGet package, dynamically obtaining language lists, dynamically switching languages, using translation strings in code and xaml interfaces, etc. It also provides source code links to help developers easily internationalize WPF applications.

继续阅读