是否可以在 WPF / Touch 应用程序中弹出一个忽略 MenuDropAlignment 的窗口?

发布于 2024-10-18 03:09:44 字数 1561 浏览 3 评论 0原文

作为一点背景知识 - Windows 有一个针对触摸/平板电脑的功能,它可以根据您的“用手习惯”来移动弹出窗口/菜单的位置(以防止菜单出现在您的手下)。

从本质上讲,如果您设置为“右手”(连接触摸设备后似乎默认为右手),您打开的每个弹出窗口都会被人为地踢到左侧 - 这会导致我们尝试的大量布局问题并将弹出窗口与其“从中弹出”的项目对齐:

平板电脑设置设置为左手 - Windows 无需人工修正 平板电脑设置设置为左手 - Windows 不进行人为修正

平板电脑设置设置为右手 - Windows 人为修正调整我们的定位 平板电脑设置设置为右手 - Windows 人为调整我们的位置

我们可以使用 SystemParameters.MenuDropAlignment,对于简单的弹出窗口,我们可以调整它使用弹出窗口的实际宽度 - 但对于动态弹出窗口,当我们将从右到左的支持添加到混合中时,这不起作用。

目前看来,我们更有可能必须检查每个弹出窗口,手动计算出一个值来调整踢,然后为每个弹出窗口添加类似的内容:

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Source={x:Static SystemParameters.MenuDropAlignment}}" Value="True"/>
        <Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=FlowDirection}" Value="RightToLeft"/>
    </MultiDataTrigger.Conditions>
    <MultiDataTrigger.Setters>
        <Setter Property="HorizontalOffset" Value="-50"/> <!-- Set to arbitrary value to test -->
    </MultiDataTrigger.Setters>
</MultiDataTrigger>

所以..回到问题:-) 有谁知道有办法阻止 WPF 弹出窗口查看此设置吗?

对于没有触摸设备的用户,您可以通过运行以下命令来访问 Tablet PC 设置:

C:\Windows\explorer.exe shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}

并查看它造成的混乱你自己 :-)

As a bit of background - Windows has a facility for Touch/TabletPCs whereby it shifts the position of popups/menus depending on your "handedness" (to prevent the menu appearing under your hand).

Essentially what this does is if you are set to "right handed" (which it seems to default to once you've connected a touch device), every popup you open is artificially kicked to the left - this causes massive layout issues where we try and line up a popup with the item it "popped up from" :

Tablet PC Settings set to Left handed - no artificial correction from Windows
Tablet PC Settings set to Left handed - no artificial correction from Windows

Tablet PC Settings set to Right handed - Windows artificially adjusts our positioning
Tablet PC Settings set to Right handed - Windows artificially adjusts our positioning

We can detect what this setting is set to with SystemParameters.MenuDropAlignment, and for trivial popups we can adjust this using the actual width of the popup - but for dynamic popups and when we throw right to left support into the mix, this just doesn't work.

Currently it's looking more likely that we are going to have to go through every popup, manually work out a value to adjust the kick, and add something like this to every one:

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Source={x:Static SystemParameters.MenuDropAlignment}}" Value="True"/>
        <Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=FlowDirection}" Value="RightToLeft"/>
    </MultiDataTrigger.Conditions>
    <MultiDataTrigger.Setters>
        <Setter Property="HorizontalOffset" Value="-50"/> <!-- Set to arbitrary value to test -->
    </MultiDataTrigger.Setters>
</MultiDataTrigger>

So.. back to the question :-) Does anyone know of a way to stop a WPF popup looking at this setting at all?

For those that don't have a touch device you can access the Tablet PC settings by running:

C:\Windows\explorer.exe shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}

And see the mess it makes for yourself :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

回忆凄美了谁 2024-10-25 03:09:44

我编写了一个自定义弹出窗口来解决这个问题:您可以设置 ForceAlignment 依赖属性并使用“Open”方法将其打开,也可以直接调用“OpenLeft”和“OpenRight”方法。

Public Class CustomPopup

Inherits Primitives.Popup
Private Shared moFI As Reflection.FieldInfo = GetType(SystemParameters).GetField("_menuDropAlignment", Reflection.BindingFlags.NonPublic + Reflection.BindingFlags.Static)

Public Enum enuForceAlignment
    None = 0
    Left
    Right
End Enum

Public Property ForceAlignment As enuForceAlignment
    Get
        Return GetValue(ForceAlignmentProperty)
    End Get

    Set(ByVal value As enuForceAlignment)
        SetValue(ForceAlignmentProperty, value)
    End Set
End Property
Public Shared ReadOnly ForceAlignmentProperty As DependencyProperty = _
                       DependencyProperty.Register("ForceAlignment", _
                       GetType(enuForceAlignment), GetType(CustomPopup), _
                       New FrameworkPropertyMetadata(enuForceAlignment.None))

Public Sub Open()
    Select Case ForceAlignment
        Case enuForceAlignment.Left
            OpenLeft()
        Case enuForceAlignment.Right
            OpenRight()
        Case Else
            IsOpen = True
    End Select
End Sub
Public Sub OpenRight()
    _Open(False)
End Sub
Public Sub OpenLeft()
    _Open(True)
End Sub
Private Sub _Open(paMenuDropAlignment As Boolean)
    If SystemParameters.MenuDropAlignment <> paMenuDropAlignment Then
        moFI.SetValue(Nothing, paMenuDropAlignment)
        IsOpen = True
        moFI.SetValue(Nothing, Not paMenuDropAlignment)
    Else
        IsOpen = True
    End If
End Sub
End Class

I wrote a custom popup that solve this problem: you can set the ForceAlignment dependency property and open it with the "Open" method, or you can directly call "OpenLeft" and "OpenRight" methods.

Public Class CustomPopup

Inherits Primitives.Popup
Private Shared moFI As Reflection.FieldInfo = GetType(SystemParameters).GetField("_menuDropAlignment", Reflection.BindingFlags.NonPublic + Reflection.BindingFlags.Static)

Public Enum enuForceAlignment
    None = 0
    Left
    Right
End Enum

Public Property ForceAlignment As enuForceAlignment
    Get
        Return GetValue(ForceAlignmentProperty)
    End Get

    Set(ByVal value As enuForceAlignment)
        SetValue(ForceAlignmentProperty, value)
    End Set
End Property
Public Shared ReadOnly ForceAlignmentProperty As DependencyProperty = _
                       DependencyProperty.Register("ForceAlignment", _
                       GetType(enuForceAlignment), GetType(CustomPopup), _
                       New FrameworkPropertyMetadata(enuForceAlignment.None))

Public Sub Open()
    Select Case ForceAlignment
        Case enuForceAlignment.Left
            OpenLeft()
        Case enuForceAlignment.Right
            OpenRight()
        Case Else
            IsOpen = True
    End Select
End Sub
Public Sub OpenRight()
    _Open(False)
End Sub
Public Sub OpenLeft()
    _Open(True)
End Sub
Private Sub _Open(paMenuDropAlignment As Boolean)
    If SystemParameters.MenuDropAlignment <> paMenuDropAlignment Then
        moFI.SetValue(Nothing, paMenuDropAlignment)
        IsOpen = True
        moFI.SetValue(Nothing, Not paMenuDropAlignment)
    Else
        IsOpen = True
    End If
End Sub
End Class
所谓喜欢 2024-10-25 03:09:44

将其设置为整个应用程序的常规模式:

FieldInfo fi = typeof(SystemParameters).GetField("_menuDropAlignment",
   BindingFlags.NonPublic | BindingFlags.Static);

fi.SetValue(null, false);

Set it to regular mode for your whole application:

FieldInfo fi = typeof(SystemParameters).GetField("_menuDropAlignment",
   BindingFlags.NonPublic | BindingFlags.Static);

fi.SetValue(null, false);
舟遥客 2024-10-25 03:09:44

这已经很老了,但如果你至少有 .net 4.5,我找到了另一种方法。

通过覆盖您的菜单项/弹出窗口模板,您可以使用以下触发器:

<DataTrigger Binding="{Binding Path=(SystemParameters.MenuDropAlignment)}" Value="[True/False]">
    <Setter TargetName="Popup" Property="Placement" Value="[Left/Right]"/>
</DataTrigger>

当然,设置以下内容:

  • True 或 False 为您的数据触发值,
  • Left 或 Right 作为您的设置器值。

绑定使用 Binding Path=() 语法而不是 Binding={x:static ...} 非常重要,这样您就可以使用静态类中的 StaticPropertyChanged 事件。

This is pretty old but I found another way if you have .net 4.5 at least.

By overriding your menuitem/popup templates, you can use the following trigger:

<DataTrigger Binding="{Binding Path=(SystemParameters.MenuDropAlignment)}" Value="[True/False]">
    <Setter TargetName="Popup" Property="Placement" Value="[Left/Right]"/>
</DataTrigger>

Off course, set the following :

  • True or False to your datatrigger value,
  • Left or Right as your setter value.

It's important that the binding use Binding Path=() syntax and not Binding={x:static ...} so that you can use the StaticPropertyChanged event from your static class.

蓝眸 2024-10-25 03:09:44

看来这是不可能的,因此我们求助于问题中的 MultiDataTrigger 来进行补偿。

It would appear this just isn't possible, so we are resorting to the MultiDataTrigger in the question to compensate.

无法言说的痛 2024-10-25 03:09:44

SystemParameters.MenuDropAlignment 用于单个方法:

System.Windows.Controls.Primitives.Popup.GetPointCombination()

此私有方法的逻辑取决于 的值>Popup.Placement,其类型为 PlacementMode

public enum PlacementMode
{
    Absolute,
    Relative,
    Bottom,
    Center,
    Right,
    AbsolutePoint,
    RelativePoint,
    Mouse,
    MousePoint,
    Left,
    Top,
    Custom
}

GetPointCombination() 中,它仅在 MenuDropAlignment 时检查 MenuDropAlignment >PlacementMode 为 RelativeAbsolutePointRelativePointMousePoint。如果您可以使用其他 PlacementModes 之一,那么您将不会受到 MenuDropAlignment 检查。

如果您使用 PlacementMode.Custom,那么您还需要将 Popup.CustomPopupPlacementCallback 设置为有效方法,以便提供弹出窗口的 CustomPopupPlacement[]< /代码> 坐标。

来源:反射器

SystemParameters.MenuDropAlignment is used in a single method:

System.Windows.Controls.Primitives.Popup.GetPointCombination()

This private method has logic that depends upon the value of Popup.Placement, which is of type PlacementMode:

public enum PlacementMode
{
    Absolute,
    Relative,
    Bottom,
    Center,
    Right,
    AbsolutePoint,
    RelativePoint,
    Mouse,
    MousePoint,
    Left,
    Top,
    Custom
}

In GetPointCombination(), it only checks the MenuDropAlignment when the PlacementMode is Relative, AbsolutePoint, RelativePoint, or MousePoint. If you can use one of the other PlacementModes then you won't be subject to the MenuDropAlignment check.

If you use PlacementMode.Custom, then you'll also want to set the Popup.CustomPopupPlacementCallback to a valid method in order to provide your popup's CustomPopupPlacement[] coordinates.

Source: Reflector

拥有 2024-10-25 03:09:44

除了 Julican 的 答案 之外,如果您不使用 StaticPropertyChanged 事件,

<Popup.Style>
    <Style TargetType="Popup">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Source={x:Static SystemParameters.MenuDropAlignment}}" Value="True">
                <Setter Property="Placement" Value="Right"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Source={x:Static SystemParameters.MenuDropAlignment}}" Value="False">
                <Setter Property="Placement" Value="Left"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Popup.Style>

上面的代码中的值可能会有所不同,具体取决于您想要的内容...
另外,请确保您没有将 Placement 属性放置在弹出窗口上:

<Popup
    ...
    Placement="Right"
    ...
/>

In addition to Julican's answer if you don't use the StaticPropertyChanged event

<Popup.Style>
    <Style TargetType="Popup">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Source={x:Static SystemParameters.MenuDropAlignment}}" Value="True">
                <Setter Property="Placement" Value="Right"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Source={x:Static SystemParameters.MenuDropAlignment}}" Value="False">
                <Setter Property="Placement" Value="Left"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Popup.Style>

In the code above values might differ depending on what you want...
Also, make sure that you didn't place the Placement property on the popup:

<Popup
    ...
    Placement="Right"
    ...
/>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文