4
Messenger.Default.Register<OpenWindowMessage>(this, message =>
{
    var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>();
    adventurerWindowVM.Adv = message.Argument;
    var adventurerWindow = new AdventurerView() 
    {
        DataContext = adventurerWindowVM
    };
    adventurerWindow.Show();
});

这段代码相当简单;它只是打开一个新窗口并设置新窗口的 DataContext。我遇到的问题是,如果我执行两次,第一个实例的内容将被覆盖并设置为第二个实例的内容,因为adventurerWindowVM它是两个窗口的 DataContext 并且每次调用此代码时都会被覆盖。我正在寻找一种方法来防止这种情况;我希望能够使用此消息打开多个窗口并让每个窗口都是唯一的,但到目前为止我还没有找到这样做的方法。任何建议将不胜感激。我为模糊的标题道歉;我不确定该如何命名这个问题。(另外,我知道这不是一种方法。这段代码会被调用什么?)

更新:我正在使用 MVVM Light,我的代码基于某人在此答案中为我提供的示例:https ://stackoverflow.com/a/16994523/1667020

这是我的 ViewModelLocator.cs 中的一些代码

public ViewModelLocator()
{
    _main = new MainViewModel();

    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    SimpleIoc.Default.Register<GameViewModel>();
    SimpleIoc.Default.Register<AdventurerViewModel>();
}
4

4 回答 4

10

给出了另一个答案后,我想我可以说这里使用的 IoC 容器只是SimpleIoC来自 MvvmLight 并且要在每次GetInstance(...)尝试解析实例时都获得一个新的 VM 实例,您需要做的就是每次传递一个唯一的密钥的虚拟机。

所以你可以切换

var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>();

var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>(System.Guid.NewGuid().ToString());

然而,正如 MVVMLight 的作者所提到的,这些 VM 将被缓存,我们需要在不再需要时将它们删除。在您的情况下,可能在Window关闭时。

因此,我将拥有整个 lambda,例如:

Messenger.Default.Register<OpenWindowMessage>(this, message =>
{
    var uniqueKey = System.Guid.NewGuid().ToString();
    var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>(uniqueKey);
    adventurerWindowVM.Adv = message.Argument;
    var adventurerWindow = new AdventurerView() 
    {
        DataContext = adventurerWindowVM
    };
    adventurerWindow.Closed += (sender, args) => SimpleIoc.Default.Unregister(uniqueKey);
    adventurerWindow.Show();
});

笔记:

虽然与仅使用 ( new AdventurerViewModel() )自己创建一个新 VM 相比,这要长 3 行,但我仍然喜欢这一点,因为如果您使用 IoC 容器来管理 VM 的 LifeTime,那么让它完全管理它们。在不需要的时候不太喜欢混合搭配。而是让 IoC 容器做它应该做的事情。

如果您需要对 VM 注入和生命周期管理进行更多控制,请查看更复杂的 Ioc 控制器,例如UnitySimpleIoC只是为了让你的脚在 IoC 类型的容器中“弄湿”,它在这方面做得非常好。

于 2013-06-08T07:41:35.843 回答
3

我认为您正在尝试将 ViewModel 的同一实例多个视图一起使用。所以视图显然会覆盖彼此的视图模型内容。

如果你这样做了怎么办?

        Messenger.Default.Register<OpenWindowMessage>(this, message =>
    {
        var adventurerWindowVM = new AdventurerViewModel();
        adventurerWindowVM.Adv = message.Argument;
        var adventurerWindow = new AdventurerView() 
        {
            DataContext = adventurerWindowVM
        };
        adventurerWindow.Show();
    });
于 2013-06-08T02:40:36.183 回答
2

这是一个方法调用,使用 lambda 表达式传入一个匿名方法。

看起来您是AdventurerViewModel从某种 IoC 容器中获取的。IoC 容器是如何配置的?特别是,它给你的对象的范围是什么?例如,如果您将 IoC 配置为在单例范围内创建对象,那么您每次都会返回对同一对象的引用。您可能需要在 IoC 容器中配置对象的范围,以便它每次都为您返回一个新副本。

你如何做到这一点将取决于你的 IoC 容器。在不知道您正在使用哪个 IoC 框架或查看其配置的情况下,不可能做出任何进一步的评论。

于 2013-06-08T02:38:54.897 回答
0

我的建议是为 SimpleIOC 创建一个扩展方法。像这样的东西:

public static T CreateInstance<T>(this SimpleIoc simpleIoc)
{
    // TODO implement
}

您已经知道获取相同实例的方法;使用创建新实例的方法扩展了 SimpleIoc:

T instance = SimpleIoc.Default.GetInstance<T>();
T createdInstance = SimpleIoc.Defalt.CreateInstance<T>();

如果您不熟悉扩展方法,请参阅扩展方法揭秘

实施:

  • 类型 T 的,获取构造函数。
  • 如果有多个构造函数:要么抛出异常,要么决定使用哪个构造函数。简单方法:使用与 中使用的方法相同的方法,并SimpleIoc.GetInstance带有一个属性。更详细的方法:尝试找出是否可以找到与构造函数之一匹配的注册元素。此处不作说明。
  • 找到所需的构造函数后,获取其参数。
  • 向 SimpleIoc 询问此参数的实例,或者如果它们也应该是新的,请询问 SimpleIoc 创建新实例。
  • 创建实例

.

public static T CreateInstance<T>(this SimpleIoc ioc)
{
    return (T)ioc.CreateInstance(typeof(T));
}

public static object CreateInstance(this SimpleIoc ioc, Type type)
{
    ConstructorInfo constructor = ioc.GetConstructor(type);
    IEnumerable<object> constructorParameterValues = ioc.GetParameters(constructor);
    constructor.Invoke(constructorParameterValues.ToArray());
}

要决定使用哪个构造函数:

private static ConstructorInfo GetConstructor(this SimpleIoc ioc, Type type)
{
    ConstructorInfo[] constructors = type.GetConstructors();
    ConstructorInfo constructorToUse;
    if (constructorInfo.Length > 1)
    {
        // Decide which constructor to use; not explained here
        // use Attribute like SimpleIoc.GetInstance?
        // other method: use SimpleIoc.IsRegistered to check which Parameters
        // are registered: use ConstructorInfo.GetParameters()
        constructorToUse = 
    }
    else
        constructorToUse = constructoInfo[0];
    return constructorToUse;
}

要在构造函数中获取参数的值,我们需要决定是要来自 Ioc 的现有值,还是创建新值:

public static IEnumerable<object> GetParameterValues(this simpleIoc ioc,
    ConstructorInfo constructor)
{
    IEnumerable<Type> parameterTypes = contructor.GetParameters()
        .Select(parameter => parameter.ParameterType);
    return ioc.GetInstances(parameterTypes);
}

public static IEnumerable<object> GetInstances(this SimpleIoc ioc,
    IEnumerable<Type> types)
{
    // TODO: decide if we want an existing instance from ioc,
    // or a new one

    // use existing instance:
    return types.Select(type => ioc.GetInstance(type));

    // or create a new instance:
    return types.Select(type => ioc.CreateInstance(type));
}

这似乎有很多代码,但其中大部分是注释,大多数方法都是一个衬里。

于 2021-02-01T10:51:07.417 回答