この記事は、元の著者の許可を得て、オリジナルの方法で2回共有し、再投稿、共有を歓迎します。
オリジナルタイトル:The Search
原文へのリンク:https//www.cnblogs.com/ZXdeveloper/p/8391864.html
この2日間は忙しくなかったので、簡単な初心者ガイド付きデモを作りました。プロジェクトに適用されないので、それは非常に粗い、つまり、必要としている人に、アイデアだけを与えることです。
初心者ガイド機能は、ページ上のアクションの順序、最初のステップで何をすべきか、2番目のステップで何をすべきか、などをユーザーに伝え、最終的に初心者ガイドページを閉じます。
私の習慣では、まず結果を見てみましょう。

効果表示は非常に簡単で、ユーザーに操作するコントロールにプロンプトを表示するように指示します。
この機能を実現するには、次のような考え方があります。
1.窓のマスク
将主窗体进行遮罩,半透明的效果,常用的做遮罩的话,一般是设置一个底色,然后设置透明度,类似于这篇博客 WPF 透明窗体制作,但是,在实际的操作用就会遇到问题,如果使用正常的半透明方式的话,黄色框部分,是不发透出白色的主窗体内容的,因为已经有底色了,所以,本文使用的半透明方法是 Clip 的擦除,效果如下图,参考的博客WPF 用 Clip 属性实现蒙板特效。

透明なウィンドウを作成します。
<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>
XAMLコードから、Backgroundプロパティは“Transparent”ではなく“#01 FFFFFF”を使用していることがわかります。Transparentを使用すると、それは本当に透明であり、メインフォーム内のコントロールに直接クリックすることができます。これは望ましくないので、ほぼ透明な色である“#01 FFFFFF”が設定されています。
2.操作するコントロールを表示する
コントロールを指示したい場合は、まずコントロールを囲む必要があります。囲む最初のタスクは、現在のフォーム内のコントロールの座標位置を取得することです。
Point point = fe.TransformToAncestor(Window.GetWindow(fe)).Transform(new Point(0, 0));
座標を取得した後、制御を囲む必要がありますが、私の方法は、現在の座標-5、幅と高さ+10を取ることです。空白の領域を描画するために、実際には、この部分は消去を指す必要があります。
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)UCガイドを作成する
UCのガイドは、設計がより簡単で、実際には非常に簡単です。

パスを使用して範囲を描画しますが、点線ボックス、最初のアイデアは行を使用することですが、あまりにも面倒だと感じて、StrokeDashArrayこのプロパティを直接使用して、Strokeはパス自体の境界線であり、もちろん、本当に境界線なので、マージンやパディングを設定するのは良くないので、最後のアプローチは、外側の領域に領域を描画することですが、この領域には境界線が含まれていません。
<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>
コンテンツの表示部分はテキストブロックであり、問題が発生したとき、テキストブロックは改行のために幅を持たなければなりませんが、最も外側の層はViewboxなので、UC WidthまたはActualWidthを取得しようとしても、できないので、最終的な解決策は、フォームの幅と高さを入力し、UCの幅と高さを外部設定することです。
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;
}
IV.次のステップへのトリガー
次のステップをトリガーすることは、サブコントロールがメインコントロールのイベントを呼び出すことに相当します。その場合、メインフォームで特定のメソッドを実装するデリゲートを記述することです。
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);
}
現在のListコレクションのインデックスを記録するために外部でインデックス変数を宣言したいのですが、まず、現在のコンテンツが空でないかどうかを判断し、空であれば消去します。消去しない場合は、キューボックスの束が表示され、リストコレクション内の最後のコントロールかどうかを判断し、そうであれば、“次へ”ボタンは表示されません。
V.拡張セクション
小さなデモなので、メインフォームがボーダーレスでない場合、値の位置に問題があるなど、いくつかの問題が見つかりましたが、解決されませんでした。
これは、ポップアップガイドフォームがメインフォームのサイズを取得するためですが、ポイントがコントロール座標位置を取得するとき、メインフォームにはヘッドが含まれていません。マスクにはヘッドがないので、位置決めが間違っています。私はまだ良い解決策を見つけていません。

ガイドコンテンツを表示する部分は、Gridに置き換えることもできます。そうすれば、User Controlに渡すことができ、興味のある友人は自分で変更できます。
源码:Demo
駅長の利用経験
效果确实不错,站长通过原作者的源码改了一点(代码),需要遮罩的控件换成 Image 控件也是相同效果,nice:
