How to Create a New User Guide Interface in WPF?

How to Create a New User Guide Interface in WPF?

The new user guide functionality 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 close the new user guide page.

Last updated 5/20/2022 6:47 AM
眾尋
8 min read
Category
WPF
Tags
.NET WPF New User Guide

This article is republished as an original work with the author's permission. Feel free to share.

Original author: 眾尋

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


I had some free time in the past couple of days, so I created a simple new user guidance demo. Since it’s not for a real project, it’s quite rough—just intended to give those in need an idea.

The new user guidance feature tells users the order of operations on the page: first step, second step, and so on, until finally closing the guidance window.

As usual, let me show you the effect first.

The effect is very simple: it just highlights the controls that users need to interact with.

To implement this functionality, the general approach involves the following steps:

1. Mask Window

Overlay the main window with a semi-transparent effect. Common masking methods usually set a background color with transparency, similar to the blog post WPF Transparent Form Creation. However, in practice, issues arise. If a normal semi-transparent method is used, the yellow box area cannot show through the white background of the main window because the background color is already there. Therefore, this article uses a clipping method for semi-transparency, as shown in the following image. The reference blog is WPF Mask Effect Using Clip Property.

First, create a transparent window:

<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, you can see that the Background property is not set to "Transparent" but to "#01FFFFFF". Because if "Transparent" were used, it would be fully transparent, allowing clicks to pass through to the main window's controls, which we don’t want. So we set "#01FFFFFF", a nearly transparent color.

2. Display the Control to Operate

To guide the user to a specific control, we need to highlight it. The first task is to obtain the coordinates of the control relative to the current window.

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

After obtaining the coordinates, we need to outline the control. My approach is to take the current coordinates minus 5, and the width and height plus 10, to create an empty area—essentially a clipping region.

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 Guidance User Control (UC)

Designing the guidance UC is straightforward—the appearance is simple:

It uses a Path to draw a region. Initially, I thought of using a Line for the dashed border, but that seemed too cumbersome. So I directly used the StrokeDashArray property. Stroke is the border line of the Path itself. However, since it's truly the border, setting Margin or Padding is inconvenient. The final approach was to draw an outer region without a border line but with the same fill color.

<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 content area uses a TextBlock. One issue I encountered was line wrapping—the TextBlock needs an explicit Width to wrap text. Since the outermost container is a Viewbox, trying to get the UC's Width or ActualWidth didn't work. So the final solution was to pass the window's width and height into the UC instead of setting its width and height 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. Triggering the Next Step

Triggering the next step essentially involves the child control calling an event on the main control. This is done by writing a delegate and implementing the specific method in the main window.

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)); // Get control coordinates

    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 external index variable to track the current position in the List. First, check if the current content is not null; if so, clear it. If not cleared, you'll see a pile of tooltips. Then, determine whether it's the last control in the List. If it is, hide the "Next" button.

5. Extensions

Since this is a small demo, I noticed some issues but didn't solve them, such as coordinate positioning problems when the main window is not borderless.

This happens because the guidance window captures the main window's size, but when Point obtains the control's coordinates, the main window does not include its title bar. Since the mask lacks the title bar area, the positioning is off. I haven't found a good solution yet. If any expert knows how to fix this, please advise. Thank you.

The guidance content area could also be replaced by a Grid, allowing you to pass in a UserControl. Interested readers can modify it accordingly.

Source code: Demo

Webmaster's Experience

The effect is quite good. The webmaster modified the source code slightly (code). Replacing the target controls with Image controls also works fine—nice:

Keep Exploring

Related Reading

More Articles
Same category / Same tag 9/13/2025

Migration Series from WPF to Avalonia: Why I Must Migrate My WPF Application to Avalonia

In the past few years, our host computer software has mainly been developed using WPF and WinForm . These technologies work well on the Windows platform and have accompanied us from small-scale trial production to the current stage of large-scale delivery. However, with business development and changes in customer requirements, the single Windows technology stack has gradually become a hurdle we must overcome.

Continue Reading
Same category / Same tag 1/26/2025

Implementing Internationalization in WPF Using Custom XML Files

This article details the method of implementing internationalization in WPF applications using custom XML files, including installing the necessary NuGet packages, dynamically retrieving the language list, dynamically switching languages, using translated strings in code and XAML interfaces, and provides a source code link to help developers easily achieve internationalization in WPF applications.

Continue Reading