1

すべてのコマンドが次のパターンに従う Winforms UI を実装しているとします。

interface ICommand
{
    bool CanExecute { get; }
    void Execute();
}

このようなコマンドをトリガーするボタンまたはメニュー項目には、次の設定が必要です。

  • プロパティEnabledはコマンドのCanExecute
  • イベントClickはコマンドにリンクされますExecute(メソッド シグネチャが異なるため、中間イベント ハンドラーを介して)

問題CanExecuteINotifyPropertyChangedは、このプロパティを直接変更することはできず、コマンドに関連する必要のないプログラム内の他の要因に依存するため、ここでは機能しないための実装です。PropertyChangedまた、プログラムのまったく無関係な部分でコマンドのイベントをトリガーする必要はありません。

が変更されたことをデータ バインディング マネージャーにどのように通知しますCanExecuteか?

これが私の問題の(純粋に架空の)例です:

bool CanExecute
{
    get
    {
        return User.LoggedInForAtLeastNMinutes(5);
        // how would you trigger data-binding updates for CanExecute? 
    }
}

理想的には、UI が (揮発性のフィールドであるかのように) 常にチェックするようにしたいのですCanExecuteが、これは Winforms のデータ バインディングの仕組みではありません。誰でもこの問題の解決策を持っていますか?


注:ところで、私はWPFを認識しています。私の質問の背景は、WPF の一般的な方向で既存の Winforms アプリケーションを徐々に改善しようとしているということです。しかし、実際にWPFを使用して、私が尋ねた問題を取り除くことは、今のところ実現不可能です。

4

3 に答える 3

2

関係なく実装します(または同じ効果を持つイベントをINotifyPropertyChanged追加します)。CanExecuteChangedポーリングではなく、適切なタイミングでプロパティ変更イベントを発生させるタイミングをオブジェクトが認識できるように努力します。

たとえば、架空の例では、UserLoggedInイベントが発生する可能性があります。これに応じて、5分のタイマーを設定できます。そのタイマーが経過すると、プロパティ変更イベントが発生します。

ポーリングアプローチを採用する場合、2つの危険に直面します。

  • ポーリングが多すぎる場合、アプリケーションがCPUを消費して、まだ発生しない可能性のあるイベントをチェックします(たとえば、10秒ごとにポーリングして5分が経過しているかどうかを確認します)
  • プロパティにバインドされたコントロールがCanExecuteUIの残りの部分より遅れる場合(たとえば、テキストを選択してからCopyTextCommand.CanExecuteプロパティを更新するまでの遅延)、十分な頻度でポーリングされません。

ハイブリッドアプローチは、C++のMicrosoftFoundation Classesが採用したアプローチであり、アプリケーションのメッセージループがアイドル状態のときにこのチェックを行うことでした。これは、プロパティに影響を与える可能性のあるユーザーインターフェイスの相互作用のみがわかっている場合に合理的なアプローチですCanExecute

于 2010-08-01T10:10:49.597 に答える
1

a を使用して、プロパティTimerを常にポーリングしCanExecuteます。PropertyChangedプロパティが変更されたときにイベントを発生させます。

于 2010-08-01T09:44:33.340 に答える
0

Application.Idle イベントでポーリングを行います。実行できるロジックが単純である限り、問題はないはずです。

これは、現在の「CommandManager」実装の抜粋です。

 public CommandManager()
    {
        Commands = new List<ICommand>();

        Binders = new List<ICommandBinder>
                      {
                          new ControlBinder(),
                          new MenuItemCommandBinder()
                      };

        Application.Idle += UpdateCommandState;
    }
 private void UpdateCommandState(object sender, EventArgs e)
    {
        Commands.Do(c => c.Enabled);
    }

(Do() は、linq Select() のように foreach を実行する拡張メソッドですが、Func の代わりに Action を使用します)

これについては以前ブログに書いたことがあります。チェックしてみてください

それが役に立てば幸い

于 2010-12-01T21:01:51.417 に答える