WPF Handedness with Popups

问题描述 投票:17回答:3

我刚刚将我的PC从Windows 7移动到Windows 8,在运行我们的WPF应用程序时,我注意到我们的WPF弹出窗口和或工具提示现在默认在左下角,而不是正常的右下角。 有人注意到这一点吗? 我知道你可以在xaml中指定每个工具提示的位置,但我们有很多工具提示和弹出窗口。 我想知道是否有一种方法可以在WPF应用程序中全局指定默认位置。Google在这个问题上没有得到很多结果。我们有理由让它们保持在原来的默认位置上(有些弹出窗口的内容与它们的启动位置相对)。

Windows 8。(左下角)

Tool Tip on Win8

Windows 7: (右下角)

Tool Tip on Win7

同样的代码! 标准的 "tooltip "xaml属性。

有什么想法吗?

解决了,我发了评论。


好了,我已经找到了问题所在。它与平板电脑Touchscreens有关。(左手......右手的偏好)这个其他的链接提供了一个原因。我现在正在研究解决这个问题的办法。我很快就会把细节发布出来!

窗口8弹出位置

.net wpf xaml windows-8 tooltip
3个回答
22
投票

谢谢@TravisWhidden的解决方案。刚刚实现了一个改进的版本,它监听到了 StaticPropertyChanged 事件,我把它贴在这里,因为它看起来不是一个 "黑客"。

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

11
投票

好吧,对于那些不希望在他们的应用程序中发生这种情况的人(这是我们的愿望),我们已经为WPF创建了一个不错的小黑客。 这对我们来说很有效。

首先,这段代码将是运行修复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;
    }
}

然而,环境可以去验证microsofts内部缓存的这些值,所以我们必须挂钩到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上的,所以你要在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");
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.