1

これは長い質問になるだろうが、私は醜い大きなオールインワンクラスを構築することで、長い間この問題を回避してきた.

例として、私は MVVM 設計 (Caliburn.Micro も使用しています) で WPF スタンドアロン アプリケーションを作成してMainViewModelおり、MainView. このビューには が含まれておりStackPanel、このコンテンツはStackPanelViewModel にバインドされていCentralVMます。

<StackPanel DockPanel.Dock="Top">
     <ContentControl Margin="10" Name="CentralVM"/>
</StackPanel>

クラスにはMainViewModel、他にもいくつかの ViewModel があります。

private PropertyChangedBase _centralVM = new PropertyChangedBase();        
private LoggedInViewModel _loggedInVM = new LoggedInViewModel();
private LoginViewModel _logInVM = new LoginViewModel();

public PropertyChangedBase CentralVM {
    get { return _centralVM; }
    set { _centralVM = value; NotifyOfPropertyChange(() => CentralVM); }
}
public LoggedInViewModel LoggedInVM {
    get { return _loggedInVM; }
    private set { _loggedInVM = value; }
}
public LoginViewModel LoginVM {
    get { return _logInVM; }
    private set { _loginVM = value;}
}

さて、MainViewModelI セットのコンストラクターで

CentralVM = LoginVM

そして、StackPanel自動的に View にバインドされますLoginView。はLoginViewあなたが推測することを行います。つまり、(ユーザー名、パスワード)を入力できます。エントリを評価するボタンがあり、それが正しい場合は、設定を に切り替えたいと思いCentralVMますLoggedInVM。しかし、ボタン イベントは のLoginVMインスタンスに「存在」しLoginViewModelます。CentralVMMainViewModel

もちろん、これは一般的な種類の問題の例にすぎません。私の最初のアイデアは、次のことを行うことでした。

-これには、ボタンがクリックされたときに設定される、LoginVM呼び出される (文字列型の) プロパティが含まれます。-次のようLoggedInAsに、にメソッドを追加します。MainViewModel

private _loggedIn = false;
private void CheckForLoginChange() {
    if (_loggedIn == false && !String.IsNullOrEmpty(LoginVM.LoggedInAs)) {
        _loggedIn = true; 
        CentralVM = LoggedInVM;
    }
}

-最後に、このメソッド呼び出しを のセッターに追加しますLoginVM。つまり、

public LoginViewModel LoginVM {
    get { return _logInVM; }
    private set { _logInVM = value; CheckForLoginChange(); }
}

しかし、これはうまくいきません。LoginVMボタンイベントがクリックされると変化するのですが、セッターが呼ばれていないからでしょうか?

この方向での助けに感謝します。「EventAggregators」または「Messenger」への流行語の参照だけでなく、詳細な回答をいただければ幸いです-可能な解決策と関係があることは知っていますが、理解できる適切なドキュメントが見つかりません...

4

2 に答える 2

4

実際、それはまさに Event Aggregator の仕事です。Caliburn.Micro に組み込まれています。

と の両方で、アグリゲーターを依存関係として使用する必要がありますMainViewModelLoginViewModel

private readonly IEventAggregator eventAggregator;
public MainViewModel(IEventAggregator eventAggregator)
{
    this.eventAggregator = eventAggregator;
    this.eventAggregator.Subscribe(this);
}

についても同様LoginViewModelです。ここで少し警告しますが、どちらもイベント アグリゲーターの同じインスタンスを受け取る必要があるため、イベントは正しく伝達されます (実際には、IoC コンテナーをIEventAggregatorシングルトンとして挿入するように設定するのが最善かもしれません)。

ここでMainViewModelを実装する必要IHandle<T>Tあります。 はメッセージとして機能するクラスです。

public class LogInSuccessful
{
    public readonly string LoggedInAs;
    public LogInSuccessful(string loggedInAs)
    {
        LoggedInAs = loggedInAs;
    }
}

それから

public class MainViewModel : ... , IHandle<LogInSuccessful>
{
    ....
    public void Handle(LogInSuccessful message)
    {
        //here you can change the VM and access message.LoggedInAs string. 
        //This method will be called when there's an appropriate event published
        //to the same event aggregator that the MainViewModel is subscribed to.
    }
}

イベントを発行するには、イベント アグリゲーターを内部で取得し、LoginViewModelある時点で次の呼び出しを行う必要があります。

eventAggregator.Publish(new LogInSuccessful("Admin"));

さらに編集

そうLoginViewModelすれば、資格情報の検証という 1 つのことだけが実行されます。それらが有効な場合、イベントを にパブリッシュします。MainViewModelこれにより、画面が管理され、適切なアクションが実行されます。LoginViewModelメインビューモデルの画面を「手動で」変更しないでください。それはその仕事ではありません。

于 2013-02-27T13:29:14.323 に答える
1

最後のコメントへの回答として、ブートストラップをカスタマイズできますが、IoC コンテナーを使用せず、そのレベルの抽象化に関心がない場合は、正直に言うと、静的クラスを使用してインスタンスを保持できます。アグリゲーター。

もちろん、あなたは実装に結び付けられていますが、小さなプロジェクトを実行しているだけで、DI/IoC の部分に興味がない場合は、それで十分です。

単純なクラスは

static class EventAggregatorProvider 
{ 
    private static EventAggregator _aggregator = new EventAggregator();

    public static EventAggregator Aggregator { get { return _aggregator; } }
}

次に、コードで静的クラスを介してアクセスします。

public void SomeMethod()
{
    // Do something
    EventAggregatorProvider.Aggregator.Publish(new SomeMessage());
}
于 2013-03-05T17:03:11.643 に答える