Можно ли заставить всплывающее окно игнорировать MenuDropAlignment в приложении WPF/Touch?

В качестве предыстории: в Windows есть средство для сенсорных/планшетных ПК, с помощью которого оно меняет положение всплывающих окон/меню в зависимости от вашей «рукой» (чтобы меню не появлялось под вашей рукой).

По сути, это означает, что если вы настроены на «правшу» (что, по-видимому, по умолчанию установлено после подключения сенсорного устройства), каждое открываемое вами всплывающее окно искусственно смещается влево — это вызывает серьезные проблемы с макетом, когда мы пытаемся и выровняйте всплывающее окно с элементом, из которого оно «выскочило»:

Настройки планшетного ПК установлены на Левая рука — никаких искусственных исправлений со стороны Windows Настройка планшетных ПК установлены на Левую руку  вручил - никакой искусственной коррекции из винды

Настройка планшетного ПК «Правая рука» — Windows искусственно корректирует наше положение Настройка планшетного ПК «Правая»  handed - 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 оболочка:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}

И посмотрите сами, какой беспорядок он делает :-)


person Steven Robbins    schedule 17.02.2011    source источник


Ответы (5)


Я написал пользовательское всплывающее окно, которое решает эту проблему: вы можете установить свойство зависимости ForceAlignment и открыть его с помощью метода «Открыть», или вы можете напрямую вызвать методы «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
person HCAxel    schedule 14.01.2013
comment
Я не думаю, что этот ответ получает достаточно внимания. Это немного спрятано там, но если вы сделаете это, вы сможете отключить это для всей системы в две строки: FieldInfo fi = typeof(SystemParameters).GetField(_menuDropAlignment, BindingFlags.NonPublic | BindingFlags.Static); fi.SetValue (ноль, ложь); - person skimania; 28.03.2013
comment
Это не всегда работает в масштабах всей системы - кажется, что это имеет эффект в течение короткого периода времени, но сбрасывается. Он отлично работает в контексте примера — настройка непосредственно перед открытием — но, к сожалению, вы не можете установить его один раз. - person fubaar; 01.08.2013
comment
Следуйте моему предыдущему комментарию - вы можете сохранить настройку, сначала вызвав свойство SystemParameters.MenuDropAlignment - например. если у вас есть if (SystemParameters.MenuDropAlignment) перед кодом настройки отражения, установленное вами значение сохраняется, в противном случае оно сбрасывается. - person fubaar; 01.08.2013

Установите его в обычный режим для всего приложения:

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

fi.SetValue(null, false);
person skimania    schedule 27.03.2013

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[] вашего всплывающего окна.

Источник: Рефлектор

person Joshua Blake    schedule 17.02.2011
comment
Извините за задержку в этом - но я только что сам проверил отражатель, и MenuDropAlignment используется для каждого элемента в перечислении, кроме Center, и Center бесполезен, потому что тогда (насколько я вижу) всегда скользит вниз, а чем вверх. - person Steven Robbins; 28.03.2011

Это довольно старый способ, но я нашел другой способ, если у вас есть хотя бы .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 из своего статического класса.

person Julichan    schedule 28.12.2018

Казалось бы, это просто невозможно, поэтому мы прибегаем к MultiDataTrigger в вопросе, чтобы компенсировать это.

person Steven Robbins    schedule 28.07.2011