我使用MVVM Light构建了一个小应用程序,我已经达到了一个点,我需要在我的应用程序中的几个不同的ViewModel之间传递参数。我已经探索了几种不同的选择,但我真的不是他们的忠实粉丝。到目前为止我遇到的最有希望的只是在ViewModel之间传递消息,但这有点限制,因为应用程序有可能同时打开多个相同的View,我需要将参数隔离到单个实例View / ViewModel。
我目前没有使用MVVM Light提供的内置INavigationService
,但我做了一个令人难以置信的相似(如果我能解决参数注入,我可能会切换)。
这是我导航服务的精简版:
public class NavigationService : INavigationService
{
/* this implementation will not allow us to have the same window open
more than once. However, for this application, that should be sufficient.
*/
public NavigationService()
{
_openPages = new Dictionary<string, Window>();
}
private readonly Dictionary<string, Window> _openPages;
public void ClosePage(string pageKey)
{
if (!_openPages.ContainsKey(pageKey)) return;
var window = _openPages[pageKey];
window.Close();
_openPages.Remove(pageKey);
}
public IEnumerable<string> OpenPages => _openPages.Keys;
public void NavigateTo(string pageKey)
{
if (!AllPages.ContainsKey(pageKey))
throw new InvalidPageException(pageKey);
// Don't re-open a window that's already open
if (_openPages.ContainsKey(pageKey))
{
_openPages[pageKey].Activate();
return;
}
var page = (Window) Activator.CreateInstance(AllPages[pageKey]);
page.Show();
page.Closed += OnWindowClosedHandler;
_openPages.Add(pageKey, page);
}
// Probably a better way to remove this.
private void OnWindowClosedHandler(object sender, EventArgs args)
{
foreach (var item in _openPages.Where(kvp => kvp.Value == sender).ToList())
{
_openPages.Remove(item.Key);
}
}
// Reflection might work for this.
// Might also consider making this more dynamic so it isn't hard-coded into my service
private readonly Dictionary<string, Type> AllPages = new Dictionary<string, Type>
{
["AddPatientView"] = typeof(AddPatientView),
["CheckInView"] = typeof(CheckInView),
["MainView"] = typeof(MainWindow),
["PatientLookupView"] = typeof(PatientLookupView),
["PatientDetailsView"] = typeof(PatientDetailsView)
};
}
我的大多数ViewModel使用依赖注入来连接其他注入的服务,如下所示:
public class CheckInViewModel : ViewModelBase
{
public CheckInViewModel(ILicenseValidationService licenseValidationService,
IPatientFetchService patientFetchService,
IPatientCheckInService patientCheckInService)
{
if (IsInDesignMode)
{
Title = "Find Member (Design)";
}
else
{
Title = "Find Member";
CanFetch = true;
FindMemberCommand = new RelayCommand(async () => await FindMemberHandler(), () => CanFetch);
CheckInPatientCommand = new RelayCommand<Window>(async (window) => await CheckInPatientHandler(window),
(window) => Patient?.PatientId != null);
_licenseValidationService = licenseValidationService;
_patientFetchService = patientFetchService;
_patientCheckInService = patientCheckInService;
}
}
}
我想实现一些注入其他参数的方法以及我注入的服务。有这样的事情是以相对简单的方式完成的吗?
依赖注入几乎在所有情况下的工作方式是当您解析或获取一个类型时,然后将使用具有最多参数的构造函数为您提供对象。
如果你针对一个接口(或者只是一个类型)注册一个具体对象,那么稍后解析/获取一个使用其中一个东西的类,然后DI提供你注册的实例。
使用MVVMLight,您可以使用SimpleIoc和SimpleIoc.Default等同于您正在考虑的静态服务。
有一个简单的捕获。这很简单。
使用simpleioc,一旦你获得了一个给定类型的视图模型,那么这就是一个单例。您可以通过传递唯一键来强制执行其他实例,但它们都已缓存。您可以使用参数获取实例,也可以替换当前对象。我不确定。可能需要更复杂的DI容器。
除此之外。
由于您使用的是不同的窗口,因此您需要实例化一个窗口并且需要以某种方式提供参数的datacontext,这会产生一些复杂性。
你可以使用的是viewmodel。你得到DI或资源或静态的inavigationservice。
您有一个DoWindow(Object vm)方法。
当你想要导航时,你可能知道vm的参数。使用参数新建viewmodel。新建一个用于所有视图的窗口。将其内容设置为您的viewmodel。这就像现在的窗户一样。除了你让他们用户控制。使用Datatype =“vmtype”将视图作为模板与viewmodel关联。将窗口的标题绑定到Content.Title,当然还要将Title属性添加到基本视图模型。
或者,对于单个窗口应用程序,您可以使用contentcontrol填充区域,或者将显示视图。将其内容绑定到currentviewmodel属性,您可以在该窗口中使用viewmodel第一个导航。