【.NET】【WPF】Bindingを強制反映するやりかた

遊びでC#を触ったら、まんまと有給を潰してしまった。

毒舌に.NETのWPFのBinding

  • .NET Framework
    • Microsoftが提供している、ウィンドウアプリケーションのランタイム環境。 *ウィンドウ付きのアプリが(裏方にある膨大な概念を意識するのに比べて)いとも簡単に(小さいアプリなら)できてしまう。
  • WPF

  • Binding

    • 従来だと、テキストボックスの文字や有効/無効などのプロパティをコード上の変数とで代入したりこねたりする必要があったのを、XAMLに「{Binding プロパティ名}」って書いたら自動で関連付けてくれるようにする機能。
    • 今回一番悪口を言いたいところ

事故

やりたかったこと

  • テキストボックスのフォーカスが離れた時点で、テキストボックスの値を検証しフォーム上にエラーメッセージを出したい
public partial class ReplaceSetEntryWnd : Window

   public ReplaceSet Value { get; set; } // ビューモデル。定義は割愛

   // フォーカスを失った時に実行するイベントハンドラ
   private void Controls_LostFocus(object sender, RoutedEventArgs e)
   {
      _validateAndShowError();
   }
<!-- Valueのプロパティと連携 -->
<TextBox Name="txName" Text="{Binding Value.Name}" LostFocus="Controls_LostFocus"/>
<TextBox Name="txPattern" Text="{Binding Value.Pattern}" LostFocus="Controls_LostFocus"/>

起きたこと

  • Value.xxxに反映するのは、LostFocusイベントが終わった後。つまり、↑のコードは、テキストボックスからフォーカスが離れた時点では、編集直前のValueの状態が残ったままで _validateAndShowError() を実行してしまう。

対策

  • コントロールから、Binding用のクラスっぽいものを直接取得してコントロール → プロパティに強制送信する。
    private void UpdateSources()
    {
        txName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
        txPattern.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }
  • ちなみに逆の プロパティ → コントロール強制送信はこれ。
    private void UpdateSources()
    {
        txName.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
        txPattern.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
    }
  • これがマジでイケてない。txNameとかを直接指定したコード組んで、ビューモデルとの連携を疎にする目的で導入したいのに、これができるんならじゃあと欲張ると、結局依存性を増やし記述がごっちゃになるハメに。
  • ちなみに、以下のようにマルチスレッドしながら遅延実行するコードも書いてみたが、.NETコントロールいじるコードで別スレッドからのアクセス違反になった。
new Task(() => {
   Task.Delay(20).Wait();
   _validateAndShowError();
}).Wait();
  • 下手にマルチスレッドするとこうなるの。。非同期でUIのロックを回避したいのに、結局シングルスレッドにせざるをえないってこれもうわかんねえな
  • バックグラウンドプロセス⇔UIの分業と同期をより細かい単位にするために、外スレッドからUI変更をキューイングしていい感じに更新するような仕組みかもしくはFLUXにインスパイヤされた新しいフレームワークがほしい
    • え?なに?Electorn?

2017/09/23 タイトル変えました