在这篇文章中查看 Rachel 的好答案。我也喜欢将我的视图和视图模型分开,因为这样我就知道我什么时候搞砸了 MVVM 基本规则。
您的 ViewModel 不应该有任何对 View 的引用,但 View 必须有对 ViewModel 的引用。例如,考虑我的自定义 SplashScreen 工厂(两个重要的行是“ var viewModel... ”和“ var splashScreen... ”):
namespace MyCompany.Factories
{
using System.Threading;
using MyCompany.Views;
using MyCompany.ViewModels;
public static class SplashScreenFactory
{
public static SplashScreenViewModel CreateSplashScreen(
string header, string title, string initialLoadingMessage, int minimumMessageDuration)
{
var viewModel = new SplashScreenViewModel(initialLoadingMessage, minimumMessageDuration)
{
Header = header,
Title = title
};
Thread thread = new Thread(() =>
{
var splashScreen = new SplashScreenView(viewModel);
splashScreen.Topmost = true;
splashScreen.Show();
splashScreen.Closed += (x, y) => splashScreen.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
return viewModel;
}
}
}
Factory 项目引用了MyCompany.ViewModels和MyCompany.Views。Views 项目只有对MyCompany.ViewModels的引用。
我们首先从调用方启动 ViewModel(在这种情况下,从 SplashScreenFactory 启动;或者如果您愿意,也可以从 App.xaml.cs 启动;出于本讨论之外的原因,我更喜欢使用我自己的 Bootstrapper 类)。
然后我们通过将 ViewModel 引用传递给 View 的构造函数来启动 View。这称为依赖注入。您可能还需要为 Window 类编写一个 Behavior,以便从您的 ViewModel 中关闭窗口。所以现在我可以从我的 Bootstrapper 类中执行以下操作:
/// <summary>
/// Called from this project's App.xaml.cs file, or from the Deals Main Menu
/// </summary>
public class Bootstrapper
{
private SplashScreenViewModel _splashScreenVM;
public Bootstrapper()
{
// Display SplashScreen
_splashScreenVM = SplashScreenFactory.CreateSplashScreen(
"MyCompany Deals", "Planning Grid", "Creating Repositories...", 200);
// Overwrite GlobalInfo parameters and prepare an AuditLog to catch and log errors
ApplicationFactory.AuditedDisplay(
Assembly.GetExecutingAssembly().GetName(),
() =>
{
// Show overwritten version numbers from GlobalInfo on SplashScreen
_splashScreenVM.VersionString = string.Format("v{0}.{1}.{2}",
GlobalInfo.Version_Major, GlobalInfo.Version_Minor, GlobalInfo.Version_Build);
// Initiate ViewModel with new repositories
var viewModel = new PlanningGridViewModel(new MyCompany.Repositories.PlanningGridHeadersRepository(),
new MyCompany.Repositories.PlanningGridLinesRepository(),
_splashScreenVM);
// Initiate View with ViewModel as the DataContext
var view = new PlanningGridView(viewModel);
// Subscribe to View's Activated event
view.Activated += new EventHandler(Window_Activated);
// Display View
view.ShowDialog();
});
}
/// <summary>
/// Closes SplashScreen when the Window's Activated event is raised.
/// </summary>
/// <param name="sender">The Window that has activated.</param>
/// <param name="e">Arguments for the Activated event.</param>
private void Window_Activated(object sender, EventArgs e)
{
_splashScreenVM.ClosingView = true;
}
请注意,我可以通过订阅 View 的 Activated 事件来控制 SplashScreen 的 View,然后使用“ClosingView”布尔 INotifyProperty 关闭视图,该 INotifyProperty 依次设置 View 的“Close”DependencyProperty(听起来很复杂,但一旦你了解了 DependencyProperties ,它变得简单)。
关键是,不要试图从 RootFrame 驱动您的导航。只需 view.Show() RootFrame,然后从 RootFrameViewModel 控制其余部分。
希望这可以帮助 :-)