В WPF, при каких обстоятельствах Visual.PointFromScreen выдает InvalidOperationException?
Предположим, что я хотел сделать это, поэтому я могу найти текущую позицию мыши относительно Visual
, не требуя доступа к определенному событию мыши:
public static Point GetMousePosition(this Visual relativeTo)
{
return relativeTo.PointFromScreen(GetMousePositionOnScreen());
}
Иногда (обычно, когда я только что переключался между двумя элементами управления вкладками) PointFromScreen
выводит InvalidOperationException
с сообщением Этот Visual не подключен к PresentationSource.
При просмотре свойств, доступных на Visual
, я не вижу никакого отношения к PresentationSource
.
Учитывая Visual
, как я могу определить, будет ли это исключение, когда я вызываю PointFromScreen
на нем?
Ответы
Ответ 1
Есть статический метод PresentationSource.FromVisual
, который:
Возвращает источник, в котором представлен предоставленный Visual.
Я знаю, что это не решает основной проблемы, но вы можете проверить, что Visual подключен к PresentationSource перед вызовом PointFromScreen
. Это предотвратило бы исключение, но вам нужно будет сделать еще несколько исследований относительно того, почему он не был связан в первую очередь.
Ответ 2
У меня была аналогичная проблема с визуализацией, сделанной на заказ.
Решение заключалось в том, чтобы отложить проблемную задачу через Dispatcher (отложенное выполнение с приоритетом фона в этом случае)...
public void MyProblematicDisplayMethod(Symbol TargetSymbol)
{
this.HostingScrollViewer.BringIntoView(TargetSymbol.HeadingContentArea);
...
// This post-call is needed due to WPF tricky rendering precedence (or whatever it is!).
this.HostingScrollViewer.PostCall(
(scrollviewer) =>
{
// in this case the "scrollviewer" lambda parameter is not needed
var Location = TargetSymbol.Graphic.PointToScreen(new Point(TargetSymbol.HeadingContentArea.Left, TargetSymbol.HeadingContentArea.Top));
ShowOnTop(this.EditBox, Location);
this.EditBox.SelectAll();
});
...
}
/// <summary>
/// Calls, for this Source object thread-dispatcher, the supplied operation with background priority (plus passing the source to the operation).
/// </summary>
public static void PostCall<TSource>(this TSource Source, Action<TSource> Operation) where TSource : DispatcherObject
{
Source.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(delegate(Object state)
{ Operation(Source); return null; }),
null);
}
Я использовал этот PostCall в других ситуациях рендеринга, связанных с ScrollViewer.
Ответ 3
Я обнаружил, что вы можете протестировать IsVisible
перед вызовом PointFromScreen
для защиты от InvalidOperationException
.
Ответ 4
Поздно к мячу, но эти ответы помогли мне. Я просто хотел указать, что источники PresentaionSource не связаны с визуальными элементами, пока они не будут полностью загружены. Поэтому, если в вашем конструкторе вы настраиваете события, которые могут быть запущены до визуального элемента, который вы пытаетесь вызвать PointFromScreen, он готов к отображению на экране, тогда вы получите эту ошибку. Хотя, как упоминалось ранее, вы можете обернуть свой метод примерно так:
public static Point GetMousePosition(this Visual relativeTo)
{
if(PresentationSource.FromVisual(relativeTo) != null)
return relativeTo.PointFromScreen(GetMousePositionOnScreen());
else
return new Point();
}
вы также можете подумать о том, чтобы не вызывать свой метод, пока не убедитесь, что визуал визуализировался на экране хотя бы один раз.
Ответ 5
Исключение может возникать из-за того, что визуал отбрасывается, но все еще находится в памяти из-за утечки памяти.
У меня была аналогичная проблема. Я нашел исключение, происходящее в визуальном виде, которое должно было быть собрано в мусор. Фиксирование утечек памяти в визуальном разрешении проблемы.