如何在PRISM 4中改善导航时传递对象的方法

3
我正在使用带有IoC的Prism。问题是如何通过导航传递一个对象(例如集合)。我看了这篇文章:如何在PRISM 4中导航到新视图时传递对象

以下是解决方案:

我提取了对象的哈希码并将其保存在一个Dictionary中,哈希码作为键,对象作为值。

然后,我将哈希码附加到UriQuery上。

之后,我只需要从目标视图中获取来自Uri的哈希码,并使用它从Dictionary请求原始对象。

一些示例代码:

参数存储库类:

public class Parameters
{
    private static Dictionary<int, object> paramList =
        new Dictionary<int, object>();

    public static void save(int hash, object value)
    {
        if (!paramList.ContainsKey(hash))
            paramList.Add(hash, value);
    }

    public static object request(int hash)
    {
        return ((KeyValuePair<int, object>)paramList.
                    Where(x => x.Key == hash).FirstOrDefault()).Value;
    }
}

调用方代码:

UriQuery q = null;
Customer customer = new Customer();
q = new UriQuery();
Parameters.save(customer.GetHashCode(), customer);
q.Add("hash", customer.GetHashCode().ToString());

Uri viewUri = new Uri("MyView" + q.ToString(), UriKind.Relative);
regionManager.RequestNavigate(region, viewUri);

目标视图代码:
public partial class MyView : UserControl, INavigationAware
{
// some hidden code

    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        int hash = int.Parse(navigationContext.Parameters["hash"]);
        Customer cust = (Customer)Parameters.request(hash);
    }
}

就是这样。

我不确定这种解决方案是否是传递对象的最佳方式。我猜这可能是一个服务。这样做是一个好方法吗?还是有更好的方法?

2个回答

6

我发表了一种更简单的方式。在此提供参考 -

我会使用OnNavigatedTo和OnNavigatedFrom方法,使用NavigationContext传递对象。

首先从INavigationAware接口派生视图模型 -

 public class MyViewModel : INavigationAware
 { ...

您可以实现OnNavigatedFrom方法,并按照以下方式设置要作为导航上下文传递的对象 -
void INavigationAware.OnNavigatedFrom(NavigationContext navigationContext)
{
     SharedData data = new SharedData();
     ...
     navigationContext.NavigationService.Region.Context = data;
}

当您想要接收数据时,在第二个视图模型中添加以下代码片段 -

void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
{
    if (navigationContext.NavigationService.Region.Context != null)
    {
        if (navigationContext.NavigationService.Region.Context is SharedData)
        {
             SharedData data = (SharedData)navigationContext.NavigationService.Region.Context;
              ...
        }
    }
}

1
我刚开始使用Prism,遇到了其中的一个限制。我通过另一种方式解决了它。我首先创建了一个从Uri继承并实现IDictionary(加上一些泛型方法以便更轻松地访问)的类。
public class NavigationUri : Uri, IDictionary<Type, object>
{
    private IDictionary<Type, object> _internalDictionary = new Dictionary<Type, object>();

    public NavigationUri(string uriString) : base(uriString, UriKind.Relative)
    {
    }
    public NavigationUri(string uriString, UriKind uriKind) :  base(uriString, uriKind)
    {
    }

    public void Add<T>(T value)
    {
        Add(typeof(T), value);
    }

    public void Add(Type key, object value)
    {
        _internalDictionary.Add(key, value);
    }

    public bool ContainsKey<T>()
    {
        return ContainsKey(typeof (T));
    }

    public bool ContainsKey(Type key)
    {
        return _internalDictionary.ContainsKey(key);
    }

    public ICollection<Type> Keys
    {
        get { return _internalDictionary.Keys; }
    }

    public bool Remove<T>()
    {
        return Remove(typeof (T));
    }

    public bool Remove(Type key)
    {
        return _internalDictionary.Remove(key);
    }

    public bool TryGetValue<T>(out object value)
    {
        return TryGetValue(typeof (T), out value);
    }

    public bool TryGetValue(Type key, out object value)
    {
        return _internalDictionary.TryGetValue(key, out value);
    }

    public ICollection<object> Values
    {
        get { return _internalDictionary.Values; }
    }

    public object this[Type key]
    {
        get { return _internalDictionary[key]; }
        set { _internalDictionary[key] = value; }
    }

    public void Add(KeyValuePair<Type, object> item)
    {
        _internalDictionary.Add(item);
    }

    public void Clear()
    {
        _internalDictionary.Clear();
    }

    public bool Contains(KeyValuePair<Type, object> item)
    {
        return _internalDictionary.Contains(item);
    }

    public void CopyTo(KeyValuePair<Type, object>[] array, int arrayIndex)
    {
        _internalDictionary.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return _internalDictionary.Count; }
    }

    public bool IsReadOnly
    {
        get { return _internalDictionary.IsReadOnly; }
    }

    public bool Remove(KeyValuePair<Type, object> item)
    {
        return _internalDictionary.Remove(item);
    }

    public IEnumerator<KeyValuePair<Type, object>> GetEnumerator()
    {
        return _internalDictionary.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _internalDictionary.GetEnumerator();
    }
}

然后我创建了一个从RegionNavigationContentLoader继承的类。在GetContractFromNavigationContext中,我存储传入的Uri,以便在CreateNewRegionItem方法中访问它。在该方法中,我检查Uri是否为NavigationUri,如果是,则循环添加所有的依赖注入覆盖。我使用Unity,但我认为这段代码可以轻松转换为另一个IOC容器。

public class BaseRegionNavigationContentLoader : RegionNavigationContentLoader
{
    private Uri _uri;
    private IServiceLocator _serviceLocator;
    private IUnityContainer _unityContainer;

    public BaseRegionNavigationContentLoader(IServiceLocator serviceLocator, IUnityContainer unityContainer) : base(serviceLocator)
    {
        _serviceLocator = serviceLocator;
        _unityContainer = unityContainer;
    }

    protected override string GetContractFromNavigationContext(NavigationContext navigationContext)
    {
        _uri = navigationContext.Uri;
        return base.GetContractFromNavigationContext(navigationContext);
    }

    protected override object CreateNewRegionItem(string candidateTargetContract)
    {
        object instance;
        try
        {
            var uri = _uri as NavigationUri;
            if (uri == null)
            {
                instance = _serviceLocator.GetInstance<object>(candidateTargetContract);
            }
            else
            {
                // Create injection overrides for all the types in the uri
                var depoverride = new DependencyOverrides();
                foreach (var supplant in uri)
                {
                    depoverride.Add(supplant.Key, supplant.Value);
                }

                instance = _unityContainer.Resolve<object>(candidateTargetContract, depoverride);
            }

        }
        catch (ActivationException exception)
        {
            throw new InvalidOperationException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "CannotCreateNavigationTarget", new object[] { candidateTargetContract }), exception);
        }
        return instance;
    }
}

现在在棱镜引导程序中,您需要在ConfigureServiceLocator方法中将BaseRegionNavigationContentLoader注册为IRegionNavigationContentLoader。 确保将其标记为TransientLifetimeManager,以便每次都会新建。 IRegionNavigationContentLoader的默认注册是容器控制的,这使其像单例一样运行,但我们每次都需要一个新的,因为我们需要在属性中将uri从一个方法传递到下一个方法。
现在我可以编写以下代码,并仍然使用构造函数注入。
var uri = new NavigationUri("MessageBoxView");
uri.Add(messageBoxEventArgs);
regionManager.RequestNavigate(RegionNames.MainRegion, uri);

这是一个超级解决方案,也许prism 4.2已经以同样的方式解决了这个问题,现在他们终于支持传递对象了。但我认为你只解决了90%的问题(-: 是否有一种方法可以避免每次都将NavigationContext.Uri强制转换为NavigationUri以检索参数。var values = ((NavigationUri)navigationContext.Uri).Values; - Martin Andersen
我猜我不完全理解你的问题。在我们的应用程序中,视图创建视图模型。因此,如果我有一个MsgBoxView,在它的构造函数中创建MsgBoxVM,并且MsgBoxVM构造函数接受另一个类,让我们称之为MsgBoxOptions。我会创建一个var uri = new NavigationUri("MsgBoxView"); 如果我已经创建了MsgBoxOptions并将其存储在名为msgBoxOptions的变量中,则会执行uri.Add(msgBoxOptions)。然后,在视图模型的构造函数中,MsgBoxOptions参数将使用相同的对象填充。 - CharlesNRice
我们所做的改进之一是在保存Uri时,同时保存UriQuery,并将其自动添加到NavigationUri中。因此,如果我想要UriQuery,只需将其作为参数添加到视图模型的构造函数中即可访问它。我更喜欢这种方式,而不是在定义的导航事件中捕获它。这符合您的要求吗?如果是这样,我可以更新代码以更好地反映我们现在拥有的内容。 - CharlesNRice

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接