IT Share you

WPF에서 컨트롤이 사용자에게 표시되는지 여부를 어떻게 확인할 수 있습니까?

shareyou 2020. 12. 4. 21:26
반응형

WPF에서 컨트롤이 사용자에게 표시되는지 여부를 어떻게 확인할 수 있습니까?


항목이 많은 매우 큰 트리를 표시하고 있습니다. 이러한 각 항목은 관련 UserControl 컨트롤을 통해 사용자에게 정보를 표시하며,이 정보는 250 밀리 초마다 업데이트되어야합니다.이 정보는 리플렉션을 사용하여 일부 값에 액세스하기 때문에 매우 비용이 많이 드는 작업이 될 수 있습니다. 첫 번째 접근 방식은 IsVisible 속성을 사용하는 것이었지만 예상대로 작동하지 않습니다.

컨트롤이 사용자에게 '표시'되는지 확인할 수있는 방법이 있습니까?

참고 : 이미 IsExpanded 속성을 사용하여 축소 된 노드 업데이트를 건너 뛰고 있지만 일부 노드에는 100 개 이상의 요소가 있으며 그리드 뷰포트 외부에있는 요소를 건너 뛰는 방법을 찾을 수 없습니다.


방금 작성한이 작은 도우미 함수를 사용하여 주어진 컨테이너에서 요소가 사용자에게 표시되는지 확인할 수 있습니다. 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귀하의 창입니다.


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);
}

포함하는 컨트롤에 다음 속성을 사용합니다.

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를 참조하십시오.


수락 된 답변 (및이 페이지의 다른 답변)은 원본 포스터가 가지고 있던 특정 문제를 해결하지만 제목에 쓰여진 질문에 대한 적절한 답변을 제공하지 않습니다. 즉, 컨트롤이 사용자에게 표시되는지 확인하는 방법 사용자 . 문제는 다른 컨트롤로 덮여있는 컨트롤이 렌더링 될 수 있지만 다른 답변이 해결하는 컨테이너의 경계 내에 있어도 표시되지 않는다는 것입니다.

컨트롤이 사용자에게 표시되는지 확인하려면 사용자 가 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
 }

참고 URL : https://stackoverflow.com/questions/1517743/in-wpf-how-can-i-determine-whether-a-control-is-visible-to-the-user

반응형