在Windows Phone中屏幕之间是否有类型安全的导航方式?

时间:2013-11-15 15:05:21

标签: c# windows-phone-8 navigation windows-phone

我正在寻找一种在我的应用中的屏幕之间导航的方法。基本上我到目前为止看到的是将字符串URI传递给NavigationService,并带有查询字符串参数。例如。

NavigationService.Navigate(new Uri("/MainPage.xaml?selectedItem=" +bookName.Id, UriKind.Relative));

我并不是真的热衷于此,但最终因为它需要魔法弦,而且它们可能会导致问题。

理想情况下,我只是创建一个我要导航到的类的实例,将参数作为参数传递给构造函数。这可能吗?如果是这样,怎么样?

5 个答案:

答案 0 :(得分:7)

虽然实际导航最终必须使用字符串,但您可以创建或使用类型安全的包装器。

即使您只将它用于类型安全导航,我建议您查看Caliburn Micro。这是一个代码段from a tutorial on using it in WP8

  

工具包附带的NavigationService支持视图模型优先方法:我们不是声明哪个是我们想要用户的页面的URL(这是标准方法),声明哪个是我们想要显示的ViewModel。 该服务将负责创建正确的网址并显示与视图模型相关联的视图

或者您可以查看Windows Phone MVC,它也有一些类型安全导航。您甚至可以将导航代码拉出来自行使用,因为它已在MS-PL下获得许可。

答案 1 :(得分:3)

基本上,不,不是内置的。遗憾的是,像IRepository实例这样的复杂参数超出了Silverlight中导航功能的能力。我通常使用某种形式的IoC容器来处理这些。更简单的POCO参数很容易序列化为字符串,但仍需要魔术字符串和手动查询字符串解析。

但是,您可以轻松地自己构建类型安全的东西。例如,这是我的方法。

对于参数数据,我有一个我称之为'Extras'的类,它使用Dictionary<string, object>GetBool(string)等方法包装GetInt32(string),并且具有静态工厂方法CreateFromUri(Uri);这对我的目的来说已经足够了。

我将此与类型安全导航结合使用。我非常喜欢MVVM模式,我的每个页面都有一个ViewModel,它几乎封装了所有逻辑。页面与ViewModel的一对一关系使后者成为理想的导航键。结合属性和反射,为我们提供了一个简单的解决方案:

public class NavigationTargetAttribute : Attribute
{
    private readonly Type target;

    public ViewModelBase Target
    {
        get { return target; }
    }

    public NavigationTargetAttribute(Type target)
    {
        this.target = target;
    }
}

使用正确的ViewModel类型将其中一个放在每个页面上。

[NavigationTarget(typeof(LoginViewModel))]
public class LoginPage : PhoneApplicationPage
{ ... }

然后,在单独的NavigationManager-esque类中,您可以执行以下操作:

GetType().Assembly
    .GetTypes()
    .Select(t => new { Type = t, Attr = t.GetCustomAttributes(false).FirstOrDefault(attr => attr is NavigationTargetAttribute) })
    .Where(t => t.Attr != null);

就这样,您的应用中包含了每种可导航类型的集合。例如,从那里开始,将它们放入字典中的工作并不多。如果您遵循约定放置页面的位置,则可以(例如)在类型和Uri之间轻松转换...例如,new Uri("/Pages/" + myPageType.Name + ".xaml", UriKind.Relative)。添加对查询参数的支持并不多。最后,你最终会得到一个方法,如下所示:

public void Navigate(Type target, Extras extras)
{
    Type pageType;
    if (navigationTargets.TryGetValue(target, out pageType))
    {
        var uri = CreateUri(pageType, extras);
        navigationService.NavigateTo(uri);
    }

    // error handling here
}

最后,在页面的OnNavigatedTo方法中,我执行以下操作:

var extras = Extras.CreateFromUri(e.Uri);
((ViewModelBase) DataContext).OnNavigatedTo(extras);

最后,这给出了强类型导航的外观。这是一个简单的方法;在我的脑海中,可以通过在导航属性中添加所需参数并在导航时验证它们来改善这一点。它也不支持更复杂的导航类型,其中nav参数的值将决定最终目的地。不过,这适合我90%的用例 - 也许它也适合你。

这里肯定省略了一些细节,比如如何获得NavigationService的实例 - 我可以在今晚晚些时候处理更完整的样本,但这应该足以开始了。

答案 2 :(得分:2)

您可以使用PhoneApplicationService.State

这是Dictionary<String,Object>

PhoneApplicationService.State通常用于逻辑删除以存储应用程序的当前状态。但是,它可以用来方便地在页面之间传递数据。

MSDN documentation

  

当用户导航到时,Windows Phone应用程序将被停用   另一个应用。当用户返回应用程序时,通过   使用“后退”按钮或完成启动器或选择器任务,   应用程序重新激活。应用程序可以存储瞬态   应用程序状态在状态字典中的处理程序中   停用事件。在Activated事件处理程序中,应用程序可以   使用状态字典中存储的值进行瞬态应用   状态。

基本上你要做的是

PhoneApplicationService.State.add(selectedName,yourobjectInstance);
NavigationService.Navigate((new Uri("/MainPage.xaml?selectedItem="+selectedName,UriKind.Relative));

然后在你导航过的方法中你可以检索它

YourObject yourObjectInstance;
var yourObj = PhoneApplicationService.State["yourObjectName"];
yourObjectInstance = yourObj is YourObject ? (yourObj as YourObject) : null;

Here is a more indepth look into how to use this feature

答案 3 :(得分:1)

WPF支持导航到已创建的对象,但WP8缺少Navigate重载。

如果您不想对XAML页面URI进行硬编码,您可以使用以下(有点脏)辅助函数来获取某些类的.xaml资源URI。

static Uri GetComponentUri<T>() where T : DependencyObject, new() {
    return BaseUriHelper.GetBaseUri(new T());
}

然后您可以修改该URL并导航到它:

var baseUri = GetComponentUri<SomePage>(); //Uri="pack://application:,,,/MyProject;component/MainWindow.xaml"
var pageUri = new UriBuilder(baseUri) { Query = "selectedItem=" + bookName.Id };
NavigationService.Navigate(pageUri);

答案 4 :(得分:1)

我们的解决方案运作得很好:
1. 不要在Page Uris中使用查询字符串,这完全是MVVM,其中视图应该只显示内容,但加载和选择项目的实际逻辑在ViewModel中。
2.使用const页面名称创建类,并且只要您想要导航,只需使用:

public static class P
{
    public const string ArticlePage = "/Pages/ArticlePage.xaml";
    public const string OnlineSectionPage = "/Pages/OnlineSectionPage.xaml";
    public const string GalleryPage = "/Pages/GalleryPage.xaml";
    ...
}

// in our viewModel
NavigationService.Navigate(P.ArticlePage);

// In navigation service
public void Navigate(string pagePath)
{
    if (EnsureMainFrame())
    {
        mainFrame.Navigate(new Uri(pagePath, UriKind.RelativeOrAbsolute));
    }
}