読者です 読者をやめる 読者になる 読者になる

開発記その 3 - HTMLのようにデフォルタボタンを設定したいなら Behavior で -

なかなかユーザー作成にたどり着けないでいてますが・・・。

画面を立ち上げて動作確認をしてると、テキストボックスに値を入力して、キーボードから手をはなしてマウスを持ってボタンをクリックするのが非常にめんどくさくなってきます。HTML の Submit ならそのままエンターキーでクリックできのに。。

そこで、、、

作りました。

public class DefaultButtonBehavior : Behavior<FrameworkElement> {

  public ButtonBase TargetButton {
    get { return (ButtonBase)GetValue(TargetButtonProperty); }
    set { SetValue(TargetButtonProperty, value); }
  }

  public static readonly DependencyProperty TargetButtonProperty =
    DependencyProperty.Register("TargetButton", typeof(ButtonBase), typeof(DefaultButtonBehavior), null);

  private void AssociatedObject_KeyDown(object sender, KeyEventArgs e) {
    if (e.Key != Key.Enter || this.TargetButton == null) {
      return;
    }
    AutomationPeer peer = ButtonBaseAutomationPeer.CreatePeerForElement(this.TargetButton);
    peer.SetFocus();

    Dispatcher.BeginInvoke(() => {
      ((IInvokeProvider)peer.GetPattern(PatternInterface.Invoke)).Invoke();
    });
  }

  protected override void OnAttached() {
    base.OnAttached();
    this.AssociatedObject.KeyDown += AssociatedObject_KeyDown;
  }

  protected override void OnDetaching() {
    base.OnDetaching();
    this.AssociatedObject.KeyDown -= AssociatedObject_KeyDown;
  }
}

設定方法は以下のような感じ。

<Grid Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top">
  <i:Interaction.Behaviors>
    <localInteractivity:DefaultButtonBehavior TargetButton="{Binding ElementName=CreateUserButton}" />
  </i:Interaction.Behaviors>

  <Button Content="{Binding CreateUserButton,Source={StaticResource Strings}}" 
      Grid.Row="3" Grid.ColumnSpan="2" HorizontalAlignment="Right" 
      Width="90" Height="25" x:Name="CreateUserButton">
    <i:Interaction.Triggers>
      <i:EventTrigger EventName="Click">
        <localInteractivity:InvokeMethodAction 
          Target="{Binding DataContext, ElementName=Self}" MethodName="CreateUser"/>
      </i:EventTrigger>
    </i:Interaction.Triggers>
  </Button>

</Grid>

ポイントは、Invoke する前にフォーカスをボタンに移して Dispatcher で一呼吸おいてるところでしょうか。どうも VM のプロパティにバインディングされるのがロストフォーカスした後っぽい(未検証)ので、こういう動きにしました。これがないと、 VM のプロパティに値が設定されずにクリックイベントを受けたメソッドが動いてしまいました。


すべてのコントロールやタイミングでこのパターンが有効かどうかは未検証ですが、とりあえずは OK ってことで。


※今回のチェックイン
http://moneybook.codeplex.com/SourceControl/changeset/changes/51734