スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: スポンサー広告

VM側から特定コントロールにフォーカスを当てる(弐)。

VM側から特定コントロールにフォーカスを当てる(壱)。で挙げた課題を解消する方法を考える。
・Styleは使い回しが効かないので、いちいち書く必要がある。
→Styleで実装しなくても良いようにする。コード上で明示的にFocus()が呼べれば良い。
・プロパティ値もfalseに戻してからtrueに設定するという実装を意図的に行う必要がある
→VM側でfalseにするのではなく、コントロール側でfalseに戻るようにすれば良い。
実現できそうなのが、添付プロパティなので、作ることにする。

using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace TawamureDays {

/// <summary>
/// FrameworkElementビヘイビアクラス
/// </summary>
public static class FrameworkElementBehavior {

/// <summary>
/// フォーカスを当てるかどうかを取得します。
/// </summary>
/// <param name="obj">対象オブジェクト</param>
/// <returns>true:当てる</returns>
public static bool GetIsFocused(DependencyObject obj) {
return (bool)obj.GetValue(IsFocusedProperty);
}

/// <summary>
/// フォーカスを当てるかどうかを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト</param>
/// <param name="value">true:当てる</param>
public static void SetIsFocused(DependencyObject obj, bool value) {
obj.SetValue(IsFocusedProperty, value);
}

/// <summary>フォーカスを当てるかどうか</summary>
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool),
typeof(FrameworkElementBehavior),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));

/// <summary>
/// IsFocusedプロパティ変更イベントハンドラ
/// </summary>
/// <param name="dpObj">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void OnIsFocusedPropertyChanged(
DependencyObject dpObj, DependencyPropertyChangedEventArgs e) {
var element = dpObj as UIElement;

if (element == null) {
return;
}

if ((bool)e.NewValue) {
if (element.IsEnabled) {
if (element.Focusable) {
element.Focus();
} else {
//自身がフォーカスできなくても、子要素はできる可能性がある。
//次の要素へ移動させます。
element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}

SetIsFocused(element, false);
}

}
}
}
SetIsFocusedを呼んでいるのは、ソース側の値をfalseにするため。よって、バインディングのモードはTwoWay必須になる。
XAML側

<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:TawamureDays"
Title="MainWindow" Height="213" Width="385"
local:WindowBehavior.UseMessageCommand="True"
local:FrameworkElementBehavior.OnLoadedCommand="{Binding LoadedCommand}"
>
<StackPanel>
<TextBox MinWidth="100" Name="Text1"
Text="Text1"
local:FrameworkElementBehavior.IsFocused="{Binding IsText1Focus, Mode=TwoWay}"/>
<TextBox MinWidth="100" Name="Text2"
Text="Text2"
local:FrameworkElementBehavior.IsFocused="{Binding IsText2Focus, Mode=TwoWay}"/>
<TextBox MinWidth="100" Name="Text3"
Text="Text3"
local:FrameworkElementBehavior.IsFocused="{Binding IsText3Focus, Mode=TwoWay}"/>
<Button Content="Text1へフォーカス" Command="{Binding Focus1Command}"/>
<Button Content="Text2へフォーカス" Command="{Binding Focus2Command}"/>
<Button Content="Text3へフォーカス" Command="{Binding Focus3Command}"/>
</StackPanel>
</Window>

・前回よりはすっきりする(スタイル設定がないから)。
・バインディングはTwoWay必須となる(get; set;両方がpublicである必要有り)。
使いやすい分、前よりはマシかな。
(補足1)
仕事上、上記実装では不完全になった(1回目だけ動作し、以後は無反応)。
しかし、上のコードを今動かすと、ちゃんと2回目以降も動く。なぜだOrz。
2回目が動かない時は、SetIsFocused(false)の箇所を手直しする。

/// <summary>
/// IsFocusedプロパティ変更イベントハンドラ
/// </summary>
/// <param name="dpObj">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void OnIsFocusedPropertyChanged(
DependencyObject dpObj, DependencyPropertyChangedEventArgs e) {
var element = dpObj as UIElement;

if (element == null) {
return;
}

if ((bool)e.NewValue) {
if (element.IsEnabled) {
if (element.Focusable) {
element.Focus();
} else {
//自身がフォーカスできなくても、子要素はできる可能性がある。
//次の要素へ移動させます。
element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}

Action<UIElement> resetAction = elem => {
if (elem != null) {
SetIsFocused(elem, false);
}
};

//非同期実行
element.Dispatcher.BeginInvoke(resetAction, element);
element = null;
resetAction = null;

}
}

(補足2)
TwoWayでデータバインディングを行う事を前提で動作が保証されるようなプロパティは、
デフォルトでTwoWayになるように設定する。

/// <summary>フォーカスを当てるかどうか</summary>
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool),
typeof(FrameworkElementBehavior),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnIsFocusedPropertyChanged));

FrameworkPropertyMetadataは、UIPropertyMetadataクラスよりより詳細な設定ができる。
FrameworkPropertyMetadataOptions.BindsTwoWayByDefaultを設定する事で、Modeを指定しない時のデフォルトが「TwoWay」になる。XAMLでいちいちTwoWayを設定しなくて済む(設定し忘れる事での動作不良も防げる)。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。