在WPF中,我怎么能确定控件是否对用户可见?(In WPF, how can I determine

2019-07-20 20:16发布

我显示了非常大的树很多它的项目。 每个项目的显示信息,用户通过其关联的用户控件的控件,而这个信息具有每250毫秒,它可以是一个非常昂贵的任务,因为我还使用反射来访问他们的一些价值观的更新。 我的第一个方法是使用IsVisible属性,但如我所料不工作。

有没有什么办法,我可以判断一个控件是否是“看得见”的用户?

注:我已经使用了IsExpanded财产跳过更新倒塌节点,但有些节点有100+元素,不能找到一个方法来跳过那些网格视区之外。

Answer 1:

你可以使用这个小助手功能,我只是写,将检查如果一个元素对于用户是可见的,在给定的容器。 该函数返回true ,如果该元素是若隐若现。 如果你要检查它是否是完全可见,通过替换最后一行rect.Contains(bounds)

private bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
    if (!element.IsVisible)
        return false;

    Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
    return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}

在你的情况下, element将是您的用户控制和container的窗口。



Answer 2:

public static bool IsUserVisible(this UIElement element)
{
    if (!element.IsVisible)
        return false;
    var container = VisualTreeHelper.GetParent(element) as FrameworkElement;
    if (container == null) throw new ArgumentNullException("container");

    Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.RenderSize.Width, element.RenderSize.Height));
    Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
    return rect.IntersectsWith(bounds);
}


Answer 3:

使用这些属性包含控制:

VirtualizingStackPanel.IsVirtualizing="True" 
VirtualizingStackPanel.VirtualizationMode="Recycling"

然后挂钩听你的数据项的INotifyPropertyChanged.PropertyChanged用户喜欢这个

    public event PropertyChangedEventHandler PropertyChanged
    {
        add
        {
            Console.WriteLine(
               "WPF is listening my property changes so I must be visible");
        }
        remove
        {
            Console.WriteLine("WPF unsubscribed so I must be out of sight");
        }
    }

欲了解更多详细信息,请参阅: http://joew.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&_c=BlogPart&partqs=cat%3DWPF



Answer 4:

接受答案(而此页面上的其他答案),解决了原来的海报有具体问题,但他们不给足够的答案写在标题中的问题,即如何确定是否控制是可见用户 。 问题是由其他控件覆盖的控制是不可见的 ,即使它可以被渲染,这是其容器的边界是什么其他答案求解之内。

为了确定AA控件是否对用户可见有时必须能够确定一个WPF的UIElement是否是可点击(或鼠标到达的PC上)由用户

我遇到过,当我试图检查一个按钮可以鼠标点击的用户这个问题。 其窃听我一个特殊的情况是,一个按钮可以是用户实际可见,但覆盖有防止鼠标点击一些透明(或半透明或不透明的话)层。 在这种情况下,控制可能是对用户可见的,但到这是一种喜欢它是不可见的,在所有的用户无法访问。

于是,我只好拿出自己的解决方案。

编辑 -我原来的职位有这样的使用方法InputHitTest不同的解决方案。 然而,它并没有在很多情况下工作,我不得不重新设计。 该解决方案更强大,似乎是工作得很好,没有任何假阴性或阳性。

解:

  1. 获得绝对相对于应用程序的主窗口对象位置
  2. 呼叫VisualTreeHelper.HitTest其各个角落(左上,左下,右上,右下)
  3. 我们把一个对象完全可点击 ,如果从所获得的对象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
 }


文章来源: In WPF, how can I determine whether a control is visible to the user?