我已经使用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;
}
}
}
我想实现一些在注入服务旁注入其他参数的方法。这样的事情以相对简单的方式完成了吗?
答案 0 :(得分:0)
几乎在所有情况下,依赖注入的工作方式都是当您解析或获取一个实例类型时,该类型将在向您提供对象时使用带有最多参数的构造函数。
如果您针对接口(或只是类型)注册一个具体对象,然后稍后解析/获取一个在其ctor中使用这些东西之一的类,则DI将提供您注册的实例。
使用MVVMLight,您可以拥有SimpleIoc和SimpleIoc。Default等同于您正在考虑的静态服务。
simpleioc有一个问题。非常简单。
使用simpleioc,一旦获得给定类型的视图模型,便成为单例。您可以通过传递唯一键来强制使用其他实例,但是它们都已缓存。您可以使用参数获取实例,也许可以替换当前对象。我不确定随手可得。建议使用更复杂的DI容器。
除此之外。
由于您使用的是不同的窗口,因此要实例化一个窗口,并且将需要一个数据上下文,因此您需要以某种方式提供参数,这会带来一些麻烦。
首先可以使用viewmodel。 您会从DI或资源或静态中获取inavigationservice。
您有一个DoWindow(Object vm)方法。
想导航时,大概知道vm的参数。使用参数更新视图模型。新建一个用于所有视图的窗口。将其内容设置为您的视图模型。现在已将其模板化为Windows。除了使它们成为用户控件。使用Datatype =“ vmtype”将视图作为模板与viewmodel关联。将窗口的标题绑定到Content.Title,然后将Title属性添加到基本视图模型。
或者,使用单个窗口应用程序,您可以让contentcontrol填充将显示视图的区域。将该内容的内容绑定到currentviewmodel属性,然后可以在该窗口中使用viewmodel首先导航。