This morning I updated the source code for the Silverlight Pages module that I am building as part of the DotNetNuke Hackathon. Based on some feedback from Michael Washington, I did a little refactoring to separate out my XAML display markup into a separate view. This makes the code just a little bit cleaner and allows me to develop the view in Blend (of course that is a whole new thing to learn which will have to wait for later). For now I will continue to hack away in XAML, learning a bit more each day and shaking off some of the cobwebs from what I had learned 2 years ago.
Michael has a lot of good resources on Silverlight development for DotNetNuke on his site http://dnnsilverlight.adefwebserver.com/. For my initial refactoring though, I found a video on the MVVM pattern by Todd Miranda which was a little easier for me to understand. Using this video as a guide, I have re-organized the files as shown.
I have separated the logic for converting the incoming data into a separate view model class. This allows me to reformat my data as needed for rendering in the view. If you had run the code from my first source code release, you would have found that the tree did not show the parent/child relationships. The reason for this is simple – the data was not in a format that was easily displayable in a tree.
My earlier post on Converting Self-Referencing Collections into a Hierarchical Collection discusses a simple Linq extension method that neatly solves this problem. Whereas in my first iteration I was attempting to do much of this work in my mainpage codebehind and a couple of helper classes, I have now significantly reduced the amount of code and provided better separation of concerns.
The PagesView.Xaml now contains the XAML for rendering the tree of pages in our website. This markup used to exist inside the MainPage XAML. By moving it out into its own view I further isolate the markup from the application logic.
This refactoring resulted in trivial markup being left in the mainpage.
Using a little code-behind we are now able to cleanly bind our ViewModel with our view
namespace DotNetNuke.Silverlight.agPageAdmin
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
PagesViewModel pages = new PagesViewModel();
pages.LoadXml();
vwPages.DataContext = pages;
}
}
}
Notice the LoadXml() method call. This is where we get our actual data that we will bind to the DataContext in the next line. Because I was having difficulty with debugging my code in DotNetNuke, I decided it would be easier to do the debugging using a standard Silverlight application hosted on a simple HTML page. By loading my data from an XML file, I can break my RIA Services link and just use my silverlight app in a simple webpage. To make sure I was using representative data, I used Fiddler with the WCF Binary Inspector plugin to view the results returned from the RIA Services call. With a little massaging, I had a nice clean xml file with good representative data.
With this current refactoring, the MainPage class is now acting very much like the Presenter in the typical Model-View-Presenter class by mapping the ViewModel to the View. One thing to keep in mind is that I have not implemented a complete MVVM. My viewmodel and view are pretty tightly coupled to my mainpage. If I wanted to fully implement MVVM I would probably dig into Prism, but for now the architecture suits my purposes.
At this point, I now have an operational services layer, a ViewModel that formats my data into a form needed by the treeview, and a View that I can readily edit in Blend if I so desire. My page treeview is now rendering correctly ( I still have a minor bug which is preventing the visibility icon from displaying) and I am ready to begin working on re-ordering and re-parenting tree nodes. This is one of the biggest features that I want in this module.
You can see the current state of my code in changeset 70211 on the agPageAdmin project on CodePlex