18

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

Windows 8:(左下)

Win8 工具提示

Windows 7:(右下)

Win7上的工具提示

相同的代码!标准“工具提示”xaml 属性。

有任何想法吗?

解决了,我发表了评论


好的,我找到了问题所在。它与平板电脑/触摸屏有关。(左撇子.. 右撇子偏好)这个其他链接提供了一个原因。我现在正在研究解决此问题的解决方案。我会尽快发布详细信息!

windows 8弹出位置

4

3 回答 3

26

感谢@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);
    }
}
于 2014-02-27T10:39:26.540 回答
12

好的,对于那些根本不希望在他们的应用程序中发生这种情况的人(这是我们的愿望),我们为 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;
    }
}

然而,环境可以取消验证这些值的微软内部缓存,所以我们必须挂钩到 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));
}

就这样它完成了。我希望这对其他人有帮助!

于 2013-08-08T00:47:57.927 回答
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");
    }
}
于 2015-03-05T14:33:53.453 回答