WPF Handedness con ventanas emergentes

Acabo de mover mi PC a Windows 8 desde Windows 7 y al ejecutar nuestra aplicación WPF noté que nuestras ventanas emergentes de WPF y / o las sugerencias de herramientas ahora están en la esquina inferior izquierda de forma predeterminada en lugar de la esquina inferior derecha. ¿Alguien ha notado esto? Sé que puede especificar su ubicación en cada información sobre herramientas en el xaml, pero tenemos muchas sugerencias de herramientas y ventanas emergentes. Quiero saber si hay una forma de especificar la ubicación predeterminada globalmente en una aplicación WPF. Google no ha producido muchos resultados sobre este tema. Tenemos una razón para mantenerlos en la misma posición predeterminada original (algunas ventanas emergentes tienen contenido relativo a su posición de inicio).

Windows 8: (abajo a la izquierda)

Tool Tip en Win8

Windows 7: (abajo a la derecha)

Consejo de herramienta en Win7

¡Mismo código! Atributo estándar “tooltip” xaml.

¿Algunas ideas?

RESUELTO Y PUBLICÉ LOS COMENTARIOS


Ok, he encontrado el problema. Tiene que ver con Tablet PC / Touchscreens. (Mano izquierda … preferencia con la mano derecha) Este otro enlace proporcionó una razón. Estoy trabajando en una solución para resolver esto ahora. ¡Publicaré los detalles pronto!

ubicación emergente de windows 8

Gracias @TravisWhidden por la solución. Acabo de implementar una versión mejorada que escucha el evento StaticPropertyChanged, lo pegaré aquí porque parece menos un “hack”.

private static readonly FieldInfo _menuDropAlignmentField; static MainWindow() { _menuDropAlignmentField = typeof(SystemParameters).GetField("_menuDropAlignment", BindingFlags.NonPublic | BindingFlags.Static); System.Diagnostics.Debug.Assert(_menuDropAlignmentField != null); EnsureStandardPopupAlignment(); SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged; } private static void SystemParameters_StaticPropertyChanged(object sender, PropertyChangedEventArgs e) { EnsureStandardPopupAlignment(); } private static void EnsureStandardPopupAlignment() { if (SystemParameters.MenuDropAlignment && _menuDropAlignmentField != null) { _menuDropAlignmentField.SetValue(null, false); } } 

Ok, para aquellos que no quieren que esto ocurra en absoluto en su aplicación (que era nuestro deseo), hemos creado un pequeño truco para WPF. Esto funciona bien para nosotros

Primero:

Este código será lo que se ejecuta lo que soluciona el problema:

 public static void SetAlignment() { var ifLeft = SystemParameters.MenuDropAlignment; if (ifLeft) { // change to false var t = typeof(SystemParameters); var field = t.GetField("_menuDropAlignment", BindingFlags.NonPublic | BindingFlags.Static); field.SetValue(null, false); ifLeft = SystemParameters.MenuDropAlignment; } } 

Sin embargo, el entorno puede desvalidar la memoria caché interna de microsofts de estos valores, por lo que debemos conectarnos a WinProc para obtener esto. No publicaré el código de WinProc, solo los mensajes que se necesitan:

Estos son los mensajes de Win32 que desvalidarán el caché interno:

 private const int WM_WININICHANGE = 0x001A; private const int WM_DEVICECHANGE = 0x219; private const int WM_DISPLAYCHANGE = 0x7E; private const int WM_THEMECHANGED = 0x031A; private const int WM_SYSCOLORCHANGE = 0x15; 

Y el snippit rápido que restablecerá tus preferencias. Debido a que estamos conectados a WinProc, querrá cambiar este valor después de que WinProc finalice con el mensaje en otros manejadores. Tenemos un retraso para volver a establecer el valor de preferencia a lo que queremos.

 if (msg == WM_WININICHANGE || msg == WM_DEVICECHANGE || msg == WM_DISPLAYCHANGE || msg == WM_THEMECHANGED || msg == WM_SYSCOLORCHANGE) { Timer timer = null; timer = new Timer((x) => { WpfHelperHacks.SetAlignment(); timer.Dispose(); },null, TimeSpan.FromMilliseconds(2), TimeSpan.FromMilliseconds(-1)); } 

Y así es completo. ¡Espero que esto ayude a alguien más!

Si no puede usar soluciones que alteran este comportamiento en todo el sistema, así es como lo hago para una sola ventana emergente:

 public enum HorizontalPlacement { Left, Right, Center }; public enum VerticalPlacement { Top, Bottom, Center }; ///  /// In WPF, PopUps pop up in different places on different machines (due to different "handedness" on touch-enabled screens. This fixes it. /// See Also: http://social.msdn.microsoft.com/Forums/vstudio/en-US/19ef3d33-01e5-45c5-a845-d64f9231001c/popup-positioningalignments?forum=wpf ///  public static class PopupPlacement { ///  /// Usage: In XAML, add the following to your tooltip: /// Placement="Custom" CustomPopupPlacementCallback="CustomPopupPlacementCallback" /// and call this method from the CustomPopupPlacementCallback. ///  public static CustomPopupPlacement[] PlacePopup(Size popupSize, Size targetSize, Point offset, VerticalPlacement verticalPlacement, HorizontalPlacement horizontalPlacement) { Point p = new Point { X = GetHorizontalOffset(popupSize, targetSize, horizontalPlacement), Y = GetVerticalOffset(popupSize, targetSize, verticalPlacement) }; return new[] { new CustomPopupPlacement(p, PopupPrimaryAxis.Horizontal) }; } private static double GetVerticalOffset(Size popupSize, Size targetSize, VerticalPlacement verticalPlacement) { switch (verticalPlacement) { case VerticalPlacement.Top: return -popupSize.Height; case VerticalPlacement.Bottom: return targetSize.Height; case VerticalPlacement.Center: return -(popupSize.Height/ 2) + targetSize.Height / 2; } throw new ArgumentOutOfRangeException("verticalPlacement"); } private static double GetHorizontalOffset(Size popupSize, Size targetSize, HorizontalPlacement horizontalPlacement) { switch (horizontalPlacement) { case HorizontalPlacement.Left: return -popupSize.Width; case HorizontalPlacement.Right: return 0; case HorizontalPlacement.Center: return -(popupSize.Width / 2) + targetSize.Width / 2; } throw new ArgumentOutOfRangeException("horizontalPlacement"); } }