none
ひとつのForm内にToolTipコンポーネントを複数作成しツールチップヒントの表示内容をローカライズした場合に、設定値が壊れる RRS feed

  • 質問

  • アプリケーション開発初心者です。

    現在、Visual C# にて、フォームエディターを使ってWindows Forms アプリケーションのUI部分を作成しています。

    フォーラムタイトルの通りですが、ToolTip コンポーネントを2つ以上作成し、言語によってツールチップヒントの表示内容が変わるように設定した場合に、
    ツールチップヒントの表示内容が自分で設定した値から変わってしまう事象が発生しました。

    この挙動が正しい(当方のToolTipの使い方が誤っている等)のか、それとも Visual Studio の不具合なのか、
    また、どのような操作をすれば意図した挙動になるのか、
    ご教授願います。


    尚、プロジェクトを新規に作成し以下の手順で操作したところ、同じ事象が発生しました。

    1. フォーム上にコントロール(ここでは Button コントロールとします)を2つ(button1、button2)作成します。

    2. さらに ToolTip コンポーネントを2つ(toolTip1、toolTip2)作成します。

    3. プロパティグリッドにて、各ボタンにツールチップヒントの表示内容を設定します。
    button1 [toolTip1 の ToolTip]:test1
    button1 [toolTip2 の ToolTip]:(空欄)
    button2 [toolTip1 の ToolTip]:(空欄)
    button2 [toolTip2 の ToolTip]:test2

    4. フォームの Language を“(既定値)”から“日本語 (日本)”に変更します。

    5. 3と同様、各ボタンにツールチップヒントの表示内容を設定します。
    button1 [toolTip1 の ToolTip]:テスト1
    button1 [toolTip2 の ToolTip]:(空欄)
    button2 [toolTip1 の ToolTip]:(空欄)
    button2 [toolTip2 の ToolTip]:テスト2

    6. フォームの Language を“(既定値)”に戻します。


    以上の操作を行い、各 Button コントロールに設定したツールチップヒントの表示内容を確認すると、以下の通りになりました。
    button1 [toolTip1 の ToolTip]:test1
    button1 [toolTip2 の ToolTip]:(空欄)
    button2 [toolTip1 の ToolTip]:test2
    button2 [toolTip2 の ToolTip]:テスト2

    見たところ、フォームの各言語用のresxファイル(Form1.resxとForm1.ja-JP.resx)の内容が混ざってしまっている(?)ように感じます。

    私事ですが説明が苦手なもので今一つわかりにくい文章になってしまっているかとは思いますが、よろしくお願いします。


    開発環境:
     Visual Studio Community 2017 15.2
     .NET Framework 4.6.01055
    2017年5月21日 10:20

回答

  • ToolTip クラスは「拡張プロバイダー」と呼ばれる仕組みでプロパティを増やしているのでその仕組み上の制限と言えるかもしれません。

    一般的なプロパティの場合、LocalizableAttribute でそのプロパティがローカライズ必要であることが示され、resx に保存されます。
    しかし、ToolTip の場合、プロパティに値を設定した時に、Designer.cs の InitializeComponent にリソースから文字列を設定するコードを生成されるようになっています。
    つまり、値を設定するまではリソースから読み込むような動きをしない状態です。

    // 
    // button2
    // 
    resources.ApplyResources(this.button2, "button2");
    this.button2.Name = "button2";
    this.toolTip1.SetToolTip(this.button2, resources.GetString("button2.ToolTip"));
    
    // 通常のコントロールは ApplyResources で全プロパティをまとめて適用
    // ToolTip は個別に SetToolTip メソッドを呼び出す

    また、この Designer.cs にコードを追加する動きは、「現在選択されているカルチャのリソースが設定値のすべてである」とみなしている(VS2015 の振る舞いからの推測)ようなので、たとえば、ja-jP.resx に ToolTip の設定値が残っている状態で、ニュートラルカルチャで ToolTip プロパティをリセットしてしまうと、リソースから読み込むコードが Designer.cs に残りません。

    拡張プロバイダーのローカライズに対する、Windows Forms デザイナーの設計不良と言えるかもしれませんが、ToolTip クラスの説明 には「通常、1 つの ToolTip コンポーネントを使用して、1 つのフォーム上にある複数のコントロールのツールヒントを作成します」とあるので、サポート外のシナリオといえるかもしれません。
    対応策としては「ToolTip を複数置かない」、ToolTip の設定先をどうしても分離したいなら、その「分離したい単位でユーザーコントロール」にすることです。
    (あるいは、自分で拡張プロバイダーを実装してプロパティ名を分けて保存できるようにし、実行時に ToolTip クラスに SetToolTip メソッドで割り当てるとか。デザイン時と実行時を適切に区別する必要があるので手間ですが…)

    2017年5月21日 12:58
    モデレータ

すべての返信

  • 私の環境 (Windows 10 x64 & Visual Studio 2017 15.0.0) でご質問の操作を試してみたところ、再現しました。

    ただ、結果が少し違い Language を "(規定値)" に戻したときに、

    button1 [toolTip1 の ToolTip]:test1
    button1 [toolTip2 の ToolTip]:(空欄)
    button2 [toolTip1 の ToolTip]:test2
    button2 [toolTip2 の ToolTip]:test2

    となりました。恐らくですが、おっしゃる通り Visual Studio の不具合なのでは(?)と思いました。Form1.ja-JP.resx の編集により Form1.resx (button2 [toolTip2 の ToolTip]) の値が変わってしまっている。挙動をすべて把握できてはいませんが、 Language を切り替えたときに、正しい値が入っているか確認し、想定する値になっていない場合(書き変わってしまっている場合)は、再度値を設定し直す必要がありそうですね。

    # Visual Studio 2015 でも、まったく同じ現象が再現しました。

    2017年5月21日 10:59
  • ToolTip クラスは「拡張プロバイダー」と呼ばれる仕組みでプロパティを増やしているのでその仕組み上の制限と言えるかもしれません。

    一般的なプロパティの場合、LocalizableAttribute でそのプロパティがローカライズ必要であることが示され、resx に保存されます。
    しかし、ToolTip の場合、プロパティに値を設定した時に、Designer.cs の InitializeComponent にリソースから文字列を設定するコードを生成されるようになっています。
    つまり、値を設定するまではリソースから読み込むような動きをしない状態です。

    // 
    // button2
    // 
    resources.ApplyResources(this.button2, "button2");
    this.button2.Name = "button2";
    this.toolTip1.SetToolTip(this.button2, resources.GetString("button2.ToolTip"));
    
    // 通常のコントロールは ApplyResources で全プロパティをまとめて適用
    // ToolTip は個別に SetToolTip メソッドを呼び出す

    また、この Designer.cs にコードを追加する動きは、「現在選択されているカルチャのリソースが設定値のすべてである」とみなしている(VS2015 の振る舞いからの推測)ようなので、たとえば、ja-jP.resx に ToolTip の設定値が残っている状態で、ニュートラルカルチャで ToolTip プロパティをリセットしてしまうと、リソースから読み込むコードが Designer.cs に残りません。

    拡張プロバイダーのローカライズに対する、Windows Forms デザイナーの設計不良と言えるかもしれませんが、ToolTip クラスの説明 には「通常、1 つの ToolTip コンポーネントを使用して、1 つのフォーム上にある複数のコントロールのツールヒントを作成します」とあるので、サポート外のシナリオといえるかもしれません。
    対応策としては「ToolTip を複数置かない」、ToolTip の設定先をどうしても分離したいなら、その「分離したい単位でユーザーコントロール」にすることです。
    (あるいは、自分で拡張プロバイダーを実装してプロパティ名を分けて保存できるようにし、実行時に ToolTip クラスに SetToolTip メソッドで割り当てるとか。デザイン時と実行時を適切に区別する必要があるので手間ですが…)

    2017年5月21日 12:58
    モデレータ
  • kenjinoteさん

    やはりVSの不具合の可能性が高いんですね…

    教えていただいた通り言語を切り替えた際に壊れた設定値を修正してみましたが、
    その都度別の言語用の設定値が壊れてしまうようでした(“(既定値)”で設定値を修正すると“日本語 (日本)”の設定値が壊れ、逆もまた然り)ので、
    別の方法を模索してみます。

    回答ありがとうございました。

    • 編集済み 亜琦奈 2017年5月22日 5:49
    2017年5月22日 3:55
  • Azuleanさん

    回答ありがとうございます。

    なるほど、そもそも複数の ToolTip を置く設計はあまりよろしくないんですね。
    (ToolTip クラスの説明を読んでおくべきでした)

    ToolTip を複数置いた経緯としては、ツールチップヒントの表示内容と同じように ToolTipTitle もコントロール毎に変えたかったためです。
    (ToolTipTitle はコントロール毎ではなく ToolTip オブジェクト毎にしか設定できないようなので)

    なので、自前で拡張プロバイダを実装して…という大掛かり(?)なことをするのも気が引けるので、
    「分離したい単位でユーザーコントロール」化する方向で考えてみようと思います。

    • 編集済み 亜琦奈 2017年5月22日 5:50
    2017年5月22日 5:49