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 메서드를 사용하는 다른 솔루션이 있습니다. 그러나 그것은 많은 경우에 작동하지 않았고 나는 그것을 재 설계해야했다. 이 솔루션은 훨씬 더 강력하며 거짓 부정 또는 긍정없이 매우 잘 작동하는 것 같습니다.
해결책:
- 응용 프로그램 기본 창에 상대적인 개체 절대 위치 얻기
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
}
'IT Share you' 카테고리의 다른 글
파이썬에서 키보드 폴링 (키 누르기 감지) (0) | 2020.12.04 |
---|---|
svn : 로컬 커밋 (0) | 2020.12.04 |
Control.Applicative를 사용하여 더 깨끗한 Haskell을 작성하는 방법은 무엇입니까? (0) | 2020.12.04 |
.svn / text-base / file.svn-base를 열 수 없습니까? (0) | 2020.12.04 |
jslint 오류 '루프 내에서 함수를 만들지 마십시오'를 수정하는 방법? (0) | 2020.12.04 |