1

私は古いwinformsプログラムをMVVMにCaliburn Micro、DIにNinjectを利用するWPFにアップグレード中です。

ソリューションを、Caliburn Micro ブートストラップの非汎用実装を実行する WPF ソリューションに更新しました。現在、実行するベースの ShellViewModel がないため、これを行います。

私は Ninject にかなり精通しているので、標準の Caliburn Micro ブートストラップではなく、Ninject フレーバーのブートストラップを使用しています。

ブートストラップは次のようになります。

public class NinjectBootstrapper : Bootstrapper
{
    private const string EXTENSION_PATH = "Plugins";
    public static IKernel Kernel;

    protected override void Configure()
    {
        Kernel = new StandardKernel();

        Kernel.Bind<IEventAggregator>()
              .To<EventAggregator>()
              .InSingletonScope();

        Kernel.Bind<ViewModels.IAnalysisOutputTableViewModel>()
              .To<ViewModels.AnalysisOutputTableViewModel>()
              .InSingletonScope();

        Kernel.Bind<IWindowManager>()
            .To<WindowManager>()
            .InSingletonScope();


        Kernel.Bind<FormMain>()
              .ToSelf();
    }

    protected override object GetInstance(Type serviceType, string key)
    {
        if (serviceType != null)
        {
            return Kernel.Get(serviceType);
        }

        throw new ArgumentNullException("serviceType");
    }

    protected override IEnumerable<object> GetAllInstances(Type service)
    {
        return Kernel.GetAll(service);
    }

    protected override void BuildUp(object instance)
    {
        Kernel.Inject(instance);
    }
}

メイン フォームを起動するために、App.xaml.cs で OnStartup を次のようにオーバーライドする必要がありました。

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        NinjectBootstrapper.Kernel.Get<FormMain>().Show();
    }
}

上記で明らかなように、メインの Ninject カーネルを public static として公開してアクセスできるようにしましたが、これはおそらくアンチパターンであることを認識しています。プログラムの変換が完了したら、これを解決します。

適切な MVVM を使用して、メイン フォームの新しい子ウィンドウを実装しました。次のようなボタンからウィンドウを開く必要があります。

private void dataViewerButton_Click(object sender, EventArgs e)
{
    windowManager.ShowWindow(analysisOutputTableViewModel);
}

ここまでは順調ですね。

問題は、この子ウィンドウを閉じようとしたときです。ウィンドウを閉じるだけでなく、プログラム全体をシャットダウンします。

これは、この子ウィンドウが Caliburn が処理する唯一のウィンドウ (またはビュー) であるため、非アクティブ化されると、Caliburn がプログラムを閉じたいと判断したためだと思います。

この自動クローズ動作を停止する方法はありますか? おそらく、メインフォームを初期化する別の方法でしょうか?


アップデート

以下の Marwijn の回答を使用して、アプリケーションを適切に閉じることができました。

最後にApplication.Exit();、メイン フォームの Closing イベントで使用しました。

private void Form_Main_FormClosing(object sender, FormClosingEventArgs e)
{
    Application.Exit();
}

私のApp.xaml.csへの次の変更と組み合わせて:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var mainForm = NinjectBootstrapper.Kernel.Get<FormMain>();
        mainForm.Show();

        mainForm.FormClosed += mainForm_FormClosed;
    }

    void mainForm_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e)
    {
        this.Shutdown(0);
    }
}

Caliburn Micro と Ninject を使用してメイン フォームから WPF ウィンドウをロードできるようになりました。すべてが適切にシャットダウンされ、プロセスが実行されたままになることはありません。このソリューションは、メイン フォームを WPF に更新できるようになるまでうまくいくようです。

4

2 に答える 2

3

その理由はWindowManager、WPF がルート VM/ウィンドウを追跡しているものであり、起動コードがウィンドウをバイパスWindowManagerして直接呼び出すShowため、CM がそれを追跡することを知る方法がないためです。

CMコードを確認し、Bootstrapper<T>起動時に実装を確認すると、次のように呼び出されます。

protected void DisplayRootViewFor(Type viewModelType, IDictionary<string, object> settings = null) 
{
    var windowManager = IoC.Get<IWindowManager>();
    windowManager.ShowWindow(IoC.GetInstance(viewModelType, null), null, settings);
}

したがって、CM はすでにルート VM のインスタンスを解決し、ウィンドウ マネージャーを介して表示しているようです。

ブートストラップをジェネリックにしてジェネリック型として使用できない理由はありますFormMainか(または、これは単なるビューですか?その場合、その複雑なビューモデルを作成していますか??)

public class NinjectBootstrapper : Bootstrapper<FormMain>

またはそうでない場合

public class NinjectBootstrapper : Bootstrapper<FormMainViewModel>

このコード:

NinjectBootstrapper.Kernel.Get<FormMain>().Show();

と同等です

IoC.GetInstance(viewModelType, ... etc)

IoC.GetInstanceブートストラップを呼び出すだけで、GetInstanceブートストラップされたオーバーライドがコンテナーから解決されるためです。

編集:アプリケーションクラスからninjectカーネルへの依存を削除したため、非静的にして、その厄介な静的コンテナの解決を取り除くことができるため、ここでボーナスポイント

WindowManagerまたは、現在のコードでウィンドウを開くために が使用されていることを確認することもできます。

var frmMain = NinjectBootstrapper.Kernel.Get<FormMain>();
windowManager.ShowWindow(frmMain);

(依存Application関係を取得する必要があるため、ここでの依存関係についてはわかりませんIWindowManager

しかし、それでも - 私は間違いなくあなたが現在持っているアプローチをとらないことを検討します.ジェネリックブートストラッパーの使用に問題があるかどうかについて頭を悩ませていますか? そのためのシェルは必要ありません。CM がビューを解決するために使用するルート ビューモデルだけです。(MainForm を使用してもうまくいかない場合は、それを処理するためのビューモデルを作成するのは簡単なはずです... MainFormViewModel を作成するだけです!接続する必要はありません。分離コードにすることはできますが、CM によってインスタンス化することができます。 ViewModel-First アプローチを使用する - その場合でも機能するはずです)

FormMain編集: にリファクタリングしない限り、デフォルトの規則ではビューが解決されないことにおそらく言及する必要がありますFormMainView。それか、デフォルトの規則を変更します

于 2013-04-11T09:57:30.613 に答える
2

使用できます:

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="MainWindow.xaml"
    ShutdownMode="OnExplicitShutdown"
   >
</Application>

そうすれば、明示的に shutdown を呼び出した場合にのみアプリケーションが停止します。

于 2013-04-12T07:34:07.993 に答える