I need your help!

Topics: Bugs
Coordinator
Nov 11, 2011 at 1:50 AM

With the rapid release of new Xaml platforms, it's very difficult to keep up and to track down bugs related to nuances or inconsistencies in the various Xaml runtimes. I've been at it for a long time now and it hasn't gotten easier with time; it's gotten worse.

I'd like to get a release out with official support for WP7.5, but there are still several issues remaining. I've been swamped with work lately (in case you haven't noticed my below-average participation here) and I have very little time to address these issues. I would really love for members of the community to step up and take these tickets on. Many of you have more WP7.5 experience than I do and are quite familiar with Caliburn.Micro, so you can handle this. Here are the tickets:

164/179 WP7.5 Issue with OnDeactive

175 WP7.5 Dormant State

174 WP7.5 IsResurrecting property

165 WP7 Issue with OnViewReady and child screens

Just post here which one you want to do. There's more information in the issue tracker. Make sure you are working against the latest code when you fix this. After you get the issue resolved, send me a pull request.

Thanks!

-PS: The sooner we can get WP7.5 under wraps, the sooner I'll start doing more with Metro.

Coordinator
Nov 14, 2011 at 12:53 PM

Anyone interested?

Nov 14, 2011 at 3:40 PM

I'll can look at 164/179 & 165.  I don't know that I can fix but I can try and reproduce and create sample solutions for them.

Coordinator
Nov 14, 2011 at 6:53 PM

That's a start and much appreciated. If you create samples that reproduce the bug, attach them to the ticket and commend with a list of instructions on how to reproduce it using your sample.

Nov 17, 2011 at 12:38 PM

I can take a look at the remaining issues, 174 and 175.

Coordinator
Nov 17, 2011 at 1:44 PM

Thank you.

Nov 17, 2011 at 5:58 PM

For 164/179 Tombstoning when the application is removed from memory seems to be fine (if that's the correct terminology).  There is a problem when an app is Tomstoned and not removed from memory (Deactivated)?  When the app is Continued OnNavigating fires and at the end of that method Deactivate is called so for anything implementing IDeactivate, OnDeactivate is called when the app is being continued.  When an app is resuming from being Tombstoned and it was removed from memory fe is null so Deactivate isn't called.

protected virtual void OnNavigating(object sender, NavigatingCancelEventArgs e) {
			externalNavigatingHandler(sender, e);   
			if (e.Cancel) return;

            var fe = frame.Content as FrameworkElement;
            if(fe == null)
                return;

            var guard = fe.DataContext as IGuardClose;
            if(guard != null && !e.Uri.IsAbsoluteUri) {
                bool shouldCancel = false;
                guard.CanClose(result => {
                    shouldCancel = !result;
                });

                if(shouldCancel) {
                    e.Cancel = true;
                    return;
                }
            }

            var deactivator = fe.DataContext as IDeactivate;
            if(deactivator != null)
                deactivator.Deactivate(false);
        }

Coordinator
Nov 18, 2011 at 2:30 PM

Fixes for 174/175 have now been merged in.

Coordinator
Nov 18, 2011 at 2:33 PM

@dbeattie

So do we just need to check if we are continuing, and if so, not call Deactivate?

Nov 19, 2011 at 4:51 PM

Yeah, it seems that would fix it.  I wasn't sure if you'd make that change in IPhoneService.cs, INavigationService.cs, or possibly somewhere else.

 

 public PhoneApplicationServiceAdapter(Frame rootFrame) {
            service = PhoneApplicationService.Current;
            service.Activated += (sender, args) => {
                if(!args.IsApplicationInstancePreserved) {
                    IsResurrecting = true;
                    Resurrecting();
                    NavigatedEventHandler onNavigated = null;
                    onNavigated = (s2, e2) => {
                        IsResurrecting = false;
                        Resurrected();
                        rootFrame.Navigated -= onNavigated;
                    };
                    rootFrame.Navigated += onNavigated;
                }
                else {
                    Continuing();
                    NavigatedEventHandler onNavigated = null;
                    onNavigated = (s2, e2) => {
                        Continued();
                        rootFrame.Navigated -= onNavigated;
                    };
                    rootFrame.Navigated += onNavigated;
                }
            };
        }

Coordinator
Nov 29, 2011 at 7:15 PM

I'm still looking for someone to help with the following tickets:

 

164/179 WP7.5 Issue with OnDeactive

165 WP7 Issue with OnViewReady and child screens

181 PhotoTask cannot be handled in 1.3 code base

 

Any takers?

Dec 9, 2011 at 4:48 AM

I verified that OnDeactivate is still called when Continuing.  "So do we just need to check if we are continuing, and if so, not call Deactivate?"  That's what needs to happen, I'm not sure how to change that but I'm looking at it.

Jan 1, 2012 at 5:02 PM

I worked on 164/179 and also 191.

I'm done with the fixes, I hope :), and sent a pull request.

Coordinator
Jan 1, 2012 at 7:50 PM

I got it in. Thanks!

Jan 2, 2012 at 4:50 AM
Edited Jan 2, 2012 at 4:54 AM

Curious how you fixed 164/179/191, I wasn't able to figure it out.

edit: nm, I see.

 

Jan 2, 2012 at 6:26 PM

They are both pretty simple fixes.

1. For "OnDeactivate" issue, my first choice was to check the value of PhoneApplicationServiceAdapter.IsResurrecting, but for the lack of simple way to do it I went towards comparing the URI's.

2. For navigation issue, I assumed (and it's also how Caliburn.Micro works, AFAIK) that the Views are under the same base namespace as the ViewModels, if not even the same namespace. Based on this I'm just trying to find out what the base namespace is.

Hope this makes sense :)

Jan 3, 2012 at 11:58 PM
TheBlueSky wrote:

2. For navigation issue, I assumed (and it's also how Caliburn.Micro works, AFAIK) that the Views are under the same base namespace as the ViewModels, if not even the same namespace. Based on this I'm just trying to find out what the base namespace is.

Hope this makes sense :)


You can't make this assumption. The framework allows you to explicitly map any two arbitrary namespaces to locate a View from a ViewModel.

The original code stripped out the base namespace of the View using the file name of containing assembly. Even this is a big assumption since there's no rule that forces every type contained in an assembly to belong to a single base namespace let alone one that's named after the assembly file name itself. Regardless, there should be no need to use the namespace of the ViewModel to determine the location of the View's XAML file.

Another assumption is that there's a one-to-one correspondence between subfolder and subnamespace. If you don't structure your project's subfolders and subnamespaces accordingly, the whole thing blows up even if you can reliably determine a single base namespace for the assembly.

Coordinator
Jan 4, 2012 at 1:31 AM

Any thoughts on how to improve the UriBuilder?

Jan 4, 2012 at 4:35 AM
Edited Jan 4, 2012 at 4:47 AM

@cb55555, you're right about the namespace mapping for Views and ViewModels, however, I'd like to explain in more details my assumption.

First of all, we both agree that the assembly file name does not represent the base namespace, for obvious reasons; at least it isn't part of the framework conventions. Also, we both agree that the Views and ViewModels can be in any namespace as long as the framework can locate them, e.g. using custom conventions.

Having said that, there is one more thing that I'm not sure we agree on, but it's based on my understanding on the framework (and looks like this is incorrect). This is, the physical location of any View or ViewModel is mapped to its namespace as: base namespace => root folder (or /), any consecutive namespace => folder with the same name and class name => file name without extension.

The assumption that works with most of the cases (for no other reason that what people are used to do in their project - at least from what I see) but not all, unfortunately, which is that Views and ViewModels share the same base namespace even if they are in different projects... hence, the proposed fix. In other words, the reason I'm comparing the full names of the View and the ViewModel is to find what the base namespace is.

I skimmed through the code trying to figure out some other way to do it, but couldn't find any. There doesn't seem to be a way to know what your file's physical location is.

Off the top of my head, I'll suggest introducing new convention for navigation purposes which can either be based on the assembly name or based on the assumption that Views and ViewModel share the same namespace, and of course, like everything, it's overridable - if that even a word :)

Interesting discussion; I'd like to know what you think.

Jan 4, 2012 at 5:16 AM

I think the most bullet-proof way to handle this without requiring conventions and making assumptions about those conventions is to create a dictionary that allows doing a "reverse lookup" of XAML files from their codebehind class names. Building the dictionary, unfortunately, will involve actually loading the XAML files and reading the contents.

If we want to take advantage of some design-time facilities, we could use a T4 template that reads all the XAML files in an assembly and writes out a class definition with code that adds the dictionary items as literals.

A runtime, for each assembly added to the AssemblySource.Instance collection the bootstrapper can locate within the assembly this class (by a known base type) and call the method that returns the "class name-to-XAML file" mapping list.

If we want to have everything done at run-time, then we could use the XamlReader class in the System.Windows.Markup namespace, which happens to be available in all the relevant frameworks. The main problem is how to enumerate the the XAML files within the assembly at runtime. I know this can be done with the ResourceReader class, but it doesn't appear to be available in the Windows Phone Silverlight framework.

Jan 4, 2012 at 6:09 PM

I still prefer some kind of convention to keep up with CM spirit :)

Loading and reading XAML file to build that dictionary may not seem bad especially if we're talking about an app with couple or few XAML pages, however, it doesn't seem too practical for apps with lots of XAML files, IMO, although that happens only once, when the app starts (or wakes up). Having said that, I don't have numbers to really tell how that may affect the performance of such apps.

I don't know much about T4 templates, but it's tempting to have them generate the required dictionary for me; if I can do the heavy lifting at design time then why not... my machine is much faster that any mobile ;)

Jan 4, 2012 at 10:03 PM
Edited Jan 4, 2012 at 10:06 PM

I created a T4 template that generated this code:

namespace TestPhoneApp
{
    using System;
    using System.Collections.Generic;
    using Caliburn.Micro;
    
    public partial class TestCatalog : XamlCatalog {
        public override List<KeyValuePair<Type, string>> GetTypeToFileMappingList() {
            var kvplist = new List<KeyValuePair<Type, string>>();
            kvplist.Add(new KeyValuePair<Type, string>(typeof(TestPhoneApp.Views.Page1), "Views/Page1.xaml"));
            kvplist.Add(new KeyValuePair<Type, string>(typeof(TestPhoneApp.App), "App.xaml"));
            kvplist.Add(new KeyValuePair<Type, string>(typeof(TestPhoneApp.MainPage), "MainPage.xaml"));
            return kvplist;
        }
    }
}

When the bootstrapper adds an assembly to the AssemblySource, we can look for a type whose base type is XamlCatalog, instantiate it, and then call the GetTypeToFileMappingList() method to add the list to a local cache. Since the paths for the XAML files are relative to the assembly, the bootstrapper will have to tack on the assembly name and handle the formatting prior to adding the items to the cache.

I was originally thinking that the cache could be a Dictionary, but there doesn't have to be a one-to-one correspondence between a type and a XAML file. Multiple XAML files could share the same codebehind class. So the reverse look-up based on type could yield multiple XAML files. Since Dictionaries require unique keys, we'll have to use a standard collection like List<T>. The calling routine will have to take into account the possibility of multiple XAML files being returned for a given type.

As for the generated project-specific subclass of XamlCatalog, we'll have to somehow make this a singleton or have the bootstrapper just take the first occurrence when it queries the assembly for a XamlCatalog.

So what do you think?

Jan 5, 2012 at 12:34 AM

This is the path on was headed on to try and fix the OnDeactivate problem, when I started testing it had problems, then I had to do some other stuff and didn't revist it.  Try and check if resuming on not fire OnNavigating.

 

public FrameAdapter(Frame frame, bool treatViewAsLoaded = false)
        {
            this.frame = frame;
            this.treatViewAsLoaded = treatViewAsLoaded;

            service = PhoneApplicationService.Current;
            service.Activated += (sender, args) =>
            {
                if (!args.IsApplicationInstancePreserved)
                {
                    this.frame.Navigating += OnNavigating;
                    this.frame.Navigated += OnNavigated;
                }
                else
                    this.frame.Navigated += OnNavigated;
            };            
        }

Coordinator
Jan 5, 2012 at 2:12 AM

For now, we are going to avoid this. It's too much. I'll revert to the previous implementation and do some simple design work so that it's easy for devs with similar needs as TheBlueSky to easily customize it. Once v1.3 is out of the door, we can revisit and see if there is a better way to handle this type of thing.

Jan 5, 2012 at 3:04 AM
Edited Jan 5, 2012 at 5:38 AM

I pushed this to my fork. Take a look how it works, and let me know what you think.

Some notes:

I know that you didn't like the idea of a separate portable class library, but I created one so that if you wanted to keep your Views in a separate assembly, there wouldn't have to be a dependency on the full Caliburn Micro framework just to add the one template-generated class based on the IXamlCatalogLoader interface.

I also created a new folder to hold the T4 template.

I added the T4 template to the HelloWindowManagerWP71 project as well as the GameLibrary (regular Silverlight app). Both work fine (i.e. no crashes) with these framework changes.

If you want, I can make the catalog loading be optional using a constructor argument defaulted to false.

I can also move the IXamlCatalogLoader into the main Caliburn.Micro project to eliminate the Caliburn.Micro.Common project altogether.

Jan 5, 2012 at 5:06 PM

I found out that because the code generation mechanism for XAML files in VS kind of dumb to the concept of shared codebehinds it will try to add the same members to the same class in two different partial class definitions resulting in a compiler error. Therefore, there has to be a one-to-one mapping between XAML and the underlying class and a Dictionary can be used.

Jan 14, 2012 at 6:01 AM

I finally had time to explore the changes cb55555 did for T4 code generation and I'm kinda have mix feelings about it. In general, I like the idea of using some sort of code generator to generate the required code (T4 in this case), however, there are few points here that thoroughly examined or the solution will turn to be more complex than it should be:

  • Having an another dependency. Whether we are talking about a dependency to Caliburn.Micro where I don't need it (e.g. in an assembly which has nothing but my views) or a dependency to Caliburn.Micro.Common which is one more library to worry about in my project (Caliburn.Micro, Caliburn.Micro.Common, Caliburn.Micro.Extension ... etc.) and we still didn't talk about worrying about the T4 template. Yes, I know NuGet make this trivial, but still.
  • Related to the above, this process might be a bit overwhelming for beginners. There is a lot of magic in Caliburn.Micro itself and adding new magical stuff will complicate things further. Not talking also about the manual work, such as right-click the T4 template and select Run Custom Tool every time you add a XAML file to my project, which means one more step can go wrong/skipped... we are human and do mistakes :)
  • The current implementation (for the T4 and the runtime support) looks solid; from what I see it was well thought of to cover different scenarios; however, I still prefer if we can just have a fully static calss with the mapping dictionary, and this class exists only in one place; e.g. my application project. As a reminder, I'm not a T4 expert :) but maybe if we can "watch" all the projects in our solution for XAML files and create the dictionary with the assembly information and anything else needed to map the view to the correct XAML. This will eliminate the need of the runtime stuff, as in runtime you just call a dictionary like you do normally.

Anyway, like EisenbergEffect said, this will not be implemented in the next release (v1.3) so for this I'll suggest to have an overloaded UriFor method that accepts a relative path to my XAML files like the way Navigate() works in WP SDK. Why not just use Navigate() then? Because UrlFor("/Path/to/My.xaml").WithParam(p => p.Id = 9).Navigate() is much more cooler :)