WPF弹出窗口的左右手设置

19

我刚把我的PC从Windows 7升级到了Windows 8,在运行我们的WPF应用程序时,我发现我们的WPF弹出窗口和/或工具提示现在默认位于左下角,而不是正常的右下角。有人注意到这个问题吗?我知道你可以在每个工具提示的XAML中指定它们的位置,但是我们有很多工具提示和弹出窗口。我想知道是否有一种方法可以在WPF应用程序中全局指定默认位置。谷歌没有在这个主题上产生太多结果。我们有一个理由保持它们在同一原始默认位置(一些弹出窗口具有与它们的启动位置相关的内容)。

Windows 8:(左下角)

Win8上的工具提示

Windows 7:(右下角)

Win7上的工具提示

相同的代码!标准的 "tooltip" XAML 属性。

有任何想法吗?

已解决并发布了评论


好的,我找到了问题。这与平板电脑/触摸屏有关(左撇子..右撇子偏好)。这个链接提供了一个原因。我正在寻找解决方法。很快就会发布详细信息!

windows 8 popup location


可能与4.0与4.5的.NET相关--MSDN没有说放置默认值是否已更改...尝试弄清楚这个问题。 - TravisWhidden
测试了另一台Win8机器,看起来没问题。是系统配置问题吗? - TravisWhidden
可能是windows 8弹出位置的重复问题。 - chue x
3个回答

28

感谢@TravisWhidden提供的解决方案。我刚刚实现了一个改进版本,它监听StaticPropertyChanged事件,我将把它粘贴在这里,因为它似乎不太像“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);
    }
}

1
附注:这需要 .NET 4.5。 - Anders
谢谢您。非常令人烦恼的问题。 - CodeOtaku
财产在什么情况下可以发生变动? - Hugh W

13

好的,对于那些不希望在他们的应用程序中出现这种情况的人(这也是我们的愿望),我们为WPF创建了一个不错的小技巧。这对我们来说效果很好。

首先:

这段代码将运行以解决该问题:

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

然而,环境可能会使 Microsoft 的这些值的内部缓存失效,因此我们必须钩取 WinProc 来获取它们。我不会发布 WinProc 代码,只会提供所需的消息:

以下是将使内部缓存失效的 Win32 消息:

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;

以下是能够将您的首选项还原的快速代码片段。由于我们被钩入到WinProc中,您应该在其他处理程序完成消息后更改此值。我们设置了一个延迟,以便将首选项值重新设置为我们想要的值。

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

就这样,它完成了。希望这能帮助其他人!


1
如果您无法使用系统范围内更改此行为的解决方案,以下是我如何为单个弹出窗口执行此操作的方法:
public enum HorizontalPlacement { Left, Right, Center };

public enum VerticalPlacement { Top, Bottom, Center };

/// <summary>
/// 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
/// </summary>
public static class PopupPlacement
{
    /// <summary>
    /// Usage: In XAML, add the following to your tooltip: 
    ///     Placement="Custom" CustomPopupPlacementCallback="CustomPopupPlacementCallback" 
    /// and call this method from the CustomPopupPlacementCallback.
    /// </summary>
    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");
    }
}

在某些情况下,这并不起作用。例如,如果您希望弹出窗口相对于鼠标指针打开,由于从自定义例程返回的点始终相对于PlacementTarget坐标,我找不到如何将鼠标指针作为PlacementTarget。 - Emanuele Benedetti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接