Wie kann ich in WPF feststellen, ob ein Steuerelement für den Benutzer sichtbar ist?

Ich zeige einen sehr großen Baum mit vielen Gegenständen. Jedes dieser Elemente zeigt Informationen für den Benutzer über das zugehörige UserControl-Steuerelement an. Diese Informationen müssen alle 250 Millisekunden aktualisiert werden. Dies kann eine sehr teure Aufgabe sein, da ich auch Reflektionen für den Zugriff auf einige ihrer Werte verwende. Mein erster Ansatz war, die IsVisible-Eigenschaft zu verwenden, aber es funktioniert nicht wie erwartet.

Kann ich irgendwie feststellen, ob ein Steuerelement für den Benutzer “sichtbar” ist?

Hinweis: Ich verwende die IsExpanded-Eigenschaft bereits, um das Aktualisieren minimierter Knoten zu überspringen, aber einige Knoten haben mehr als 100 Elemente und können keine Möglichkeit finden, diejenigen zu überspringen, die sich außerhalb des Raster-Ansichtsfensters befinden.

Solutions Collecting From Web of "Wie kann ich in WPF feststellen, ob ein Steuerelement für den Benutzer sichtbar ist?"

Sie können diese kleine Hilfsfunktion, die ich gerade geschrieben habe, verwenden, um zu prüfen, ob ein Element für den Benutzer in einem bestimmten Container sichtbar ist. Die function gibt true zurück true wenn das Element teilweise sichtbar ist. Wenn Sie überprüfen möchten, ob es vollständig sichtbar ist, ersetzen Sie die letzte Zeile durch 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); } 

In Ihrem Fall ist element Ihr Benutzersteuerelement und container Ihr Fenster.

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

Verwenden Sie diese Eigenschaften für das enthaltene Steuerelement:

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

und dann hören Sie sich die INotifyPropertyChanged.PropertyChanged-Abonnenten dieser Datenelemente an

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

Weitere Informationen finden Sie unter: http://joew.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&_c=BlogPart&partqs=cat%3DWPF

Die angenommene Antwort (und die anderen Antworten auf dieser Seite) lösen das spezifische Problem, das das ursprüngliche Plakat hatte, aber sie geben keine angemessene Antwort auf die Frage, die im Titel geschrieben wird, dh, wie man feststellt, ob ein Steuerelement für die Benutzer . Das Problem ist, dass ein Steuerelement, das von anderen Steuerelementen abgedeckt wird, nicht sichtbar ist , obwohl es gerendert werden kann und es sich innerhalb der Grenzen seines Containers befindet, wofür die anderen Antworten zuständig sind.

Um zu bestimmen, ob ein Steuerelement für den Benutzer sichtbar ist, müssen Sie manchmal in der Lage sein zu bestimmen, ob ein WPF-UIElement vom Benutzer anklickbar (oder Maus erreichbar auf einem PC) sein kann

Ich stieß auf dieses Problem, als ich versuchte zu überprüfen, ob eine Schaltfläche vom Benutzer per Mausklick angeklickt werden kann. Ein spezielles Fall-Szenario, das mich störte, war, dass eine Schaltfläche für den Benutzer tatsächlich sichtbar sein kann, aber mit einer transparenten Ebene (oder halbtransparent oder überhaupt nicht transparent) bedeckt ist, die Mausklicks verhindert. In einem solchen Fall könnte ein Steuerelement für den Benutzer sichtbar sein, aber für den Benutzer nicht zugänglich, was so aussieht, als wäre es überhaupt nicht sichtbar.

Also musste ich meine eigene Lösung finden.

Bearbeiten – Mein ursprünglicher Beitrag hatte eine andere Lösung, die InputHitTest-Methode verwendet. Es funktionierte jedoch in vielen Fällen nicht und ich musste es neu gestalten. Diese Lösung ist viel robuster und scheint sehr gut zu funktionieren, ohne falsche negative oder positive Ergebnisse.

Lösung:

  1. Ermitteln Sie die absolute Position des Objekts relativ zum Hauptfenster der Anwendung
  2. Rufen Sie VisualTreeHelper.HitTest an allen Ecken auf (oben links, unten links, oben rechts, unten rechts)
  3. Wir rufen ein Objekt Fully Clickable auf, wenn das von VisualTreeHelper.HitTest erhaltene Objekt dem ursprünglichen Objekt oder einem visuellen übergeordneten Objekt für alle seine Ecken entspricht und Teilweise anklickbar für eine oder mehrere Ecken ist.

Bitte beachten Sie # 1: Die Definition hier von Fully Clickable oder Partially Clickable ist nicht exakt – wir überprüfen nur alle vier Ecken eines Objekts, auf das geklickt werden kann. Wenn zum Beispiel eine Schaltfläche 4 anklickbare Ecken hat, aber ihre Mitte eine Stelle hat, die nicht anklickbar ist, betrachten wir sie weiterhin als vollständig anklickbar. Alle Punkte in einem gegebenen Objekt zu überprüfen wäre zu verschwenderisch.

Bitte beachten Sie # 2: Es ist manchmal erforderlich, eine IsHitTestVisible Eigenschaft des Objekts auf ” true” zu setzen (dies ist jedoch der Standardwert für viele allgemeine Steuerelemente), wenn wir möchten, dass VisualTreeHelper.HitTest es findet

  private bool isElementClickable(UIElement container, UIElement element, out bool isPartiallyClickable) { isPartiallyClickable = false; Rect pos = GetAbsolutePlacement((FrameworkElement)container, (FrameworkElement)element); bool isTopLeftClickable = GetIsPointClickable(container, element, new Point(pos.TopLeft.X + 1,pos.TopLeft.Y+1)); bool isBottomLeftClickable = GetIsPointClickable(container, element, new Point(pos.BottomLeft.X + 1, pos.BottomLeft.Y - 1)); bool isTopRightClickable = GetIsPointClickable(container, element, new Point(pos.TopRight.X - 1, pos.TopRight.Y + 1)); bool isBottomRightClickable = GetIsPointClickable(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(UIElement container, UIElement element, Point p) { DependencyObject hitTestResult = HitTest< T>(p, container); if (null != hitTestResult) { return isElementChildOfElement(element, hitTestResult); } return false; } private DependencyObject HitTest(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 elemList = FindVisualChildren((DependencyObject)parent); foreach (DependencyObject obj in elemList) { if (obj.GetHashCode() == child.GetHashCode()) return true; } return false; } private IEnumerable FindVisualChildren(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(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); } 

Dann genügt es, um zu erfahren, ob eine Schaltfläche anklickbar ist:

  if (isElementClickable