Принятый ответ (и другие ответы на этой странице) решают конкретную проблему, с которой столкнулся оригинальный постер, но они не дают адекватного ответа на вопрос, указанный в названии, т. Е. Как определить, является ли элемент управления виден пользователю . Проблема в том, что Элемент управления, который покрывается другими элементами управления, не виден , даже если он может быть визуализирован и находится в границах своего контейнера, для чего решают другие ответы.
Чтобы определить, является ли элемент управления видимым для пользователя, вы иногда должны иметь возможность определить, доступен ли пользователь WPF UIElement (или мышь на ПК) пользователю
Я столкнулся с этой проблемой, когда пытался проверить, может ли пользователь щелкнуть мышью по кнопке. Сценарий особого случая, который меня беспокоил, состоял в том, что кнопка может быть фактически видимой для пользователя, но покрыта некоторым прозрачным (или полупрозрачным или непрозрачным) слоем, который предотвращает щелчки мыши. В таком случае элемент управления может быть видимым для пользователя, но недоступным для пользователя, как будто он вообще невидим.
Так что мне пришлось придумать собственное решение.
РЕДАКТИРОВАТЬ - В моем исходном сообщении было другое решение, в котором использовался метод InputHitTest. Однако во многих случаях это не сработало, и мне пришлось его переделать. Это решение намного надежнее и, кажется, работает очень хорошо без каких-либо ложных негативов или позитивов.
Решение:
- Получить абсолютную позицию объекта относительно главного окна приложения
- Позвоните
VisualTreeHelper.HitTest
по всем углам (вверху слева, внизу слева, вверху справа, внизу справа)
- Мы называем объект Полностью кликабельный , если объект, полученный из
VisualTreeHelper.HitTest
, равен исходному объекту или его визуальному родителю для всех его углов, и Частично кликабелен для одного или больше углов.
Обратите внимание # 1: определение здесь полностью кликабельно или частично
Кликабельны не точны - мы просто проверяем все четыре угла
объект кликабелен. Если, например, кнопка имеет 4 нажимаемых угла, но это
центр имеет место, на которое нельзя нажать, мы все равно будем рассматривать его как
Полностью кликабелен. Для проверки всех точек в данном объекте было бы слишком
расточительно.
Обратите внимание # 2: иногда требуется установить объект IsHitTestVisible
свойство true (однако это значение по умолчанию для многих распространенных
контролирует), если мы хотим, чтобы VisualTreeHelper.HitTest
нашел его
private bool isElementClickable<T>(UIElement container, UIElement element, out bool isPartiallyClickable)
{
isPartiallyClickable = false;
Rect pos = GetAbsolutePlacement((FrameworkElement)container, (FrameworkElement)element);
bool isTopLeftClickable = GetIsPointClickable<T>(container, element, new Point(pos.TopLeft.X + 1,pos.TopLeft.Y+1));
bool isBottomLeftClickable = GetIsPointClickable<T>(container, element, new Point(pos.BottomLeft.X + 1, pos.BottomLeft.Y - 1));
bool isTopRightClickable = GetIsPointClickable<T>(container, element, new Point(pos.TopRight.X - 1, pos.TopRight.Y + 1));
bool isBottomRightClickable = GetIsPointClickable<T>(container, element, new Point(pos.BottomRight.X - 1, pos.BottomRight.Y - 1));
if (isTopLeftClickable || isBottomLeftClickable || isTopRightClickable || isBottomRightClickable)
{
isPartiallyClickable = true;
}
return isTopLeftClickable && isBottomLeftClickable && isTopRightClickable && isBottomRightClickable; // return if element is fully clickable
}
private bool GetIsPointClickable<T>(UIElement container, UIElement element, Point p)
{
DependencyObject hitTestResult = HitTest< T>(p, container);
if (null != hitTestResult)
{
return isElementChildOfElement(element, hitTestResult);
}
return false;
}
private DependencyObject HitTest<T>(Point p, UIElement container)
{
PointHitTestParameters parameter = new PointHitTestParameters(p);
DependencyObject hitTestResult = null;
HitTestResultCallback resultCallback = (result) =>
{
UIElement elemCandidateResult = result.VisualHit as UIElement;
// result can be collapsed! Even though documentation indicates otherwise
if (null != elemCandidateResult && elemCandidateResult.Visibility == Visibility.Visible)
{
hitTestResult = result.VisualHit;
return HitTestResultBehavior.Stop;
}
return HitTestResultBehavior.Continue;
};
HitTestFilterCallback filterCallBack = (potentialHitTestTarget) =>
{
if (potentialHitTestTarget is T)
{
hitTestResult = potentialHitTestTarget;
return HitTestFilterBehavior.Stop;
}
return HitTestFilterBehavior.Continue;
};
VisualTreeHelper.HitTest(container, filterCallBack, resultCallback, parameter);
return hitTestResult;
}
private bool isElementChildOfElement(DependencyObject child, DependencyObject parent)
{
if (child.GetHashCode() == parent.GetHashCode())
return true;
IEnumerable<DependencyObject> elemList = FindVisualChildren<DependencyObject>((DependencyObject)parent);
foreach (DependencyObject obj in elemList)
{
if (obj.GetHashCode() == child.GetHashCode())
return true;
}
return false;
}
private IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private Rect GetAbsolutePlacement(FrameworkElement container, FrameworkElement element, bool relativeToScreen = false)
{
var absolutePos = element.PointToScreen(new System.Windows.Point(0, 0));
if (relativeToScreen)
{
return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
}
var posMW = container.PointToScreen(new System.Windows.Point(0, 0));
absolutePos = new System.Windows.Point(absolutePos.X - posMW.X, absolutePos.Y - posMW.Y);
return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
}
Тогда все, что нужно, чтобы выяснить, является ли кнопка (например) нажатой кнопкой мыши, - это позвонить:
if (isElementClickable<Button>(Application.Current.MainWindow, myButton, out isPartiallyClickable))
{
// Whatever
}