17

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 :-)

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
Steven Robbins
  • 26,441
  • 7
  • 76
  • 90

5 Answers5

3

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
Scott Solmer
  • 3,871
  • 6
  • 44
  • 72
HCAxel
  • 69
  • 3
  • 1
    I don't think this answer gets enough credit. It's a bit hidden in there, but if you do this, you can essentially turn this off systemwide in two lines: FieldInfo fi = typeof(SystemParameters).GetField("_menuDropAlignment", BindingFlags.NonPublic | BindingFlags.Static); fi.SetValue(null, false); – skimania Mar 27 '13 at 22:29
  • 1
    This doesn't work system wide consistently - it seems to have an effect for a short period of time, but resets. It works fine in the context of the example - setting just before opening - but unfortunately you can't just set it once. – fubaar Aug 01 '13 at 12:05
  • 4
    Follow up to my previous comment - you can make the setting "stick" by calling the SystemParameters.MenuDropAlignment property first - e.g. if you have "if (SystemParameters.MenuDropAlignment)" before the reflection setting code, the value you set appears to stick, otherwise it is reset. – fubaar Aug 01 '13 at 12:30
2

Set it to regular mode for your whole application:

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

fi.SetValue(null, false);
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
skimania
  • 917
  • 1
  • 9
  • 20
0

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

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Joshua Blake
  • 346
  • 1
  • 7
  • Sorry for the delay in this one - but I've just checked reflector myself and MenuDropAlignment is used for every item in the enum apart from Center, and Center is no good because it then (as far as I can see) always slides down rather than up. – Steven Robbins Mar 28 '11 at 10:49
0

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.

-1

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

Steven Robbins
  • 26,441
  • 7
  • 76
  • 90