我应该如何“暂停”程序以向用户显示提示?

时间:2015-07-29 08:22:05

标签: c# wpf

当用户第一次运行我的程序时,我希望他们通过一系列提示。每次他们点击某个“检查点”时,程序会暂停其操作,背景会有点模糊(除了提示引用的窗口区域),并且提示将出现在顶部,解释如何使用它/做什么等。

我不知道该怎么称呼它,在我脑海里它被称为“教程提示”,但谷歌搜索任何与此相关的东西显示了大量的WPF / C#通用教程。

最好的方法是什么?我真的只是看着使用弹出窗口和控制它们何时可见?是否有更好/更优雅的解决方案或任何资源来帮助解决这个问题?

2 个答案:

答案 0 :(得分:6)

好吧,我想我可能已经投入了太多时间,但这听起来像是一个很酷的挑战:P

我创建了一个名为TipFocusDecorator的Decorator类来处理所有这些。

public class TipFocusDecorator : Decorator
{

    public bool IsOpen
    {
        get { return (bool)GetValue(IsOpenProperty); }
        set { SetValue(IsOpenProperty, value); }
    }
    // Using a DependencyProperty as the backing store for Open.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsOpenProperty =
        DependencyProperty.Register("IsOpen", typeof(bool), typeof(TipFocusDecorator), 
        new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsOpenPropertyChanged));


    public string TipText
    {
        get { return (string)GetValue(TipTextProperty); }
        set { SetValue(TipTextProperty, value); }
    }
    // Using a DependencyProperty as the backing store for TipText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TipTextProperty =
        DependencyProperty.Register("TipText", typeof(string), typeof(TipFocusDecorator), new UIPropertyMetadata(string.Empty));


    public bool HasBeenShown
    {
        get { return (bool)GetValue(HasBeenShownProperty); }
        set { SetValue(HasBeenShownProperty, value); }
    }

    // Using a DependencyProperty as the backing store for HasBeenShown.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HasBeenShownProperty =
        DependencyProperty.Register("HasBeenShown", typeof(bool), typeof(TipFocusDecorator), new UIPropertyMetadata(false));

    private static void IsOpenPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var decorator = sender as TipFocusDecorator;

        if ((bool)e.NewValue)
        {
            if (!decorator.HasBeenShown)
                decorator.HasBeenShown = true;

            decorator.Open();
        }

        if (!(bool)e.NewValue)
        {
            decorator.Close();
        }
    }

    TipFocusAdorner adorner;

    protected void Open()
    {
        adorner = new TipFocusAdorner(this.Child);
        var adornerLayer = AdornerLayer.GetAdornerLayer(this.Child);
        adornerLayer.Add(adorner);

        MessageBox.Show(TipText);  // Change for your custom tip Window
        IsOpen = false;
    }

    protected void Close()
    {
        var adornerLayer = AdornerLayer.GetAdornerLayer(this.Child);
        adornerLayer.Remove(adorner);
        adorner = null;
    }

}

必须在要关注的控件周围的XAML中使用此装饰器。它有三个属性:IsOpenTipTextHasBeenShownIsOpen必须设置为true才能显示焦点和提示窗口(并在提示窗口关闭时自动设置为false)。 TipText允许您定义必须在提示窗口中显示的文本。并且HasBeenShown会跟踪是否已显示提示窗口,因此它只显示一次。您可以对所有这些属性使用Bindings,也可以从代码隐藏中设置它们。

要创建焦点效果,此类使用另一个自定义Adorner,即TipFocusAdorner

public class TipFocusAdorner : Adorner
{
    public TipFocusAdorner(UIElement adornedElement)
        : base(adornedElement)
    {
    }

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        var root = Window.GetWindow(this);
        var adornerLayer = AdornerLayer.GetAdornerLayer(AdornedElement);
        var presentationSource = PresentationSource.FromVisual(adornerLayer);
        Matrix transformToDevice = presentationSource.CompositionTarget.TransformToDevice;

        var sizeInPixels = transformToDevice.Transform((Vector)adornerLayer.RenderSize);
        RenderTargetBitmap rtb = new RenderTargetBitmap((int)(sizeInPixels.X), (int)(sizeInPixels.Y), 96, 96, PixelFormats.Default);

        var oldEffect = root.Effect;
        var oldVisibility = AdornedElement.Visibility;
        root.Effect = new BlurEffect();
        AdornedElement.SetCurrentValue(FrameworkElement.VisibilityProperty, Visibility.Hidden);
        rtb.Render(root);
        AdornedElement.SetCurrentValue(FrameworkElement.VisibilityProperty, oldVisibility);
        root.Effect = oldEffect;

        drawingContext.DrawImage(rtb, adornerLayer.TransformToVisual(AdornedElement).TransformBounds(new Rect(adornerLayer.RenderSize)));
        drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(22, 0, 0, 0)), null, adornerLayer.TransformToVisual(AdornedElement).TransformBounds(new Rect(adornerLayer.RenderSize)));
        drawingContext.DrawRectangle(new VisualBrush(AdornedElement) { AlignmentX = AlignmentX.Left, TileMode = TileMode.None, Stretch = Stretch.None },
            null,
            AdornedElement.RenderTransform.TransformBounds(new Rect(AdornedElement.RenderSize)));
    }
}

这种模糊和模糊(并冻结,因为它实际上使用了一个屏幕截图)所有窗口,同时保持所需的控件聚焦和清晰(和移动 - 即在TextBoxes中,文本输入插入符号仍然可见并闪烁)

要使用此Decorator,您只需在XAML中设置如下:

<StackPanel>
    <local:TipFocusDecorator x:Name="LoginDecorator" 
                             TipText="Enter your username and password and click 'Login'"
                             IsOpen="{Binding ShowLoginTip}">
        <local:LoginForm />
    </local:TipFocusDecorator>
</StackPanel>

最终结果,当ShowLoginTip设置为true时:

enter image description here

已知问题

现在使用简单的MessageBox来显示提示,但您可以为提示创建自己的Window课程,根据需要设置样式,并使用ShowDialog()进行调用而不是MessageBox.Show()(并且您还可以控制Window出现的位置,如果您希望它显示在焦点控件旁边或类似的位置。)

此外,这在UserControls中无效,因为AdornerLayer.GetAdornerLayer(AdornedElement)将在UserControls中返回null。通过查找AdornerLayer(或父级的父级,递归)的父级UserControl,可以很容易地解决这个问题。有功能可以这样做。

这对于Pages也不适用,仅适用于Windows。仅仅因为我使用Window.GetWindow(this)来获取装饰器的父Window ...您可以使用其他函数来获取父级,可以使用Windows,Pages或其他任何方式。与AdornerLayer问题一样,这里有很多解决方案。

此外,我猜这可能会以某种方式制作动画(例如,使模糊和暗淡效果逐渐显现),但还没有真正研究过它......

答案 1 :(得分:0)

您可以将提示创建为窗口并使用<UIElement.Effect> <BlurEffect/> </UIelement.Effect> 进行显示。这为您提供了一个模态对话框,正如其他人所建议的那样。一定要设置它的所有者。在您展示它之前,您可以使用

Uncaught TypeError: $(...).pwstrength is not a function

设置窗口或外部容器(可能是网格)模糊效果。 radius属性设置模糊的“级别”,所以我想你最初可以将它设置为0并在显示对话框时以编程方式修改它