【问题标题】:Passing parameters to the ViewModel using a custom INavigationService Implementation?使用自定义 INavigationService 实现将参数传递给 ViewModel?
【发布时间】:2019-04-19 02:55:34
【问题描述】:

我已经使用 MVVM Light 构建了一个小型应用程序,并且我已经达到了需要在我的应用程序中的几个不同 ViewModel 之间传递参数的地步。我已经探索了几种不同的选择,但我真的不是它们的忠实粉丝。到目前为止,我遇到的最有希望的是在 ViewModel 之间传递消息,但这有点限制,因为应用程序有可能同时打开多个相同的 View,我需要将参数隔离到单个实例视图/视图模型。

我目前没有使用 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来提供参数吗?它们似乎是依赖关系。 Fwiw 我更喜欢单窗口应用程序。使用多窗口,用户很容易“丢失”一两个。
  • @Andy DI 是一个选项,但我不确定如何在不定义某种静态服务的情况下将参数动态注入构造函数。

标签: c# wpf mvvm dependency-injection mvvm-light


【解决方案1】:

依赖注入在几乎所有情况下的工作方式是当您解析或获取一个类型时,该类型将使用具有最多参数的构造函数来为您提供对象。

如果您针对接口(或只是一个类型)注册一个具体对象,然后稍后解析/获取一个在其 ctor 中使用其中一种事物的类,那么 DI 会提供您注册的该实例。

有了 MVVMLight,你就有了 SimpleIoc,SimpleIoc.Default 相当于你正在考虑的那个静态服务。

simpleioc 有一个问题。很简单。

使用 simpleioc,一旦您获得给定类型的视图模型,那么它就是一个单例。您可以通过传递唯一键来强制使用不同的实例,但它们都被缓存了。您可以使用参数获取实例,并且可能会替换当前对象。我不确定是不是随便。建议使用更复杂的 DI 容器。

除此之外。

由于您使用不同的窗口,这会产生一些复杂性,因为您希望实例化一个窗口,并且您需要以某种方式提供您的参数的数据上下文。

您可以首先使用视图模型。 您从 DI 或资源或静态中获取导航服务。

你有一个 DoWindow(Object vm) 方法。

当您想要导航时,您大概知道 vm 的参数。使用参数新建视图模型。新建一个用于所有视图的窗口。将其内容设置为您的视图模型。这被模板化为您现在拥有的窗口。除了你让他们成为用户控件。使用 Datatype="vmtype" 将视图作为模板与视图模型相关联。将窗口的标题绑定到 Content.Title,当然还可以将 Title 属性添加到基本视图模型。

或者使用单个窗口应用程序,您可以让内容控件填充您的视图将显示的区域。将其内容绑定到 currentviewmodel 属性,您可以在该窗口中使用 viewmodel 第一次导航。

【讨论】:

  • 当你说“使用 DataType=vmtype”时,你会把它放在哪里,它会做什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-08
  • 2011-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多