【WPF】マウスクリックでフォーカスされるようにする

WPFではWindows.Formと異なり、UIコントロールをボタンでクリックしてもフォーカス表示にならないし、そもそもフォーカス自体されないようです。Tabボタンでしかフォーカスできません。
でもそれじゃあUIとして不便というか、不親切なので、マウスクリックでもフォーカスされるようにしたいよね、というのが今回の内容です。

Githubのコードはこちら

実際の動作

動作確認もかねて、まずは適当なUIコントロールを持つ画面を作ります。
今回はTextBlockをBorderで囲ったものを上下に並べました。

Tabキーを押下するとフォーカスが遷移する。

ところがマウスクリックで選択してもフォーカスされない!
(マウスの軌道の色が変わっているのはキャプチャツールによるものです)

実装

stackoverflowの以下の記事が参考になりました。

Just a moment...

FocusAdorner.csファイルを作成し、以下を記載。
Tabキーのフォーカスデザインと合わせるために、OnRender内の数値を調整しています。
Microsoft コントロールのフォーカススタイルより)

C#
public class FocusAdorner : Adorner
{
    // Be sure to call the base class constructor.
    public FocusAdorner(UIElement adornedElement)
      : base(adornedElement)
    {
        IsHitTestVisible = false;
    }

    // A common way to implement an adorner's rendering behavior is to override the OnRender
    // method, which is called by the layout system as part of a rendering pass.
    protected override void OnRender(DrawingContext drawingContext)
    {
        var drawRect = LayoutInformation.GetLayoutSlot((FrameworkElement)this.AdornedElement);
        drawRect = new Rect(1, 1, drawRect.Width - 2, drawRect.Height - 2);

        // Some arbitrary drawing implements.
        SolidColorBrush renderBrush = new SolidColorBrush(Colors.Transparent);
        Pen renderPen = new Pen(new SolidColorBrush(Colors.Black), 1);
        renderPen.DashStyle = new DashStyle(new double[] { 1, 2 }, 0);

        drawingContext.DrawRoundedRectangle(renderBrush, renderPen, drawRect, 3, 3);
    }
}

.xaml.csに以下のメソッドを作成します。
元の回答の方ではStackPanelを指定していますが、様々なUIパーツに使用できるよう、UIElementとしています。

C#
private void Contact_Select(object send, MouseButtonEventArgs e)
{
    var sender = (UIElement)send;
    Keyboard.Focus(sender);
}

private void Got_Focus(object send, RoutedEventArgs e)
{
    var sender = (UIElement)send;
    AdornerLayer.GetAdornerLayer(sender).Add(new FocusAdorner(sender));
}

private void Lost_Focus(object send, RoutedEventArgs e)
{
    var sender = (UIElement)send;
    var layer = AdornerLayer.GetAdornerLayer(sender);
    foreach (var adorner in layer.GetAdorners(sender))
        layer.Remove(adorner);
}

これらを.xamlの方で呼び出すようにします。
回答の方ではPreviwMouseDownを使用していますが、PreviewMouseUpで呼びます。

XML
<Border Width="300" Height="100"  BorderBrush="LightSkyBlue" BorderThickness="5"
                Focusable="True" 
                PreviewMouseUp="Contact_Select"
                GotFocus="Got_Focus"
                LostFocus="Lost_Focus" Margin="0,48,0,260">
    <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="TextBlock" TextWrapping="Wrap"  Height="83" Width="288" FontSize="50"/>
</Border>

さあこれでクリックでもフォーカスが出るようになった……

あれ?なんかコントロールのサイズに合ってないぞ?
というわけで修正します。

ズレを修正

あまり詳しくなくて恐縮なのですが、どうも親要素、祖先要素などの関係で上記のようなサイズのズレが生じるようです。
こちらの件についてもstackoverflowにお世話になりました。

Just a moment...

ForcusAdorner.csのOnRenderの処理前半を修正します。

C#
protected override void OnRender(DrawingContext drawingContext)
{
    // 修正
    var element = (FrameworkElement)this.AdornedElement;
    var drawRect = new Rect(0, 0, element.ActualWidth, element.ActualHeight);

    drawRect = new Rect(1, 1, drawRect.Width - 2, drawRect.Height - 2);

    // Some arbitrary drawing implements.
    SolidColorBrush renderBrush = new SolidColorBrush(Colors.Transparent);
    Pen renderPen = new Pen(new SolidColorBrush(Colors.Black), 1);
    renderPen.DashStyle = new DashStyle(new double[] { 1, 2 }, 0);

    drawingContext.DrawRoundedRectangle(renderBrush, renderPen, drawRect, 3, 3);
}

完成!

無事にマウスクリックでフォーカスされるようになりました!
Tabキーでも変わらずフォーカスされます。

コメント

タイトルとURLをコピーしました