none
ComboBox の IME を無効にする方法は? RRS feed

  • 質問

  • ComboBox の IME を完全に無効にする方法はあるでしょうか?

    通常 Control の IME を無効にするには

    InputMethod.IsInputMethodEnabled = False

    もしくは

    InputMethod.IsInputMethodSuspended = True

    だと思うんですが、IsEditable="True" にした ComboBox は、XAML で IME を無効にしても半角/全角キーを押すと IME を ON にできてしまいます。
    そこで調査したところ、ComboBox の編集時は要素である TextBox に対して設定すればいいのではとの記事を読み、さらに調べるうちに下記の記事を見つけました。

    MaxLength for editable ComboBox

    以下は上記の記事を基に起こしたコードです。若干修正しています。 

    /// <summary>
    /// ComboBox 添付ビヘイビア
    /// </summary>
    public class ComboBoxBehaviors {
    
    	public static readonly DependencyProperty MaxLengthProperty =
    		DependencyProperty.RegisterAttached("MaxLength", typeof(int),
    		typeof(ComboBoxBehaviors), new UIPropertyMetadata(OnMaxLengthChanged));
    
    	public static int GetMaxLength(DependencyObject obj) {
    		return (int)obj.GetValue(MaxLengthProperty);
    	}
    
    	public static void SetMaxLength(DependencyObject obj, int value) {
    		obj.SetValue(MaxLengthProperty, value);
    	}
    
    	private static void OnMaxLengthChanged
    			(DependencyObject obj, DependencyPropertyChangedEventArgs args) {
    
    		var comboBox = obj as ComboBox;
    		if (comboBox == null) return;
    
    		comboBox.Loaded +=
    			(s, e) => {
    				var textBox = comboBox.FindChild(typeof(TextBox), "PART_EditableTextBox");
    				if (textBox == null) return;
    
    				textBox.SetValue(TextBox.MaxLengthProperty, args.NewValue);
    			};
    	}
    }
    
    public static class UIChildFinder {
    
    	public static DependencyObject FindChild
    			(this DependencyObject reference, Type childType, string childName) {
    
    		DependencyObject foundChild = null;
    		if (reference != null) {
    
    			int childrenCount = VisualTreeHelper.GetChildrenCount(reference);
    			for (int i = 0; i < childrenCount; i++) {
    
    				var child = VisualTreeHelper.GetChild(reference, i);
    
    				// If the child is not of the request child type child
    				if (child.GetType() != childType) {
    					// recursively drill down the tree
    					foundChild = FindChild(child, childType, childName);
    					if (foundChild != null) break;
    
    				} else if (!string.IsNullOrEmpty(childName)) {
    					var frameworkElement = child as FrameworkElement;
    
    					// If the child's name is set for search
    					if (frameworkElement != null && frameworkElement.Name == childName) {
    						// if the child's name is of the request name
    						foundChild = child;
    						break;
    					}
    				} else {
    					// child element found.
    					foundChild = child;
    					break;
    				}
    			}
    		}
    		return foundChild;
    	}
    }
    
    

    XAML
    <ComboBox pz:ComboBoxBehaviors.MaxLength="10" />

    この場合、MaxLength は有効になります。しかし上記コードを参考にして、以下のように IsInputMethodEnabled を定義しても、IME を無効にすることができません。何か根本的に間違っているでしょうか?

    public static readonly DependencyProperty IsInputMethodEnabledProperty =
    	DependencyProperty.RegisterAttached("IsInputMethodEnabled", typeof(bool),
    	typeof(ComboBoxBehaviors), new UIPropertyMetadata(OnIsInputMethodEnabledChanged));
    
    public static bool GetIsInputMethodEnabled(DependencyObject obj) {
    	return (bool)obj.GetValue(IsInputMethodEnabledProperty);
    }
    
    public static void SetIsInputMethodEnabled(DependencyObject obj, bool value) {
    	obj.SetValue(IsInputMethodEnabledProperty, value);
    }
    
    private static void OnIsInputMethodEnabledChanged
    		(DependencyObject obj, DependencyPropertyChangedEventArgs args) {
    
    	var comboBox = obj as ComboBox;
    	if (comboBox == null) return;
    
    	comboBox.Loaded +=
    		(s, e) => {
    			var textBox = comboBox.FindChild(typeof(TextBox), "PART_EditableTextBox");
    			if (textBox == null) return;
    
    			textBox.SetValue(InputMethod.IsInputMethodEnabledProperty, args.NewValue);
    		};
    }
    

    ひらぽん http://d.hatena.ne.jp/hilapon/
    2011年1月6日 6:05
    モデレータ

回答

  • こんにちわ。

     

    実装方法としてはComboBoxテンプレート内のTextBoxの「InputMethod.IsInputMethodEnabled」添付プロパティを操作してIMEを変更するので問題ありません。

    今回問題なのは、ComboBoxBehaviorsで定義した「IsInputMethodEnabled」依存関係プロパティの方です。

    依存関係プロパティの定義箇所にて、UIPropertyMetadataの第一引数を使用してコールバックメソッドを登録していますが、それを以下のようにするときちんと動作します。

     

    public static readonly DependencyProperty IsInputMethodEnabledProperty =

    DependencyProperty.RegisterAttached("IsInputMethodEnabled", typeof(bool),

    typeof(ComboBoxBehaviors), new UIPropertyMetadata(true, new PropertyChangedCallback(OnIsInputMethodEnabledChanged)));

     

    上記ではUIPropertyMetadataの第1引数で依存関係プロパティの初期値を定義しています。

    原因だったのは依存関係プロパティの「初期値」です。bool型のdefault値は「false」ですので、XAML上で上記依存関係プロパティに対し「false」を設定してもプロパティ変更用のコールバックメソッドが呼ばれていないのが原因です。試しにコールバックメソッドでブレークポイントを設定すると呼ばれないのが分かると思います。

     

    私の環境ではひらぽんさんのコードをそのまま拝借し、初期値を設定した所、問題なくIME制御が行えるようになっています。

     


    ★良い回答には回答済みマークを付けよう! kazuto Blog : http://blogs.wankuma.com/kzt/
    2011年1月6日 11:49

すべての返信

  • こんにちわ。

     

    実装方法としてはComboBoxテンプレート内のTextBoxの「InputMethod.IsInputMethodEnabled」添付プロパティを操作してIMEを変更するので問題ありません。

    今回問題なのは、ComboBoxBehaviorsで定義した「IsInputMethodEnabled」依存関係プロパティの方です。

    依存関係プロパティの定義箇所にて、UIPropertyMetadataの第一引数を使用してコールバックメソッドを登録していますが、それを以下のようにするときちんと動作します。

     

    public static readonly DependencyProperty IsInputMethodEnabledProperty =

    DependencyProperty.RegisterAttached("IsInputMethodEnabled", typeof(bool),

    typeof(ComboBoxBehaviors), new UIPropertyMetadata(true, new PropertyChangedCallback(OnIsInputMethodEnabledChanged)));

     

    上記ではUIPropertyMetadataの第1引数で依存関係プロパティの初期値を定義しています。

    原因だったのは依存関係プロパティの「初期値」です。bool型のdefault値は「false」ですので、XAML上で上記依存関係プロパティに対し「false」を設定してもプロパティ変更用のコールバックメソッドが呼ばれていないのが原因です。試しにコールバックメソッドでブレークポイントを設定すると呼ばれないのが分かると思います。

     

    私の環境ではひらぽんさんのコードをそのまま拝借し、初期値を設定した所、問題なくIME制御が行えるようになっています。

     


    ★良い回答には回答済みマークを付けよう! kazuto Blog : http://blogs.wankuma.com/kzt/
    2011年1月6日 11:49
  • 実装方法としてはComboBoxテンプレート内のTextBoxの「InputMethod.IsInputMethodEnabled」添付プロパティを操作してIMEを変更するので問題ありません。今回問題なのは、ComboBoxBehaviorsで定義した「IsInputMethodEnabled」依存関係プロパティの方です。

    依存関係プロパティの定義箇所にて、UIPropertyMetadataの第一引数を使用してコールバックメソッドを登録していますが、それを以下のようにするときちんと動作します。

     public static readonly DependencyProperty IsInputMethodEnabledProperty =DependencyProperty.RegisterAttached("IsInputMethodEnabled", typeof(bool),typeof(ComboBoxBehaviors), new UIPropertyMetadata(true, new PropertyChangedCallback(OnIsInputMethodEnabledChanged)));

     上記ではUIPropertyMetadataの第1引数で依存関係プロパティの初期値を定義しています。

    原因だったのは依存関係プロパティの「初期値」です。bool型のdefault値は「false」ですので、XAML上で上記依存関係プロパティに対し「false」を設定してもプロパティ変更用のコールバックメソッドが呼ばれていないのが原因です。試しにコールバックメソッドでブレークポイントを設定すると呼ばれないのが分かると思います。

    私の環境ではひらぽんさんのコードをそのまま拝借し、初期値を設定した所、問題なくIME制御が行えるようになっています。


    ありがとうございます。ご指摘どおり、UIPropertyMetadata のコンストラクタの第一パラメータに true を設定したら、こちらの想定どおりの動作をするようになりました!

    原因だったのは依存関係プロパティの「初期値」です。bool型のdefault値は「false」ですので、XAML上で上記依存関係プロパティに対し「false」を設定してもプロパティ変更用のコールバックメソッドが呼ばれていないのが原因です。試しにコールバックメソッドでブレークポイントを設定すると呼ばれないのが分かると思います。


    試してみたら確かにそうでした。この辺りは今後注意する必要はありそうです。貴重な回答有難うございました。

    しかし IME の挙動は WinForm と根本的に違っており、移行する人は相当混乱すると思われます。この問題・・・ComboBox の IME を無効にしても有効になる・・・は Connect  に提案もしくは問題として挙げておきたいと思います。

     


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2011年1月7日 1:38
    モデレータ