Saturday, September 4, 2010

Show loading while downloading XAP modules in prism.

Show loading while downloading XAP modules in prism.

Scenario : To show progress bar when modules (xap’s) in prism are downloaded.

Problem : Currently in prism the default ModuleManager class does not expose a progress event that can be tracked to show some progressing of the modules (xap’s) being downloaded.

Solution : Prism’s ModuleManager internally uses instance of XapModuleTypeLoader to load modules (xap’s) asynchronously. Thanks to the community of prism for providing CreateDownloader() as virtual method.

Straight to the point I will jot down the steps to achieve the above mentioned goal.

Step 1 - Create an event and an event argument classes that will be used in custom class below

[sourcecode language="csharp"]

public class ModuleDownloadProgressEvent : CompositePresentationEvent<ModuleDownloadProgressArgs>
{
}

public class ModuleDownloadProgressArgs
{
public ModuleDownloadProgressArgs(int BytesReceived, bool IsComplete) { _BytesReceived = BytesReceived; _IsComplete = IsComplete; }
private int _BytesReceived;
public int BytesReceived { get { return _BytesReceived; } set { _BytesReceived = value; } }
private bool _IsComplete;
public bool IsComplete { get { return _IsComplete; } set { _IsComplete = value; } }
}

[/sourcecode]

Step 2 - Create a class that derives from IFileDownloader.

[sourcecode language="csharp"]

public class CustomFileDownloader : IFileDownloader
{
private readonly WebClient webClient = new WebClient();
private readonly IEventAggregator eventAgg;

public CustomFileDownloader()
{
eventAgg = ServiceLocator.Current.GetInstance<IEventAggregator>();
}

private event EventHandler<DownloadCompletedEventArgs> _downloadCompleted;
public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted
{
add
{
if (this._downloadCompleted == null)
{
this.webClient.OpenReadCompleted += this.WebClient_OpenReadCompleted;
this.webClient.DownloadProgressChanged += this.DownloadProgressChanged;
}

this._downloadCompleted += value;
}

remove
{
this._downloadCompleted -= value;
if (this._downloadCompleted == null)
{
this.webClient.OpenReadCompleted -= this.WebClient_OpenReadCompleted;
this.webClient.DownloadProgressChanged -= this.DownloadProgressChanged;
}
}
}

void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
RaiseDownloadProgressChanged(e.ProgressPercentage, false);
}

private void RaiseDownloadProgressChanged(int val, bool isComplete)
{
eventAgg.GetEvent<ModuleDownloadProgressEvent>().Publish(new ModuleDownloadProgressArgs(val, isComplete));
}

public void DownloadAsync(Uri uri, object userToken)
{
this.webClient.OpenReadAsync(uri, userToken);
}

private void WebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
this._downloadCompleted(this, ConvertArgs(e));
RaiseDownloadProgressChanged(0, true);
}

private static DownloadCompletedEventArgs ConvertArgs(OpenReadCompletedEventArgs args)
{
return new DownloadCompletedEventArgs(args.Error == null ? args.Result : null, args.Error, args.Cancelled, args.UserState);
}
}

[/sourcecode]

Step 3 - Create a class that derives from XapModuleTypeLoader and override its virtual method CreateDownloader returning your class object prepared in step 1

[sourcecode language="csharp"]

public class CustomXapModuleTypeLoader : XapModuleTypeLoader
{
protected override IFileDownloader CreateDownloader()
{
return new CustomFileDownloader();
}
}

[/sourcecode]

Step 4 - Create a class that derives from ModuleManager and override its virtual method ModuleTypeLoaders replacing default XapModuleTypeLoader with your class object prepared in step 2.

[sourcecode language="csharp"]

public class CustomModuleManager : ModuleManager
{
public CustomModuleManager(IModuleInitializer moduleInitializer, IModuleCatalog moduleCatalog, ILoggerFacade loggerFacade)
: base(moduleInitializer, moduleCatalog, loggerFacade) { }
private System.Collections.Generic.IEnumerable<IModuleTypeLoader> typeLoaders;
public override System.Collections.Generic.IEnumerable<IModuleTypeLoader> ModuleTypeLoaders
{
get
{
if (this.typeLoaders == null)
{
this.typeLoaders = new List<IModuleTypeLoader>()
{
new CustomXapModuleTypeLoader()
};
}

return this.typeLoaders;
}
set
{
this.typeLoaders = value;
}
}
}

[/sourcecode]

Step 5 - Register your custom ModuleManager class prepared in step 4, in overridden method ConfigureContainer() of your Bootstrapper class.

[sourcecode language="csharp"]

base.RegisterTypeIfMissing(typeof(IModuleManager), typeof(CustomModuleManager), true);

[/sourcecode]

Step 6 – Create a method in Shell that will respond to the ModuleDownloadProgressEvent event when published

[sourcecode language="csharp"]

public void ShowHide(ModuleDownloadProgressArgs e)
{
// Your Dream Goes Here
}

[/sourcecode]

Step 7 – Finally in the Shell subscribe to the ModuleDownloadProgressEvent event prepared in Step 1.

[sourcecode language="csharp"]

eventAggregator.GetEvent<ModuleDownloadProgressEvent>().Subscribe(ShowHide, ThreadOption.UIThread);

[/sourcecode]

That’s it!!!

Enjoy Prism.

2 comments:

Harry said...

Hi
First of I have to thank you for all the amazing information about XAP modules

Secondly is there anyway that you have these information for wordpress users in an easier format

Dhaval Upadhyaya said...

Thanks Harry,
Let me know the exact format you want this information.