作为一点背景知识 - 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 弹出窗口查看此设置吗?
对于没有触摸设备的用户,您可以通过运行以下命令访问平板电脑设置:
C:\Windows xplorer.exe shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}
看看它给你自己带来的混乱:-)
我编写了一个自定义弹出窗口来解决这个问题:您可以设置 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
将整个应用程序设置为常规模式:
FieldInfo fi = typeof(SystemParameters).GetField("_menuDropAlignment",
BindingFlags.NonPublic | BindingFlags.Static);
fi.SetValue(null, false);
这已经很老了,但如果你至少有 .net 4.5,我找到了另一种方法。
通过覆盖菜单项/弹出窗口模板,您可以使用以下触发器:
<DataTrigger Binding="{Binding Path=(SystemParameters.MenuDropAlignment)}" Value="[True/False]">
<Setter TargetName="Popup" Property="Placement" Value="[Left/Right]"/>
</DataTrigger>
当然,设置以下内容:
绑定使用 Binding Path=() 语法而不是 Binding={x:static ...} 非常重要,这样您就可以使用静态类中的 StaticPropertyChanged 事件。
这似乎是不可能的,因此我们求助于问题中的 MultiDataTrigger 来进行补偿。
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
为 PlacementMode
、Relative
、AbsolutePoint
或 RelativePoint
时才检查 MousePoint
。如果您可以使用其他 PlacementModes
之一,那么您将不会受到 MenuDropAlignment
检查。
如果您使用
PlacementMode.Custom
,那么您还需要将 Popup.CustomPopupPlacementCallback
设置为有效方法,以便提供弹出窗口的 CustomPopupPlacement[]
坐标。
来源:反射器
除了 Julican 的 answer 如果您不使用 StaticPropertyChanged 事件
<Popup.Style>
<Style TargetType="Popup">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Static SystemParameters.MenuDropAlignment}}" Value="1">
<Setter Property="Placement" Value="Right"/>
</DataTrigger>
<DataTrigger Binding="{Binding Source={x:Static SystemParameters.MenuDropAlignment}}" Value="0">
<Setter Property="Placement" Value="Left"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
在上面的代码中,值可能会有所不同,具体取决于您想要的...... 另外,请确保您没有将 Placement 属性放置在弹出窗口上:
<Popup
...
Placement="Right"
...
/>