如何在给定控件"around"淡出所有内容?



在我的应用程序中,我需要一些模态行为在某些情况下,即用户应该只允许与UI的特定元素交互(例如组框内的所有控件)。我不想使用模态对话框,所以我试着找到一种方法来"淡出"一切,除了应该保持活动的控件,最好是通过使其他一切变暗(从而给有问题的控件视觉焦点)。

这样的行为是如何实现的?请注意,应该成为模态的元素始终是UI的一部分,所以我不能只是把它放在覆盖或类似的东西上。

我偶然发现了Decorators和Adorners,但是关于这些的信息很少…

你可以应用一个覆盖到整个窗口,并设置该覆盖的OpacityMask,使其在必须是modal的元素之上是透明的。我将试着在几分钟内发布一个例子。


编辑:好吧,这比我想象的要难一点…下面是一个可行但不太美观的解决方案:

    private Grid _modalOverlay;
    private void btnShowOverlay_Click(object sender, RoutedEventArgs e)
    {
        if (_modalOverlay != null)
            root.Children.Remove(_modalOverlay);
        _modalOverlay = MakeModalOverlay(groupBox1, root, 0.5);
        root.Children.Add(_modalOverlay);
    }
    private static Grid MakeModalOverlay(FrameworkElement element, FrameworkElement root, double opacity)
    {
        var offset = GetRelativeOffset(element, root);
        Grid g = new Grid();
        var c0 = new ColumnDefinition();
        c0.Width = new GridLength(offset.X);
        var c1 = new ColumnDefinition();
        c1.Width = new GridLength(element.ActualWidth);
        var c2 = new ColumnDefinition();
        c2.Width = new GridLength(root.ActualWidth - element.ActualWidth - offset.X);
        var r0 = new RowDefinition();
        r0.Height = new GridLength(offset.Y);
        var r1 = new RowDefinition();
        r1.Height = new GridLength(element.ActualHeight);
        var r2 = new RowDefinition();
        r2.Height = new GridLength(root.ActualHeight - element.ActualHeight - offset.Y);
        g.ColumnDefinitions.Add(c0);
        g.ColumnDefinitions.Add(c1);
        g.ColumnDefinitions.Add(c2);
        g.RowDefinitions.Add(r0);
        g.RowDefinitions.Add(r1);
        g.RowDefinitions.Add(r2);
        Brush b = new SolidColorBrush(Colors.Black) { Opacity = opacity };
        for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++)
        {
            if (i == 1 && j == 1)
                continue;
            Rectangle r = new Rectangle();
            r.Fill = b;
            Grid.SetColumn(r, i);
            Grid.SetRow(r, j);
            g.Children.Add(r);
        }
        Panel.SetZIndex(g, int.MaxValue);
        return g;
    }
    private static Vector GetRelativeOffset(Visual visual, Visual ancestor)
    {
        Visual tmp = visual;
        Vector offset = default(Vector);
        while (tmp != ancestor)
        {
            offset += VisualTreeHelper.GetOffset(tmp);
            tmp = (Visual) VisualTreeHelper.GetParent(tmp);
            if (tmp == null)
                throw new ArgumentException("ancestor is not an visual ancestor of visual");
        }
        return offset;
    }
    private void btnHideOverlay_Click(object sender, RoutedEventArgs e)
    {
        if (_modalOverlay != null)
            root.Children.Remove(_modalOverlay);
    }
在上面的代码中,root是窗口的根面板。

这个解决方案是可行的,但是它有两个主要问题:

  • 不支持调整大小;这可能可以通过绑定列宽和行高的覆盖网格,使用转换器来解决,但它不是很直接
  • 它阻止你点击其他控件,但你仍然可以使用键盘与他们进行交互。我认为防止这种情况发生的唯一方法就是禁用它们…

我会选择Adorner,因为它是msdn指定的WPF元素,可以用来"在视觉上屏蔽或覆盖部分或全部的UIElement"(见这里)。

可以在这篇博客文章中找到一个很好的起点,您可能希望从渲染区域中排除您想要强调的UIElement区域。(在这一点上,你甚至可以创建整个窗口的VisualBrush,并用它来绘制装饰器,如果你想获得一个很酷的效果,但否则,一个0.5不透明度的实体刷应该可以做到)。

Thomas很好地指出,当这种"模态"行为开启时,你应该禁用所有用户不应该与之交互的控件,因为只有禁用它们才能保证这些控件不会以任何方式响应用户输入(或者至少使它们无法聚焦,但我认为这可能会扰乱它们之前的状态,当你从"模态"状态返回时)。

相关内容

  • 没有找到相关文章

最新更新