Как потушить все «вокруг» данного элемента управления? - PullRequest
8 голосов
/ 02 мая 2011

В моем приложении мне нужно некоторое модальное поведение в определенных ситуациях, то есть пользователю должно быть разрешено взаимодействовать только с конкретным элементом пользовательского интерфейса (например, со всеми элементами управления внутри группового блока).Я не хочу использовать модальные диалоги, поэтому я пытаюсь найти способ «затухания» всего , за исключением элемента управления, который должен оставаться активным, предпочтительно путем затемнения всего остального (таким образом, визуально фокусируя элемент управлениявопрос).

Как такое поведение может быть достигнуто?Обратите внимание, что элемент, который должен стать модальным, всегда является частью пользовательского интерфейса, поэтому я не могу просто поместить его на оверлей или что-то подобное.

Я наткнулся на декораторов и украшателей, но тамдовольно мало информации об этих ...

Ответы [ 2 ]

3 голосов
/ 03 мая 2011

Вы можете применить наложение ко всему окну и установить OpacityMask этого наложения так, чтобы оно было прозрачным над элементом, который должен быть модальным.Я постараюсь опубликовать пример через несколько минут.


РЕДАКТИРОВАТЬ: ОК, это немного сложнее, чем я ожидал ... Вот несколько работающее, но уродливое решение:

    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 является корневой панелью окна.

Это решение работает, но имеет две основные проблемы:

  • Это не поддерживает изменение размера;Вероятно, это можно решить, связав ширину столбца и высоту строки наложенной сетки, используя конвертеры, но это не очень простовзаимодействовать с ними с помощью клавиатуры.Я думаю, что единственный способ предотвратить это - отключить их ...
1 голос
/ 03 мая 2011

Я бы пошел с Adorner, потому что это элемент WPF, для которого msdn указывает, который может использоваться для «Визуальной маскировки или переопределения части или всего UIElement» ( см. Здесь ).

Хорошую отправную точку можно найти в этом сообщении в блоге , где вы, вероятно, захотите исключить из отображаемой области область UIElement, которую вы хотите подчеркнуть.(В этот момент вы можете даже создать VisualBrush для всего окна и нарисовать его, если хотите получить крутой эффект, но в противном случае это должна сделать сплошная кисть с непрозрачностью 0,5).

Томас очень хорошо заметил, что, когда это «модальное» поведение включено, вы должны отключить все элементы управления, с которыми пользователь не должен взаимодействовать, потому что только их отключение гарантирует, что эти элементы управления не будут реагировать на пользовательский ввод каким-либо образом (или прихотя бы сделать их не сфокусированными, но я думаю, что это может испортить их предыдущее состояние, когда вы вернетесь из «модального» состояния).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...