是否可以让弹出窗口忽略 WPF / Touch 应用程序中的 MenuDropAlignment?
Posted
技术标签:
【中文标题】是否可以让弹出窗口忽略 WPF / Touch 应用程序中的 MenuDropAlignment?【英文标题】:Is it possible to get a popup to ignore MenuDropAlignment in a WPF / Touch app? 【发布时间】:2011-06-28 23:19:08 【问题描述】:作为背景知识 - 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\explorer.exe shell:::80F3F1D5-FECA-45F3-BC32-752C152E456E
看看它给你自己造成的混乱:-)
【问题讨论】:
【参考方案1】:我写了一个自定义弹窗来解决这个问题:可以设置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); 这在系统范围内无法始终如一地工作 - 它似乎会在短时间内产生效果,但会重置。它在示例的上下文中工作正常 - 在打开之前设置 - 但不幸的是你不能只设置一次。 跟进我之前的评论 - 您可以通过首先调用 SystemParameters.MenuDropAlignment 属性来“坚持”设置 - 例如如果你在反射设置代码前有“if(SystemParameters.MenuDropAlignment)”,你设置的值会出现卡住,否则会被重置。【参考方案2】:为您的整个应用程序将其设置为常规模式:
FieldInfo fi = typeof(SystemParameters).GetField("_menuDropAlignment",
BindingFlags.NonPublic | BindingFlags.Static);
fi.SetValue(null, false);
【讨论】:
【参考方案3】: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()
中,它仅在PlacementMode
为Relative
、AbsolutePoint
、RelativePoint
或MousePoint
时检查MenuDropAlignment
。如果您可以使用其他PlacementModes
之一,那么您将不受MenuDropAlignment
检查的约束。
如果您使用PlacementMode.Custom
,那么您还需要将Popup.CustomPopupPlacementCallback
设置为有效方法,以便提供弹出窗口的CustomPopupPlacement[]
坐标。
来源:反射器
【讨论】:
很抱歉这个延迟 - 但我自己刚刚检查了反射器,并且 MenuDropAlignment 用于枚举中除中心之外的每个项目,并且中心不好,因为它那时(至于我可以看到)总是向下而不是向上滑动。【参考方案4】:这已经很老了,但如果你至少有 .net 4.5,我找到了另一种方法。
通过覆盖您的 menuitem/popup 模板,您可以使用以下触发器:
<DataTrigger Binding="Binding Path=(SystemParameters.MenuDropAlignment)" Value="[True/False]">
<Setter TargetName="Popup" Property="Placement" Value="[Left/Right]"/>
</DataTrigger>
当然,设置以下:
判断您的数据触发器值的真假, Left 或 Right 作为您的设置器值。绑定使用 Binding Path=() 语法而不是 Binding=x:static ... 很重要,这样您就可以使用静态类中的 StaticPropertyChanged 事件。
【讨论】:
【参考方案5】:这似乎是不可能的,所以我们在问题中求助于 MultiDataTrigger 来弥补。
【讨论】:
以上是关于是否可以让弹出窗口忽略 WPF / Touch 应用程序中的 MenuDropAlignment?的主要内容,如果未能解决你的问题,请参考以下文章
winform如何让弹出窗口始终置于程序最顶层,请问这个你是怎么实现的。