Создание элемента управления, охватывающего всю область, кроме определенной области

Я хочу создать элемент управления, охватывающий всю область, кроме определенной области. Эта конкретная область будет иметь форму эллипса / прямоугольника. Что-то вроде концепции ниже

Создание элемента управления, охватывающего всю область, кроме определенной области

Моя идея - использовать пути. Ниже мой код пути.

<Path Name = "ShowcasePath" StrokeThickness = "1" IsHitTestVisible = "True">
    <Path.Data>
        <GeometryGroup>
            <EllipseGeometry RadiusX = "100" RadiusY = "50"/>
            <RectangleGeometry/>
        </GeometryGroup>
    </Path.Data>
</Path>

CentreEllipseGeometry и RectRectangleGeometry будут установлены в коде позади.

У этого метода есть две проблемы

  1. Цвет заливки полупрозрачный даже без альфа-значения. Окончательный цвет будет прозрачно-белым. Так что это не настоящая проблема.
  2. Элемент внутри эллипса должен быть полностью функциональным, а элементы за пределами эллипса - нефункциональными.

Создание элемента управления, охватывающего всю область, кроме определенной области

Есть идеи по решению вышеуказанных проблем?

или какие-нибудь другие идеи для этого элемента управления?

Этот гигантский красный ящик действительно режет глаза!

Ron Beyer 27.03.2018 20:33

@RonBeyer Окончательный цвет будет прозрачным белым.

Vijay Nirmal 27.03.2018 20:40

Я понимаю, что это всего лишь макет того, что вы хотите охватить, на него просто трудно смотреть (по крайней мере, на моем ЖК-дисплее). Может быть, лучшим вариантом было бы привязать Visibility всего остального, чтобы все, кроме того, что вы хотите показать, было скрыто?

Ron Beyer 27.03.2018 20:44

@RonBeyer Нет, все элементы управления должны быть видны через полупрозрачный белый цвет. Поэтому я не могу настроить Visibility на это.

Vijay Nirmal 27.03.2018 20:52

Свяжите все элементы управления (кроме того, который вы хотите сохранить активным) свойство IsEnabled, а затем при необходимости установите для него значение False. Удачного кодирования :)

Christopher H. 27.03.2018 20:55

@zackraiyan Я хочу создать CustomControl, который будет стоять поверх всех остальных элементов управления. Так что это невозможно. Кроме того, установка IsEnabled изменит внешний вид элемента управления.

Vijay Nirmal 27.03.2018 21:04

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

Christopher H. 27.03.2018 21:06

@zackraiyan Я думаю, вы не поняли сути. Этот элемент управления может что угодно (например, Grid, GridView, Button, RadioButton). Мой настраиваемый элемент управления должен перекрывать все элементы управления, кроме выделенного элемента управления. Все остальные элементы управления должны быть неактивными.

Vijay Nirmal 27.03.2018 21:14

Я не знаю, возможно ли это с помощью customControl или нет (возможно, это так), но единственный способ, который приходит на ум, - это форма с обратной формой, которая выходит только из обведенной области. :(

Christopher H. 27.03.2018 21:16

В этом сценарии вы столкнетесь с парой ошибок. Во-первых, если родительская панель, на которой размещено ваше наложение, не останется фиксированного размера, наличие настраиваемого пути только исказит вашу открытую область, поэтому вывод будет непредсказуемым, если вы вручную не создадите данные пути на лету. Клип здесь не вариант. Кроме того, есть предостережение о том, что базовые элементы UIElements будут по-прежнему доступны для вкладок / действий, даже если вы не можете щелкнуть их, если вы также не справитесь с этим. Однако вы можете проявить творческий подход к своему порядку DOM и дать ИЛЛЮЗИЮ этого эффекта без какого-либо беспорядка, если вам нужен пример.

Chris W. 27.03.2018 21:34

@ChrisW. Я буду менять данные пути на лету. Я не понял второй проблемы. Также, возможно, мы можем создать элемент управления в визуальном слое, но я не знаком с ним.

Vijay Nirmal 27.03.2018 21:37

Вы можете создать слой Adorner и добавить к нему элемент управления «обложкой».

Ron Beyer 27.03.2018 21:43

Если вам уже удобно создавать данные пути на лету, чтобы получить область вырезания, это первый шаг. Однако ваш оверлей, хотя он может предотвращать прямые события щелчка / наведения ... он по-прежнему будет разрешать взаимодействие пользователя с клавиатурой с элементами за наложением по умолчанию, такими как Tab / Enter. Таким образом, вам придется либо отключить каждый элемент, как указано в @zackraiyan, либо создать макет таким образом, чтобы вы могли IsHitTestVisible=False целые разделы макета, при этом оставив свой единственный UIElement доступным для требуемых событий отдельно.

Chris W. 27.03.2018 21:45

@ ChrisW., Спасибо, что указали на свойство IsHittestVisible .. Полностью пропустил

Christopher H. 27.03.2018 21:47

@ChrisW. Я уже пробовал IsHitTestVisible со своим путем, но это не сработало для пути. Это имеет смысл, потому что пути - это просто линии, а не надежный контроль. Итак, новый вопрос: «Как создавать нестандартные формы без контуров?»

Vijay Nirmal 27.03.2018 21:57

Нет, вы бы применили его к родительскому контейнеру всех UIElements, и все дочерние UIElements этого контейнера автоматически игнорировали бы все события попадания. Оверлей не заботится о том, что находится под ним в порядке DOM, но родительская панель будет управлять своими дочерними элементами.

Chris W. 27.03.2018 22:01

@zackraiyan о да, однажды я узнал об этом на собственном горьком опыте, когда наличие тонны элементов, ищущих изменение свойства, вызывало большую проблему с производительностью до тех пор, пока один bool на родительском hittestvisibility не имел огромного значения ... Теперь, если вы спросите меня, где мой ключи от машины могут быть, я, вероятно, не был бы таким полезным, потому что половину времени я понятия не имею, лол

Chris W. 27.03.2018 22:02

Почему бы не пойти со смесью?

Christopher H. 27.03.2018 22:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
18
60
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

  1. измените свойство ZIndexProperty элемента так, чтобы оно было впереди. YourElement.SetValue(Canvas.ZIndexProperty, index);
  2. Удалите элемент и снова добавьте его в родительскую сетку (таким образом, это будет последний добавляемый элемент).

    parentGrid.Children.Remove(YourElement); parentGrid.Children.Add(YourElement); parentGrid.UpdateLayout();

После того, как пользователь успешно взаимодействует с элементом управления, вы можете вернуть его в прежнее состояние. Надеюсь, это поможет ..

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

Vijay Nirmal 28.03.2018 15:11
Ответ принят как подходящий

Я думаю, вы уже достаточно близко подошли к своему примеру, по крайней мере, я не могу найти в нем ничего плохого. Если вы попробуете следующий код, вы увидите, что он делает именно то, что вы описали:

<Grid>
    <Button Content = "Clickable" HorizontalAlignment = "Left" 
      Margin = "113,90,0,0" VerticalAlignment = "Top" Width = "75"/>
    <Button Content = "Non-Clickable" HorizontalAlignment = "Left" 
      Margin = "272,90,0,0" VerticalAlignment = "Top" Width = "75"/>
    <Path Name = "ShowcasePath" StrokeThickness = "1" Fill = "#88ff0000" IsHitTestVisible = "True">
        <Path.Data>
            <GeometryGroup FillRule = "EvenOdd">
                <EllipseGeometry Center = "150,100" RadiusX = "100" RadiusY = "50"/>
                <RectangleGeometry Rect = "0,0,500,500"/>
            </GeometryGroup>
        </Path.Data>
    </Path>
</Grid>

Однако имейте в виду, что пользователи все равно могут изменить фокусируемый элемент с помощью Tab и активировать элементы с помощью Space или Return, даже если вы установите IsHitTestVisible на false.

Функциональность "сфокусировать эллипс на элементе" может быть легко реализована следующим образом:

public void FocusOnElement(FrameworkElement element)
{
    Point center = new Point(element.ActualWidth / 2, element.ActualHeight / 2);
    Point newPosition = element.TranslatePoint(center, ShowcasePath);
    ellipseGeometry.Center = newPosition;
}

О включении / отключении всех других функций лучше всего позаботиться в вашей модели просмотра, или, если у вас ее нет, примерно так:

private void DiableAllButThisAndItsParents(FrameworkElement thisElement)
{
    List<FrameworkElement> hierarchy = FindParents(thisElement).ToList();

    foreach (FrameworkElement element in hierarchy)
    {
        element.IsEnabled = true;

        if (ReferenceEquals(element, thisElement)) continue;

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
        {
            var child = VisualTreeHelper.GetChild(element, i);
            if (!(child is FrameworkElement childElement)) continue;

            childElement.IsEnabled = hierarchy.Contains(childElement);
        }
    }
}

private IEnumerable<FrameworkElement> FindParents(FrameworkElement element)
{
    DependencyObject current = element;

    while (current != null)
    {
        if (current is FrameworkElement)
            yield return (FrameworkElement) current;
        current = VisualTreeHelper.GetParent(current);
    }
}

Сложите все это вместе, и это должно выглядеть примерно так:

Я не хочу связываться с IsEnabled, потому что это изменит внешний вид

Vijay Nirmal 28.03.2018 15:16

@VijayNirmal Ну, конечно, вы можете использовать любую комбинацию техник, упомянутых выше.

Manfred Radlwimmer 28.03.2018 15:18
IsHitTestVisible работает исправно. Вчера не сработало. Но другая проблема с моим подходом заключается в том, что каждый элемент управления внутри эллипса неразрешим. Предположим, у меня есть кнопка 1 и кнопка 2 рядом друг с другом, тогда, даже если кнопка 1 выделена, мы все равно можем нажать кнопку 2.
Vijay Nirmal 28.03.2018 15:28

Другие вопросы по теме