OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

WPF Windows8で実行された際にポップアップが左右逆に出るのを防ぐ

前回書いてた問題、解決策が見つかったぜはっはー。
さすがはstackoverflowやー。

[WPF]文字列が表示領域に入りきらないときにポップアップで全文を表示する

↑の記事の最後に書いたとおり、
WPFのPopUpとかToolTipのPlacementの設定と、
Windows8のタブレット用の「利き手」の設定が競合するという残念な現象があって、
ポップアップの配置が指定どおりに表示されないことがある。

具体的にいうと、Placement="Left"って指定してんのに右側に出るよって話だ。
以下コード例。

<Window x:Class="PopupPlacementControlSample.MainWindow"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        Title="MainWindow" Height="350" Width="525">  
    <Grid>  
        <Button Width="100" Height="30" Content="ボタン" Name="button" />  
          
        <Popup PlacementTarget="{Binding ElementName=button}"   
               Placement="Left"   
               IsOpen="True">  
            <Label Content="ボタンの左" Background="Orange" />   
        </Popup>  
          
    </Grid>  
</Window>  

これが↓こんなふうに表示されて、なんじゃこら!?となる。

popup-placement

で、前回も書いたとおり、実行するPCなりタブレットなりで
コントロール パネル>ハードウェアとサウンド>タブレット PC 設定>きき手
で「右きき」/「左きき」を変えれば一応解決するけど、
自分が使うソフトでない場合、ユーザーにこれやってくださいって言うのもねぇ…という点で
未解決だったんですが、これをプログラム側で制御する方法がありましたよと。

↓これこれ。
.net - WPF Handedness with Popups - Stack Overflow

アンサーが2つあるけど、下のほうがスマートだな。
どっちにしてもやってるのは、SystemParameters.MenuDropAlignmentの値を
リフレクション使って書き換えるっていう、なかなか強引な方法である。

上に記載したxamlのコードビハインドに書き足してみる。

/// <summary>  
/// MainWindow.xaml の相互作用ロジック  
/// </summary>  
public partial class MainWindow : Window  
{  
    private static readonly FieldInfo _menuDropAlignmentField;  
  
    //静的コンストラクタ  
    static MainWindow()  
    {  
        //フィールド情報を取得  
        _menuDropAlignmentField = typeof(SystemParameters).GetField("_menuDropAlignment", BindingFlags.NonPublic | BindingFlags.Static);  
  
        //アサーション(なくても良い)  
        System.Diagnostics.Debug.Assert(_menuDropAlignmentField != null);  
  
        EnsureStandardPopupAlignment();  
        SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;  
    }  
  
    private static void SystemParameters_StaticPropertyChanged(object sender, PropertyChangedEventArgs e)  
    {  
        EnsureStandardPopupAlignment();  
    }  
  
    private static void EnsureStandardPopupAlignment()  
    {  
        //MenuDropAlignmentがTrueならFalseに書き換える  
        if (SystemParameters.MenuDropAlignment && _menuDropAlignmentField != null)  
        {  
            _menuDropAlignmentField.SetValue(null, false);  
        }  
    }  
  
    public MainWindow()  
    {  
        InitializeComponent();  
    }  
}  

確かにこれでちゃんとポップアップが左に表示されるようになった。
プライベートフィールドの名前を勝手に決め打ちで指定してるので
あやうい方法ではありますけども。