Archive for the ‘Caliburn Micro’ Category
Introduction to Caliburn Micro – Part 2
If you haven’t used Caliburn Micro please go through Caliburn Micro Introduction 1
Introduction
Most of the us know what is MVVM model but most of us hesitant to use it in their development. Reason they say is, implementing MVVM in a small project is an overkill. Might be true if they use some complicated MVVM frameworks. In my opinion using Caliburn Micro as a MVVM framework wont overkill or complicate your development rather It will ease our development. Caliburn Micro uses Convention over Configurations, so zero configuration. Last couple of months I did so many tracers and some large projects in Silverlight and WPF using Caliburn Micro, some tracers are just one page projects still I used Caliburn Micro because it’s very easy to develop compare to the normal code behind approach. Some of us are not using Caliburn Micro, the reason is they are doubtful whether it can used in big production level projects as it’s a micro. My answer is yes, I used Caliburn Micro in very complex applications that have more than thirty pages and very complicated functionalities. Don’t underestimate Caliburn Micro by its name, it’s very powerful and easy to learn. I am sure once you start using Caliburn Micro you will never go back to normal codebehind approach in Silverlight or WPF or WP7 neither a different MVVM framework.
iTraveller Development Days
In the development phase of iTraveller I come across lot of blockers related to MVVM model development using Caliburn Micro. In this post I am going to give a brief description about some of my blockers and how I overcome those.
Capturing PreviewDrop/KeyUp or any kind of event in Viewmodel
If you had a chance to look at iTraveller then you can see most of the functionality is based on drag and drop features. My first concern was how to capture drop event or Mouse Down Even or KeyUp Event using Caliburn Micro. What we should do to capture the event is as follows.
<ListBox Name="MyPhotoSets" VerticalAlignment="Stretch"
Visibility="{Binding IsListVisible}" AllowDrop="True" MinHeight="100" MinWidth="100" Style="{DynamicResource PhotoListStyle}" ItemTemplate="{DynamicResource SetTemplate}" ItemContainerStyle="{DynamicResource PhotoListItem}" > <i:Interaction.Triggers> <i:EventTrigger EventName="PreviewDrop"> <cal:ActionMessage MethodName="NewPhotosDropped"> <cal:Parameter Value="$eventArgs"></cal:Parameter> </cal:ActionMessage> </i:EventTrigger> <i:EventTrigger EventName="SelectionChanged"> <cal:ActionMessage MethodName="CategorySelectionChanged"> <cal:Parameter Value="{Binding ElementName=MyPhotoSets,
Path=SelectedItem}"></cal:Parameter> </cal:ActionMessage> </i:EventTrigger> <i:EventTrigger EventName="KeyUp"> <cal:ActionMessage MethodName="KeyUpHappened"> <cal:Parameter Value="$eventArgs"></cal:Parameter> </cal:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </ListBox>
You should add a xmlns reference to System.Windows.Interactivity and Caliburn Micro in your xaml page as shown below
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro" xmlns:i="clr-namespace:System.Windows.Interactivity;
assembly=System.Windows.Interactivity"
As you can see in the ListBox I added an event trigger for PreviewDrop and KeyUp. Obviously in you Viewmodel you need to get the DragEventArgs or KeyEventArgs to perform the desired action. We can instruct Caliburn Micro to send these eventargs to the viewmodel function by adding a parameter with value ‘$eventArgs’. Just examine the xaml code.
<i:EventTrigger EventName="PreviewDrop"> <cal:ActionMessage MethodName="NewPhotosDropped"> <cal:Parameter Value="$eventArgs"></cal:Parameter> </cal:ActionMessage> </i:EventTrigger>
In the above code will work like this. If a PreviewDrop event happened in Listbox then call NewPhotosDropped function in my Viewmodel with parameter DragEventArgs. Have a look at my Viewmodel’s NewPhotosDropped function
public void NewPhotosDropped(DragEventArgs e) { //perform your task with parameter e.
}
That’s it now you will get the DragEventArgs in your Viewmodel and do what ever you wanted to do with it.
How to get a View in ViewModel
Most of the case you don’t need a reference to the View. But if in any case you need the reference of your View in your Viewmodel, no issues. You can override OnViewLoaded function of Caliburn micro in Viewmodel and get your view as follows.
protected override void OnViewLoaded(object view) { base.OnViewLoaded(view); MyView myView = view as MyView; if (myView != null) _webBrowserControl = myView.WebBrowserControl; }
How to Bind a ViewModels to a ListBox/ItemsControls that has Custom Template
In some scenarios you may need to bind a list of Viewmodels to a Listbox. If there is no custom templates then no issues. But if the Listbox has some custom templates and the view should show in a contentcontrol placed in the template. Then we should add one dependency property to the content control. Let’s take an e.g
<ListBox x:Name="IndividualComments" BorderThickness="0"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Vertical" Background="Gray"/> </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.ItemTemplate> <DataTemplate> <Border BorderBrush="Black" BorderThickness="1" CornerRadius="0" Background="Transparent" Margin="1"> <ContentControl ca:View.Model="{Binding}" Margin="0" Background="Transparent"/> </Border> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
View model code
private ObservableCollection<IndividualCommentViewModel> _individualComments; public ObservableCollection<IndividualCommentViewModel> IndividualComments { get { return _individualComments; } set { _individualComments = value; } }
In the viewmodel I will have a list of IndividualCommentViewModel. The Listbox in xaml page has some custom template to add a border around the comments. The viewmodel should be binded to a ContentControl inside the border. I tried lot of ways to show my view in the content control but failed. After some search in Caliburn Micro code and googling I came across View.Model dependency property.
As you can see the contentcontrol
<ContentControl ca:View.Model=”{Binding}” Margin=”0″ Background=”Transparent”/>
If we set ca:View.Model=”{Binding}” then Caliburn Micro is intelligent enough to set the View to the content control. ‘ca’ is my xmlns name reference in the page, it can be any name.
Showing Views in Tabcontrols just like in Visual Studio
In iTraveller views like Preview, Search Result, Upload basket, etc are in tabbed view. In one of Rob’s post he explained in detail how we can achieve tabbed view. I just used the same approach he mentioned. I don’t think I need to explain it again.
How to Give a Title to Popup window
After the release, when I recheck iTraveller, I could see that the popup window is showing without the Title I had given in the xaml page. The title is coming with fully functional name of the popup’s viewmodel. So how to solve it. What we need to do is inherit the viewmodel from screen. See the below code to set the title
FlickrAuthorizeViewModel flickrAuthorizeViewModel = new FlickrAuthorizeViewModel(flickrService); flickrAuthorizeViewModel.DisplayName = "Flickr Authorization"; WindowManager winManager = new WindowManager(); winManager.ShowDialog(flickrAuthorizeViewModel, null);
Screen class has a property called Display Name, just set the title to Display Name property as shown above. We are done, the WindowManager will now show the popup with the Title I given in DisplayName property
Event Aggregator
Event Aggregator in Caliburn Micro is a powerful implementation of Observer/Subscriber pattern. The usage of Event aggregator is not limited to ViewModel communication. It can be used in any layer, if there is a need of Observer/Subscriber communication. In iTraveller the messaging (Event Aggregation) is a separate Layer. One main reason I separated it from UI layer is, I may need to raise messages from other layers other than UI. In iTraveller’s User Comment service uses Event Aggregator to publish the result once it’s processed all the comments fetched from Flickr or Facebook. I used Event Aggregator here because once the comments are saved the application does two things, 1: it’s start indexing those comments for searching, 2: a tab need to open to display the comments fetched from Flickr/Facebook. Rather than calling those functionalities the Comment service just publish the processed comments. Who ever listening those messages will get notified, here in iTraveller Indexing service and UserCommentUpdateViewModel is listening to UserComment message. They know what they should do when they got UserComment update notification.
As the day passes… more and more in love with Caliburn Micro . Thanks for reading. Any questions or comments pls update it in the comment section.
Populate Listbox Asynchronously using Await and Reactive Extension (Rx)
Recently I completed rewriting one of my WPF application called iTraveller, one important area of interest was User interface. I did lot of things to make the UI responsive even in, time consuming operations. My first hurdle was populating the thumbnail images in a list box without halting the UI. If we populate Listbox in normal approach then the UI will be unresponsive till it completes. I searched a lot to find a better approach to populate ListBox asynchronously without halting my UI. Most of the approaches were too complex or not suited for my requirement. So I combined Async CTP’s await and Reactive Extension (Rx) to satisfy my requirement. The result was far far better than I thought, with my approach I could populate Listbox asynchronously with a very responsive UI.
Let’s see how I solved the issue.
First have a look at my Photo entity
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Media.Imaging; using System.Threading.Tasks; using System.Threading; using System.IO; using Db4objects.Db4o; namespace ITraveller.Domain { public class Photo:IDisposable { public Photo() { this.ImageID = Guid.NewGuid(); } private Guid _imageID; public Guid ImageID { get { return _imageID; } set { _imageID = value; } } private String _imageName; public String ImageName { get { return _imageName; } set { _imageName = value; } } private string _title; public string Title { get { return _title; } set { _title = value; } } private string _description; public string Description { get { return _description; } set { _description = value; } } private Uri _uri; public Uri Uri { get { return _uri; } set { _uri = value; } } private string _imagePath; public string ImagePath { get { return _imagePath; } set { _imagePath = value; } } private LocalCategory _category; public LocalCategory Category { get { return _category; } set { _category = value; } } private int _commentCount; public int CommentCount { get { return _commentCount; } set { _commentCount = value; } } private BitmapImage _thumbnailImage; public BitmapImage ThumbnailImage { get { return _thumbnailImage; } set { _thumbnailImage = value; } } private BitmapImage _previewImage; public BitmapImage PreviewImage { get { return _previewImage; } set { _previewImage = value; } } public void EmptyImage() { this.PreviewImage = null; this.ThumbnailImage = null; } public delegate void LoadImage(); public Photo GetPhotoWithThumbnail() { this.ThumbnailImage = new BitmapImage(); this.ThumbnailImage.BeginInit();
this.ThumbnailImage.UriSource = new Uri(this.ImagePath);
this.ThumbnailImage.DecodePixelHeight = 50;
this.ThumbnailImage.EndInit();
return this; } public Photo GetPhotoWithPreviewImage() { try { this.PreviewImage = new BitmapImage(); this.PreviewImage.BeginInit(); this.PreviewImage.UriSource = new Uri(this.ImagePath); this.PreviewImage.EndInit(); return this; } catch (Exception ex) { this.PreviewImage = null; return this; } } public void Dispose() { this.PreviewImage = null; this.ThumbnailImage = null; } } }
It’s a normal Entity. GetPhotoWithThumbnail function creates the BitmapImage in the photo entity. Before adding to Listbox I make a call to this function to get the thumbnail image and bind it to ListBox.
I used db4o for persisting my data, I covered how I used db4o in one of my previous post. Let’s start the code walk through from Application layer. In my app layer I have a function to fetch all the photos belongs to a category.
public async Task<List<Photo>> GetPhotosByCategory(LocalCategory category) { return await TaskEx.Run(() => { IRepository repository = GenericRepository.GetRepositoryInstance(); return repository.Find<Photo>(p => p.Category.ID == category.ID).ToList<Photo>(); }, CancellationToken.None); }
I used await here to perform the data fetching task asynchronously.
Now let’s go to the Viewmodel and see how I am calling this function and populating the listbox. iTraveller implements MVVM model and as usual I used Caliburn Micro as my MVVM framework.
public async void LoadPhotosInCategory(LocalCategory category) { PhotoService photoService = new PhotoService();
//data retrieving call using await List<Photo> photos=await photoService.GetPhotosByCategory(category);
//loading of List using Reactive extension IObservable<Photo> photosToLoad = photos.ToObservable<Photo>(); photoToLoad.Subscribe<Photo>(p => { Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action<Photo>(AddThumbnailToCollection), p); }, () => {
//this block will execute once the iteration is over. } ); }
private void AddThumbnailToCollection(Photo photo) { photo = photo.GetPhotoWithThumbnail(); this.Thumbnails.Add(photo); }
public ObservableCollection<Photo> Thumbnails { get; private set; }
Dispatcher can be find in System.Windows.Threading namespace.
The observablecollection Thumbnails is binded to the Listbox in the View. That all happens behind the scene and done by Caliburn Micro.
I used Dispatcher here because the creation of BitmapImage should be in the same thread of the UI otherwise while binding it will throw error.
Try it out yourself and see how responsive your UI even if the Listbox is getting loaded with hundreds of thumbnails.
iTraveller–A photo uploader for Flickr and Facebook
Today I released my new version of iTraveller. I was working on this version for last 2 weeks, it’s a complete rewrite of old iTraveller, with several added usability features. Now iTraveller can upload photos to Facebook as well.
What is iTraveller
iTraveller is a desktop application to organize your photos and upload them to Flickr and Facebook. iTraveller ease the organizing of photos. We can mark which photos should go to Flickr or Facebook and add to appropriate set/album in offline. Once you decide to upload, click the Synchronize button leave the rest to iTraveller. While iTraveller synching with Flickr/Facebook, you go for a walk or grab a cup of coffee or you can continue creating your local category or adding more photos to Flickr Set/Facebook Album. Another useful feature of iTraveller is, you can see the photo comments posted by other users in offline mode.
I used Calburn micro to separate my View and View Model. Also I extensively used Async CTP to make a very responsive UI. I will give more details of my architecture in a different post.
Technologies used
Let’s see some screen shots of iTraveller
Home Screen
First step in using iTraveller is creating a Local set. You can create a local set by dragging and dropping your photos from Windows explorer to the Local Set box at the left top pane. When you drag and drop photos to Local Set pane, it will ask whether you need to create a new set or add to an existing category. You can see the photos inside a category by clicking on category and all the photos inside the category will show in the bottom pane of the application.
Humans are prone to errors, if you accidentally create any Local Set, don’t worry select the Local Category and just press delete key in your keyboard, you are done. The local set is deleted (only from iTraveller). In some scenarios you may need to delete only couple of photos from a local set. You can do that as well, select the photo from the thumbnail view and press delete key in your keyboard.
Mark for Flickr Uploading
You need to authorize iTraveller to connect to Flickr. Clicking on the synchronize button for the first time will popup a dialog box and follow the step to authorize it. After the authorization iTraveller will download the Flickr sets from Flickr.
Now let’s see how we can upload photo(s) to Flickr. Select a Flickr set in the right hand side of the application. Click on the Flickr set you want to upload your photo. It will open a tab as shown below.
Drag the required photo from the thumbnail to the Queue box and drop it. You also can give the Title and Description for the photo. By default visible to public is unchecked, if you want your photo to be visible to public then check the ‘Is Visible to Public’. You can put as many photo to queue. Once you are connected to internet, click the synchronize button just below the Flickr set to upload it to Flickr.
Mark for Facebook Uploading
You can upload the photo to Facebook just like the way we did for Flickr, instead of Flickr set you should select a Facebook Album on the right hand side. First time synching with Facebook needs your authorization. After you allow iTraveller to communicate with Facebook account, it will download all your Facebook Albums. iTraveller will take some time to get the photo albums from Facebook.
Once the synchronization is over and iTraveller got any new comments from Facebook or Flickr then it will open an Updates tab for you, so that you wont miss any updates.
You can provide different title and description for Facebook and Flickr Photos. These text is also searchable.
Create Flickr Set Through iTraveller
You can also create Flickr Set through iTraveller. Click on the ‘+’ icon, just above the Flickr Set Thumbnails. iTraveller will popup a window to enter new Flickr Set and Set description, click save once you done. While synchronizing with Flickr, the set will get created in Flickr. To create a Flickr Set successfully, at least one photo should be their in the upload queue of the newly created Set. The thumbnail of the newly created Set will be displayed only after the synchronization.
Search
iTraveller have built in searching feature. It enables the user to search for photos that satisfy the search text. The search will only look for photos added to iTraveller. User can search for the photo name, Title, Description, Flickr Title, Flickr Description, Facebook Description and Photo Comments. Enter the Search Text at the text box located at the top right corner of the application.
As a photographer you will have a lot of photos in your hardisk. Always our big problem is how to find a photo with some attributes. iTraveller can help you out, you can add a title and description and just save it. When ever you want to find it just enter the search text and click search. Another reason for search is, you may wanted to find the photos that got comments from your friends (either from Facebook or Flickr), let’s say Kevin or Sony. Go ahead and enter Kevin;Sony in the search text and hit Search button. iTraveller will give you the list of photos commented by Kevin or Sony.
You can see the preview of the Searched photo by double clicking on the thumbnail.
Comments
In the preview if any photo have any comments then a View comments button will shown in the preview tab as shown below.
Clicking on the comments button will open a new comment tab as shown below. You can leave this tab open and the comments will get changed when you navigate from one photo to another through the thumbnail.
Preview Tab
Preview tab will display the image in preview mode. Below the preview image, you can see below buttons
More feature will get added in next release, but needs motivation. Use my iTraveller and Motivate me .
Upgrade to New Versions
In coming days I will release iTraveller with bug fixes and new enhancements.
If you have already installed iTraveller 2.0, then you can upgrade to latest version. Please take a backup of the following files and folders before you update to new version, it will help you keep your data safe. In case of any issue after the update like not showing your Local set or any thing, you always can put the backuped up files back to the iTraveller folder.
You can find the the below files/folders here ‘Program Files\iTraveller\iTraveller’
Files/Folders to Backup
- ITravellerData.data (File)
- FacebookThumbnails (Folder)
- FlickrThumbnails (Folder)
- SearchIndex (Folder)
Download
I uploaded the application here. Install it and let me know your feed back.
Photo Album using Silverlight,Caliburn Micro and MongoDB–Part 2
This post is a continuation to my another post about a photostream application in Silverlight 4. You all can read more about the post here. I publish the code to codeplex with some more updates. The functionalities added to application is as below
- Add/Delete Categories
- Public commenting for Photos
- Delete Photo
- Filter photo’s based on Category
Below are some of the functionality in the app.
Home Page
The home page will display the thumbnails of all your photos. In the right hand pane you can see the list of categories I created. User can see the photos in a particular category by clicking the category. You can add category by clicking on the Add/Edit category in the right hand side.
Photo Preview
Nothing much to say it just show a preview of your photo. As you can see there are some icons at the bottom of the preview window. One for adding/viewing comments and another for deleting the photo. You might wonder how a public user can delete a photo. No they can’t am working on the admin part once it’s added only the admin user can do the delete and other admin related functionalities.
Public Comment(s)
Clicking on the small icon next to the delete button will show the comments as well as user can add comments.
Upload Photos
You can upload multiple photos at a time. Currently assigning a photo to category can be done at the time of uploading.
Hope this app will be helpful for some one.
Download Source code.
Photo Album using Silverlight, Caliburn Micro and Mongo DB
In this post I am going to explain a small Photo Album I created using Silverlight. With this app I also joined the band wagon of NoSQL movement. I used MongoDB as my backend for this application. This application is still in the development mode. I will add more functionality to this application like Public comments, admin module, etc. Currently this app shows the functionality of admin module, like upload photo, delete photo, etc. The application uses MVVM pattern and as usual I used Caliburn Micro here. I used several framework in the app as shown below
- Silverlight
- Caliburn Micro
- Reactive Extension
- MongoDB
- WCF
In this application I used Reactive Extension (Rx) to perform Async service calls. I felt the Rx is a very powerful tool to perform Async calls. The one good advantage of Rx is you can place Async calls in Thread Pool and push it to dispatcher once the call completed. All these can be done in one or two lines of code. This way the UI will always responsive.
This is my first experiment with Mongo db, I can say it’s very easy to use and fast. There are several Mongo drivers for .NET available in the market and is free. I used the one from here. The advantage of Mongodb is, I don’t want to write code to map my POCO to tables. You can directly persist your entity to Mongodb. You can find a lot of article about Mongo db in internet
I don’t think I need to explain Caliburn Micro in detail here. I wrote a post that gives a brief introduction to Caliburn Micro.
Running MongoDB
Below are the steps to run MongoDB
- Download the latest version of Mongo db from Mongodb.org
- Extract the zip file to C:\Mongodb
- Create a Folder called data in C drive
- Create a subfolder called db inside data
- Goto command prompt and change directory to C:\Mongodb
- Type Mongod in the command prompt and hit enter
Now the Mongodb will listen to the default port and our app can make request. There are different ways of hosting Mongodb, you can get more details from Mongodb site.
The App
Below is the screen shot of my application.
This page will display the thumbnails of uploaded images. We can see the bigger image by clicking on the thumbnail as show below.
The preview will display in a Windowless popup. We can delete the photo by clicking the Trash icon bottom right side of the image. We can upload images by clicking on the upload icon at the top right of the window. The upload page will get displayed as shown below.
When the user upload the image, the application will create a thumbnail version of the image and store along with the original image in the server. This helps to transfer and load the thumbnail page.
Let’s see the entity used in this application.
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.IO; using System.Drawing; using System.Drawing.Imaging; namespace PicturePortfolio.Domain { [DataContract] public class Photo { private const int THUMBWIDTH = 100; private const int THUMBHEIGHT = 100; public Photo() { } public Photo(PhotoPersistanceObject persistanceObject) { this.Name = persistanceObject.Name; this.Description = persistanceObject.Description; this.ID = persistanceObject.ID; this.PublicComments = persistanceObject.PublicComments; this.GUID = persistanceObject.GUID; this.LoadThumbNail(); } [DataMember] public int ID { get; set; } [DataMember] public string GUID { get; set; } [DataMember] public byte[] ImageFile { get; set; } [DataMember] public byte[] ImageThumbNail { get; set; } [DataMember] public string Description { get; set; } [DataMember] public string Name { get; set; } [DataMember] public List<PublicComment> PublicComments { get; set; } public PhotoPersistanceObject GetDataForPersistance() { PhotoPersistanceObject photoPersistance = new PhotoPersistanceObject(); photoPersistance.ID = this.ID; photoPersistance.Name = this.Name; photoPersistance.ImagePath = "/test/" + this.Name; photoPersistance.Description = this.Description; photoPersistance.PublicComments = this.PublicComments; if (string.IsNullOrEmpty(this.GUID) == true) photoPersistance.GUID = Guid.NewGuid().ToString(); else photoPersistance.GUID = this.GUID; return photoPersistance; } public void SaveImage() { if (this.ImageFile == null) throw new ArgumentNullException(); try { byte[] buffer = this.ImageFile.ToArray(); MemoryStream memStream = new MemoryStream(); memStream.Write(buffer, 0, buffer.Length); this.SaveOrginalImage(memStream); this.SaveImageThumbNail(memStream); } catch (Exception ex) { throw; } } public void LoadImage() { string fullFileName = this.GetFileFullPath(); this.ImageFile = this.GetImageAsByteArray(fullFileName); } public void LoadThumbNail() { string fullFileName = this.GetFilePath() + this.ThumbNailImageName; this.ImageThumbNail = this.GetImageAsByteArray(fullFileName); } #region Private Methods private void SaveOrginalImage(MemoryStream memStream) { System.Drawing.Image imgToSave = System.Drawing.Image.FromStream(memStream); imgToSave.Save(this.GetFileFullPath(), ImageFormat.Jpeg); } private void SaveImageThumbNail(MemoryStream memStream) { Image img = Image.FromStream(memStream); Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback); Image imageToSave = img.GetThumbnailImage (THUMBWIDTH, THUMBHEIGHT, myCallback, IntPtr.Zero); imageToSave.Save(this.GetFilePath() + ThumbNailImageName, System.Drawing.Imaging.ImageFormat.Jpeg); } private static bool ThumbnailCallback() { return false; } private byte[] GetImageAsByteArray(string fullFileName) { FileInfo fil = new FileInfo(fullFileName); if (fil.Exists == true) { FileStream fileStream = new FileStream(fullFileName, FileMode.Open, FileAccess.Read); byte[] buffer = new byte[fileStream.Length]; fileStream.Read(buffer, 0, (int)fileStream.Length); fileStream.Close(); return buffer; } else return null; } private string GetFilePath() { string filePath = AppDomain.CurrentDomain.BaseDirectory + "Images\\"; return filePath; } private string GetFileFullPath() { string fullPath = this.GetFilePath() + this.Name; return fullPath; } private string ThumbNailImageName { get { string fileName = this.Name.Substring(0, Name.LastIndexOf(".")); return fileName + "_thumb.jpg"; } } #endregion } }
The entity class is very simple. I may need to give a brief explanation about PhotoPersistanceObject used in the entity class. The PhotoPersistanceObject is used for persisting meta data of the image like Name, description etc. You might think that why cant we save Photo object directly, yes I also thought the same way but their is some issue with Mongodb driver I used here. It’s not supporting byte array neither binary, It allows to store binary in Mongodb but throws error while retrieving. So I used a trimmed down version of Photo entity to persist the data.
Async Calls using Thread Pool
As I told before one of the cool feature of Reactive Extension is it’s advantage of making async calls in thread pool. Even if we place async calls in separate thread in Silverlight still it will block the UI to load if we do it in page load. Because the our thread will work in UI. We can achieve the placing calls in thread pool using Rx. See the below code.
private void GetAllThumbnails() { var func=Observable.FromEvent<GetAllPhotosCompletedEventArgs>(_service,"GetAllPhotosCompleted") .ObserveOn(Scheduler.ThreadPool);
_service.GetAllPhotosAsync();
func.ObserveOn(Scheduler.Dispatcher)
.Select(result => result.EventArgs.Result)
.Subscribe(s => this.ParseThumbnails(s.ToList()));
}
As you can see the service calls are doing in ThreadPool. Once the call is done we will change the ObserveOn to Dispatcher, other wise cross thread expection will throw. I call the GetAllThumbnails in the constructor of a ViewModel. As we are doing the call in Threadpool my UI will loaded without any delay, once the call is completed the thumbnails will start display in the listbox.
Mongodb Repository
I wrote a Generic repository similar to the one I wrote for EF. You can find the generic repository for EF in one of my post. Below is the Generic repository class for Mongodb.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using MongoDB; using MongoDB.Linq; using MongoDB.Connections; namespace PicturePortfolio.Persistance { public class GenericRepository:IRepository { public GenericRepository() { } Mongo _mongoDb=null; public IMongoCollection<TEntity> GetQuery<TEntity>() where TEntity : class { if (_mongoDb == null) { _mongoDb = new Mongo(); _mongoDb.Connect(); } return DataBase.GetCollection<TEntity>(typeof(TEntity).Name + "s"); } IMongoDatabase db = null; private IMongoDatabase DataBase { get { if (db == null) { db = _mongoDb.GetDatabase("Protfolio"); } return db; } } public IList<TEntity> GetAll<TEntity>() where TEntity : class { try { return GetQuery<TEntity>().Linq().ToList<TEntity>(); } catch (Exception ex) { throw; } } public IList<TEntity> Find<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> criteria) where TEntity : class { return this.GetQuery<TEntity>().Linq<TEntity>().Where(criteria).ToList<TEntity>(); } public TEntity Single<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> criteria) where TEntity : class { return this.GetQuery<TEntity>().Linq<TEntity>().Single(criteria); } public TEntity First<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> criteria) where TEntity : class { return this.GetQuery<TEntity>().Linq<TEntity>().First(criteria); } public void Add<TEntity>(TEntity entity) where TEntity : class { this.GetQuery<TEntity>().Save(entity); } public void Delete<TEntity>(TEntity entity) where TEntity : class { this.GetQuery<TEntity>().Remove(entity); } public void Delete<TEntity>(IEnumerable<TEntity> entites) where TEntity : class { throw new NotImplementedException(); } public void Update<TEntity>(TEntity entity, TEntity old) where TEntity : class { this.GetQuery<TEntity>().Update(entity, old, UpdateFlags.None, true); } } }
This app is in very primitive stage. you may need to do lot of refactoring to make use in real scenario. I will work on this app and upload it to codeplex some times later.
Download the current source here.
An Introduction to Caliburn Micro
Introduction
This post gives some brief introduction to develop Silverlight applications using Caliburn Micro which gives the basic features are:
- BootStrapping
- Conventions to write ViewModels
- Event Aggregation
- Action Message
Once you got the above basic ideas, it’s very easy to develop scalable applications in Silverlight. You can download the latest Caliburn Micro source from CodePlex.
When MVVM pattern introduced into my Silverlight development, initially it’s not digesting for me. But after I got the clear picture of MVVM pattern, I felt It’s very easy to develop Silverlight application that can adaptable to changes very easily. We choose Caliburn Micro as our MVVM framework, one reason it’s very light weight, another reason is the code base is very less. As the code base is very less we can go through it understand it better.
I hope every one who reads this post has some idea of MVVM pattern. In short MVVM separates View (presentation) and the code manipulates the view separately, and called it as View Model. One of the main advantage of MVVM is we can modify View or ViewModel without affecting each other. There is no event handlers for any control in the code behind, so it’s easy to replace one control with another. We can even reuse the same view model for different platform, let say we are developing an app for both Silverlight and WP7, here only difference in view is it’s base classes but UI is same. So we can share the same View Model thus by increase the maintainability and decrease the effort.
How the View and View Model’s will communicate? here the MVVM framework like Caliburn Micro will comes into picture. The MVVM Framework will bind the View and View Model’s. There are several MVVM frameworks in the market like Prism, MVVM Light, etc. but am not going to cover those.
Caliburn Micro uses conventions to bind View and View Model’s, I feel the conventions are better than wiring through code or attributes. You will get the conventions of Caliburn Micro as we progress through this post.
There are two approach in MVVM model, Code first and View first. Which one is better? I am not the person to comment on, their is an age old debate is going on. Personally I prefer Code First approach. But here to simplify things I will do it in View First approach.
Caliburn Micro In Action
Let’s do some small application in Silverlight using Caliburn Micro. The functionality of this app is pretty simple, display a list of customer and allow the user to edit it.
I am going to create a user control to display the details of individual customer. In the listing page we will bind this User control to a List. Let’s create the control.
IndividualResultView.xaml
<Grid x:Name="LayoutRoot" Background="White"> <Border BorderThickness="3" BorderBrush="Black" Margin="3"> <Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="30"></RowDefinition> <RowDefinition Height="60"></RowDefinition> <RowDefinition Height="30"></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="120"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock x:Name="lblName" Text="Name" Grid.Row="0" Grid.Column="0"></TextBlock> <TextBlock x:Name="Name" Grid.Row="0" Grid.Column="1"></TextBlock> <TextBlock x:Name="lblAddress" Text="Address" Grid.Row="1" Grid.Column="0" ></TextBlock> <TextBlock x:Name="Address" Grid.Row="1" Grid.Column="1"></TextBlock> <TextBlock x:Name="lblPhoneNumber" Text="Phone#" Grid.Row="2" Grid.Column="0"></TextBlock> <TextBlock x:Name="PhoneNumber" Grid.Row="2" Grid.Column="1"></TextBlock> <HyperlinkButton x:Name="EditCustomer" Grid.Row="3" Content="Edit"></HyperlinkButton> </Grid> </Border> </Grid>
IndividualResultView.xaml.cs
Let’s see the IndividualResultView.xaml.cs, as per the MVVM pattern it should not contain any View related code, other than initializing the UI controls. You can see that our code behind is clean.
using System.Windows.Controls; namespace WCFEnabledSilverlightApp.Views { public partial class IndividualResultView : UserControl { public IndividualResultView() { InitializeComponent(); } } }
We need to create a ViewModel for the above view, let’s do it. I named my ViewModel as IndividualResultViewModel.cs. Below is the code
using DataModel; using Caliburn.Micro; namespace WCFEnabledSilverlightApp.ViewModels { public class IndividualResultViewModel:PropertyChangedBase { string _name; public string Name { get { return _name; } private set { _name = value; NotifyOfPropertyChange(() => Name); } } string _address; public string Address { get { return _address; } private set { _address = value; NotifyOfPropertyChange(() => Address); } } public string PhoneNumber { get; private set; } private Customer _customer = null; public IndividualResultViewModel(Customer customer) { this._customer = customer; this.Name = customer.CustomerName; this.Address = customer.Address; this.PhoneNumber = customer.PhoneNumber; } public void EditCustomer() { EditCustomerViewModel editCustomer = new EditCustomerViewModel(_customer); Screen scrn = new Screen(); WindowManager winMngr = new WindowManager(); winMngr.ShowDialog(editCustomer); } } }
Convention 1
We learned our first convention of Caliburn Micro. The UI should suffix with “View”. The View Model should suffix with “ViewModel” and prefix with ViewName
<ViewName>View.xaml
<ViewName>ViewModel.cs
Eg.
IndividualResultView.xaml
IndividualResultViewModel.cs
Convention 2
You might have noticed that in the view and viewmodel we used some convention. The name of TextBlock that display the customer details have a corresponding property in ViewModel. For E.g. the TextBlock name for displaying Customer Name is “Name” and in ViewModel you can see a property “Name”. The Caliburn Micro will bind the Property in the ViewModel to the View. That means what ever value we set for property “Name” will be displayed in the View. Pretty simple.
You might have one question now how to handle click event or those kind of events raised by your view in our View model. It’s pretty easy create a function with same name as your button. Just go back to the View and check the name of the hyperlink button, also check whether their is method with same name in View model. Hyperlink button name is EditCustomer and I have one method in View model EditCustomer. The method will create an instance of another view model.
Later in this post you can see how we can pass parameters from your view to view model.
NotifyOfPropertyChange
In our properties the setter calls a function called NotifyOfPropertyChange, what is the use of this function call? We call this function to notify the UI that the value has changed in the view model. We can notify UI without calling NotifyOfPropertyChange by implementing INotifyPropertyChanged interface and pass the property name. Caliburn Micro wraps the implementation of INotifyPropertyChanged in PropertyChangedBase and our view model is inherited from it. NotifyOfPropertyChange we are not passing any string value instead we pass the Property itself. The advantage of this method is we can use VS refactoring feature on properties. If we pass it as string then refactoring will not take into consideration.
Event Handling
When I implemented MVVM using Caliburn Micro I implemented the communication between View Models using normal event driven mechanism. That means If I want to handle an event occurred in a child view model in the parent. Then child view model raise an event and the parent will handle it. I didn’t like this approach as it creates a dependency between View models. So I start exploring Caliburn Micro to figure out the event handling features and my search ends in EventAggregator.
Caliburn Micro’s Event Aggregator functionality is a very powerful implementation of Observer pattern. We can write View Models with less coupling using EventAggregators. Let’s see how can we establish communication between View Models using Event Aggregator.
In the sample code I created a static property to give access to EventAggregator as shown below
public class EventAggregationProvider { static EventAggregator _eventAggregator = null; public static EventAggregator EventAggregator { get { if (_eventAggregator == null) _eventAggregator = new EventAggregator(); return _eventAggregator; } } }
In the sample app I have a popup window to edit the Customer Details, when the user clicks Save/Ok I need to notify the Parent View Model to initiate the Save process. Let’s see how to implement it.
In my CustomerEditViewModel.cs there is a method called SaveCustomer which will get called when the user clicks Ok from the View. You can see that in SaveCustomer I am publishing the Customer object. See how I am publishing it.
EventAggregationProvider.EventAggregator.Publish<Customer>(customer);
This ViewModel done his job, it’s job of the parent view model who can subscribe to this message and get the notification. Now let’s see how the parent view model is subscribing to this event.
public class CustomerListingViewModel : PropertyChangedBase, IHandle<Customer> {
public CustomerListingViewModel()
{
Messages.EventAggregationProvider.EventAggregator.Subscribe(this);
}
public void Handle(Customer message) { //do the save process }
}
To Subscribe to Customer message the CustomerListingViewModel is implemented by IHandle<Customer>. That means this CustomerListingViewModel is capable of handling any message with type Customer. One more line of code need to added to get notification. You can see in the constructor of CustomerListingViewModel to subscribe to EventAggregator.
Your View Model can subscribe to any number of messages by implementing IHandle with the respective type. For e.g
public class CustomerListingViewModel : PropertyChangedBase, IHandle<Customer>,IHandle<Order> {
public CustomerListingViewModel()
{
Messages.EventAggregationProvider.EventAggregator.Subscribe(this);
}
public void Handle(Customer message) { //do the save process }
public void Handle(Order message)
{
//do the save process
}
}
In the above e.g this View Model will get notified if any view model publish Customer or Order message.
Action Message
The Action Message is one of the functionality of Caliburn Micro to call parameterized function from View. Let’s see how we can do it. To do this we need to refer system.windows.interactivity assembly.
In the below e.g. a message box will show when a LostFocus happened in View EditCustomerView. Let’s go through the View
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ca="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
<TextBox x:Name="Name" Grid.Row="0" Grid.Column="1"> <i:Interaction.Triggers> <i:EventTrigger EventName="LostFocus"> <ca:ActionMessage MethodName="NameLostFocus"> <ca:Parameter Value="{Binding ElementName=Name,Path=Text}"></ca:Parameter> </ca:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </TextBox>
I referenced System.Windows.Interactivity and Caliburn Micro in the View. As you can see in the above view I mentioned “LostFocus” to the EventName. The ActionMessage takes the MethodName that we created in the ViewModel. Parameter’s Value take the parameter to the method. So what’s this means, it’s very simple, whenever the lost focus happened in Name text box then the view should call NameLostFocus method with TextBox’s Text as the parameter. Below is the NameLostFocus method in the ViewModel
public void NameLostFocus(string text) { MessageBox.Show(text); }
You can see more about Action Message in Rob’s blog
Bootstrapping
Bootstrapping is the method of booting our application and allow Caliburn Micro to take the control. So how to create a bootsrapper. It’s very simple as shown below
namespace WCFEnabledSilverlightApp { public class BootStrapper:Bootstrapper<ViewModels.CustomerListingViewModel> { } }
It’s tells caliburn that CustomerListingViewModel is the first page to load. It’s same as setting RootVisual to the some page say MainPage in non MVVM model. We need to provide this BootStrapper in App.xaml as shown below
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="WCFEnabledSilverlightApp.App" xmlns:bs="clr-namespace:WCFEnabledSilverlightApp" > <Application.Resources> <bs:BootStrapper x:Name="bootStrapper"></bs:BootStrapper> </Application.Resources> </Application>
Below is code behind of App.xaml. No code just clean.
using System.Windows; namespace WCFEnabledSilverlightApp { public partial class App : Application { public App() { InitializeComponent(); } } }
This post is just an introduction to Calibun Micro, just use it and get more comfortable with it. Thanks for reading this long post.
Introduction to Caliburn Micro Part 2
Download the source code.
Twitter Public Timeline reader for WP7 using Caliburn Micro
This is my first app for Windows Phone 7. This application doesn’t have much functionality, it just make a call to Public Timeline rest api to get the tweets. I choose PublicTimeline as it doesn’t requires any Twitter authentication. John Papa have a blog about communicating with Twitter, I used his blog to gather the details to communicate with Twitter. As usual I used my favorite MVVM frame work Caliburn Micro here as well. To kick start with WP7 development using caliburn micro I suggest to download the template from shazaml.com. The template has the CM’s WP7 bootstrapping technique mentioned by Rob Eisenberg in his blog.
Screen shot of my app.
In this app I used Microsoft Reactive Extension (Rx) libraries to make asynchronous call to Twitter. I combined both the Rx and Caliburn micro’s EventAggregator to do Async call and publish the data. Rx works in publisher/subscriber model, Initially I wrote an observer by my self. After I thought why should I reinvent the wheel, as Caliburn Micro has a powerful publisher/subscriber module. So I removed my observer class with EventAggregator shipped with CM. I used Rx here to avoid all the messy code that we need to write to make an async call. Jerome Laban have a blog about Rx library and is a pretty good article to start off with Rx library.
Another functionality I wanted to add to the app was caching facility, that means the app should be able to cache older tweets. Instead of fetching from Twitter we can get it from cache, if user wants to see older tweets. I was thinking of serializing my Entities to IsolatedStorage and do all the hard work by myself. Before implementing the serialization functionality I did a google search and come accross this codeplex project called winPhone7db. It did a decent job of serializing my entities to Isolated storage. As the name states its gives a feel like we are dealing with db. It encapsulates all the hurdles of communicating with Isolated storage and serialization.
Let’s jump into the details of the app. Below code explains my MainPage view model, it’s not very complicated just like any other basic view model
public class MainPageViewModel:Screen,IHandle<TimelineMessage> { readonly INavigationService navigationService; public MainPageViewModel(INavigationService navigationService) { this.navigationService = navigationService; EventAggregatorHelper.EventAggregator.Subscribe(this); this.Tweets = new ObservableCollection<TweetViewModel>(); } public ObservableCollection<TweetViewModel> Tweets { get; private set; } protected override void OnViewLoaded(object view) { base.OnViewLoaded(view); TwitterCommunicator twitterCommunicator = new TwitterCommunicator(); twitterCommunicator.GetPublicTimelines("sonyarouje");
} public void LoadMore() { TwitterCommunicator twitterCommunicator = new TwitterCommunicator(); this.CreateUI(twitterCommunicator.LoadFromCache()); } private void CreateUI(List<Tweet> tweets) { foreach (Tweet tweet in tweets) { TweetViewModel tweetViewModel = new TweetViewModel(tweet); this.Tweets.Add(tweetViewModel); } } public void Handle(TimelineMessage message) { if (message.Tweets != null) { this.CreateUI(message.Tweets); TwitterCommunicator twitterCommunicator = new TwitterCommunicator(); twitterCommunicator.SaveToCache(message.Tweets); } } }
I inherited the view model from Screen so that I can make use of OnViewLoaded method. The async call to Twitter orginates from onViewLoaded method. Also you can see that this viewmodel is subscribed to TimelineMessage. This is to get the notification once the Reactive Extension completes it’s async call to Twitter using webclient.
TweetViewModel is another view model to display individual tweets. All the individual TweetViewModel get added to an observable collection called Tweets and caliburn micro internally bind the respective view to an ItemsControl in my MainPageView.
Now let’s go through the Twitter communicator
public class TwitterCommunicator { private readonly string FriendTimeLine = "http://twitter.com/statuses/friends_timeline/{0}.xml?count=50"; private readonly string PublicTimeLine = "http://api.twitter.com/1/statuses/public_timeline.xml?screen_name={0}"; public void GetPublicTimelines(string userName) { string uriString = string.Format(PublicTimeLine, userName); Uri uri = new Uri(uriString); WebClient wc = new System.Net.WebClient(); var o = Observable.FromEvent<DownloadStringCompletedEventArgs>(wc, "DownloadStringCompleted") .ObserveOn(Scheduler.ThreadPool) .Select(newString => newString.EventArgs.Result); o.ObserveOn(Scheduler.Dispatcher).Subscribe(s => EventAggregatorHelper.EventAggregator.Publish<TimelineMessage>(new TimelineMessage(LoadPublicTimeLines(s.ToString())))); wc.DownloadStringAsync(uri); } public List<Tweet> LoadFromCache() { Repository repository = new Repository(); return repository.GetFromCache<Tweet>(); } public void SaveToCache(List<Tweet> tweets) { Repository repository = new Repository(); repository.Add<Tweet>(tweets); repository.SaveChanges(); } private List<Tweet> LoadPublicTimeLines(string statuses) { try { XElement xmlElement = XElement.Parse(statuses); ; XNamespace ns = ""; var twitterQuery = from msg in xmlElement.Descendants(ns + "status") let sender = msg.Element(ns + "user") select new Tweet { TwitterId = (msg.Element(ns + "id").Value).ToLong(), CreatedAt = (msg.Element(ns + "created_at").Value).ToDateTime(), Text = msg.Element(ns + "text").Value, User = new User { UserId = (sender.Element(ns + "id").Value).ToLong(), Name = sender.Element(ns + "name").Value, ScreenName = sender.Element(ns + "screen_name").Value, Description = sender.Element(ns + "description").Value, Location = sender.Element(ns + "location").Value, ProfileImageUrl = sender.Element(ns + "profile_image_url").Value, Url = sender.Element(ns + "url").Value, Protected = (sender.Element(ns + "protected").Value).ToBool(), FollowersCount = (sender.Element(ns + "followers_count").Value).ToLong() } }; List<Tweet> statusList = twitterQuery.ToList<Tweet>(); return statusList; } catch (Exception ex) { return null; } }
You need to add Microsoft.Phone.Reactive assembly to avail the functionality of Rx. Also I installed the Rx setup for Silverlight 4 and added System.Observable reference shipped with the setup.
The function GetPublicTimeLines place the async call to Twitter. As I said before I use Rx to handle the async call and notify the subscriber once it’s done. I use the WebClient class to communicate with Twitter. I choose WebClient as it is very easy to use shipped with WP7, also am not doing any complex calls to twitter that requires authentication. The below code will get executed once the async call is completed.
o.ObserveOn(Scheduler.Dispatcher).Subscribe(s => EventAggregatorHelper.EventAggregator.Publish<TimelineMessage>(new TimelineMessage(LoadPublicTimeLines(s.ToString()))));
The above code does publication of the result. Before publishing I processed the result we got from twitter and converted the xml message to Tweet entity. Conversion is done by LoadPublicTimeLine method. Once the processing is done I published the message using EventAggregator.
Now Let’s see the caching part. I created a repository class to communicate with SilverlightPhoneDatabase. Below is the class I created for it.
using System; using System.Linq; using System.Collections.Generic; using SilverlightPhoneDatabase; namespace TwitterClientAPI { public class Repository { private const string DATABASENAME = "TweetsCache"; Database _db; private Database TweetDataBase { get { if (_db == null) { if (Database.DoesDatabaseExists(DATABASENAME) == false) { _db = Database.CreateDatabase(DATABASENAME); _db.Save(); } else { _db = Database.OpenDatabase(DATABASENAME); } } return _db; } } private Table<T> GetTable<T>() where T : class { Database db = this.TweetDataBase; if (db.Table<T>() == null) { db.CreateTable<T>(); } return db.Table<T>(); } public void Add<T>(List<T> entities) where T : class { Table<T> table = this.GetTable<T>(); foreach (T entity in entities) { table.Add(entity); } } public void Add<T>(T entity) where T : class { Table<T> table = this.GetTable<T>(); table.Add(entity); } public void SaveChanges() { this.TweetDataBase.BeginSave((s)=> { if (s.Error == null) { //save unsuccessful, take necessary action } }); } public List<T> GetFromCache<T>() where T:class { try { var query = (from tCache in this.TweetDataBase.Table<T>() select tCache); List<T> cachedData = query.ToList<T>(); return cachedData; } catch (Exception ex) { return null; } } } }
I used the same generic repository pattern explained in one of my previous post. When I wrote this app I spend most of the time in investigating about different methods of implementing Async call and caching of tweets. Developing an app for WP7 is pretty simple I spent less than 2 hrs to finish the app. Apart from the development, I spent some time to learn Rx. Through this app I got some insight of WP7 development, Reactive Extension, SilverlightPhoneDatabase and more attached to Caliburn Micro :).
If you really want to do some decent job using Twitter API then I suggest Hammock. It’s a REST library to consume REST Services. The library also supports OAuth authentication and can be used to connect to Twitter. Sudheer has blog series explaining about authentication with Twitter using Hammock. Also Scott Gu has a very good blog about the overview of WP7 development.
To run the source you need to have WP7 Development tools. You can download the tools from here.
Download Source code.
Scalable Silverlight app using MEF and Caliburn Micro
In this post I am going to give a brief insight of my experiment with MEF and Caliburn Micro. I am always a big fan of plug-in application model. When I go through the MEF I was so impressed and want to try as it is similar to my favorite area (plugin model). As usual I used caliburn micro here as well.
The app I developed is just for demo purpose and there is no business value. In my app I display couple of user controls and all are loaded by MEF. The app also demonstrate the powerful event handling provided by Caliburn micro. With the help of MEF and Caliburn micro we can build a very powerful scalable silverlight app.
Below is the screen shot of my app
As you can see my UI is not very fancy, because my intention was to try how MEF will work in conjunction with caliburn micro. The first two expanders wont do any job other than displaying some thing to user. The Display Text and Font selection will do some demonstration of Event publishing. The user selected Font properties will get applied to the Text in the Display Text expander.
I implemented an interface called IView to make all the view models satisfy MEF exports. Below is the interface.
namespace SilverlightClassLib.Common { [InheritedExport] public interface IView { } }
A blank interface decorated by and Attribute called InheritedExport. Which ever the class implements this interface will be considered for Exporting. You can get more details about this approach from Brad Abrams blog. In this demo all my viewmodels implemented IView interface. MEF will gather all my Exported ViewModels and Caliburn micro will intern load the View for the view model.
I compose my UI in MainPageViewModel.cs as shown below. To compose the ui you have to add reference to assembly System.ComponentModel.Composition and add using’s to System.ComponentModel.Composition.Hosting and System.ComponentModel.Composition
namespace MEFCaliburnMicroTracer.ViewModels { public class MainPageViewModel:PropertyChangedBase { public MainPageViewModel() { var catalog = new PackageCatalog(); catalog.AddPackage(Package.Current); var container = new CompositionContainer(catalog); container.ComposeParts(this); } [ImportMany(AllowRecomposition=true)] public ObservableCollection<IView> importedViews { get; set; } } }
In the constructor we load the current xap file to MEF container. The exported part will get added to property which is decorated with ImportMany attribute. In this case it’s ImportedViews property. I set AllowRecomposition to true to load exported parts if we do any async loading of xap’s. In this case there is no parameter required as we are not doing any async loading.
A Listbox In the MainPageView is binded to importedViews property. This listbox is responsible of displaying all my Views.
The greatest advantage of MEF is, we can add or remove new functionality without affecting the existing modules. And the powerful EventAggregator in Caliburn micro will help to enable loosely coupled interaction between the modules.
Download Source
Persisting Combo box SelectedItem – Caliburn Micro
In this post I am going to explain how to persist Selected Item in a Combo box. In my application I have several screens and the user can navigate between them. I need to persist the Combobox’s Selected Item and bind it back when user navigate back to the screen. Here persisting is not db level persistence.
My application is based on Caliburn Micro, a wonderful lightweight MVVM framework.
To persist the selections, I wont create new ViewModel when ever the user clicks on the Navigation button. Only once the instance of the view model will get created. The following code shows the implementation, very simple approach.
FontPropertiesViewModel _fontPropertiesViewModel; public void GetFontProperties() { if (_fontPropertiesViewModel == null) _fontPropertiesViewModel = new FontPropertiesViewModel(); ActiveScreen = _fontPropertiesViewModel; }
<UserControl x:Class="MVVMTracer.Pages.Views.FontPropertiesView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ca="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro" mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot" Background="Transparent" Margin="0,0,10,0"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="25"/> <RowDefinition Height="35"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="35"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="150"/> <ColumnDefinition Width="0.555*"/> </Grid.ColumnDefinitions> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Text" VerticalAlignment="Center" FontSize="13.333" Margin="10,0,0,0" Foreground="#FF0F5DA7" Grid.ColumnSpan="2" Width="297"/> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Type" Margin="10,0,0,0" Grid.Row="2" FontSize="11" VerticalAlignment="Center"/> <ComboBox x:Name="FontType" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" FontSize="11" SelectedItem="{Binding SelectedFontType}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <ca:ActionMessage MethodName="FontTypeSelectionChanged"> <ca:Parameter Value="{Binding ElementName=FontType,Path=SelectedItem}"></ca:Parameter> </ca:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </ComboBox> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Size" Margin="10,0,0,0" Grid.Row="3" FontSize="11" VerticalAlignment="Center"/> <ComboBox x:Name="FontSize" Margin="0,0,0,0" Grid.Row="3" FontSize="11" HorizontalAlignment="Left" VerticalAlignment="Center" Width="53" Grid.Column="1" SelectedItem="{Binding SelectedFontSize}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <ca:ActionMessage MethodName="FontSizeSelectionChanged"> <ca:Parameter Value="{Binding ElementName=FontSize,Path=SelectedItem}"></ca:Parameter> </ca:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </ComboBox> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Style" Margin="10,0,0,0" Grid.Row="4" VerticalAlignment="Center" FontSize="11"/> <ComboBox x:Name="FontStyle" Grid.Row="4" Grid.Column="1" FontSize="11" VerticalAlignment="Center" SelectedItem="{Binding SelectedFontStyle}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <ca:ActionMessage MethodName="FontStyleSelectionChanged"> <ca:Parameter Value="{Binding ElementName=FontStyle,Path=SelectedItem}"></ca:Parameter> </ca:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </ComboBox> <Button x:Name="Done" Content="Done" Grid.Row="5" Grid.Column="1" Width="70" HorizontalAlignment="Right" d:LayoutOverrides="Height" VerticalAlignment="Bottom"/> </Grid> </Grid> </UserControl>
If you examine the xaml you can see that the selected item of the combo boxes are binded to different properties, also I use ActionMessaging to notify the view model when ever a selection change happen in the Comboboxes. You might wonder why am using ActionMessaging rather than I could bind SelectedItem in two way mode. Yes I tried it first but unfortunately the Selected Item is not showing when user navigates back to the page.
Time to go through the view model
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Caliburn.Micro; using MVVMTracer.MockServices; using EmblemDesigner.Controls; namespace MVVMTracer.Pages.ViewModels { public class FontPropertiesViewModel:PropertyChangedBase { public FontPropertiesViewModel() { this.FontType = FontServices.GetFontFamily(); this.FontStyle = FontServices.GetFontStyle(); } private List<string> _fontType; public List<string> FontType { get { return _fontType; } private set { _fontType = value; NotifyOfPropertyChange(() => FontType); } } private string _selectedFontType; public string SelectedFontType { get { return _selectedFontType; } private set { _selectedFontType = value; } } public void FontTypeSelectionChanged(object selectedItem) { this.SelectedFontType = selectedItem as string; } private List<string> _fontSize; public List<string> FontSize { get { return _fontSize; } private set { _fontSize = value; NotifyOfPropertyChange(() => FontSize); } } private string _selectedFontSize; public string SelectedFontSize { get { return _selectedFontSize; } private set { _selectedFontSize = value; } } public void FontSizeSelectionChanged(object selectedItem) { this.SelectedFontSize = selectedItem as string; } private List<string> _fontStyle; public List<string> FontStyle { get { return _fontStyle; } private set { _fontStyle = value; NotifyOfPropertyChange(() => FontStyle); } } private string _selectedFontStyle; public string SelectedFontStyle { get { return _selectedFontStyle; } private set { _selectedFontStyle = value; } } public void FontStyleSelectionChanged(object selectedItem) { this.SelectedFontStyle = selectedItem as string; } } }
Let’s take an eg. of FontType to understand what I am doing here. In the View one of my Combo box name is FontType. Caliburn Micro is intelligent enough to bind the FontType properties in the view model to the Combo box with name FontType in the view. Really great. If you run the application, you can see the FontType combo box is populated with the fonts we added to the FontType properties.
But it’s not enough to persist the user selection. To do so we need to add some more code to the view model. You might have noticed in xaml that the ActionMessage of FontType combo is attached to FontTypeSelectionChanged. When ever the user change selection it will get notified to FontTypeSelectionChanged function in the view model. This function will intern update the SelectedFontType property. The selected SelectedFontType property is binded to FontType’s Combobox’s SelectedItem.
Below diagram shows the communication between the view and the view model what I explained above.
One thing I noticed here is the Auto Implemented or Automatic Properties wont work here.
If any better approach pls update me.
An Experiment In Caliburn Micro
In this blog I will explain my experiment with Caliburn Micro. The business logic of this app is
- It should display a list of images
- Add the image to the canvas when the user selected an image
- Drag the Image and Position the Image where ever the user required
- For dragging/rotation/resize the image I used an Adorner control. You can get more details about the control here.
I have to add Images displayed in a list box to a canvas. Pretty simple 🙂 and helped me a lot to learn Caliburn Micro. Before going further I wanted to say that the app may not be a full MVVM model, you will come to know as you proceed. I am working on it to make it completely MVVM complaint.
Below is the screen shot of the Silverlight app.
Two buttons are there on the top.
- Show Text: button will load User control that will have a UI with some text box and combo box.
- Image Controls: Button will load the user control displayed above.
Bare with my naming, I am very poor in it.
When the user clicks on any of these button we need to show the respective View in the left hand side. I used Caliburn Micro as my MVVM frame work.
My project structure is below
- Views: Contains my XAML files
- ViewModels: contains my view model of the views
- ValueObject: contains the value object class I used in this app
- Messages: I used EventAggregator provided by Caliburn Micro to enable messaging between ViewModels. This folder contains the Class I used for messaging between ViewModels.
- Controls: contains custom controls I used in this app.
- Adorner: contains the Adorner controls
- Let’s jump into the code
TabView
This is the main view which calls other other user controls- XAML
<navigation:Page x:Class="MVVMTracer.Views.TabView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:tl="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit" xmlns:we="clr-namespace:MVVMTracer.Controls" mc:Ignorable="d" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="TabView Page"> <UserControl.Resources> <DataTemplate x:Key="shapeTemplate"> <we:ControlBase Width="{Binding Width}" Height="{Binding Height}"/> </DataTemplate> </UserControl.Resources> <Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="40"></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal" Height="30"> <Button x:Name="ShowTextControls" Content="Show Text"></Button> <Button x:Name="ShowImageControls" Content="Image Controls"></Button> </StackPanel> <tl:TransitioningContentControl Grid.Row="1" Grid.Column="0" x:Name="ActiveScreen"></tl:TransitioningContentControl> <we:CustomItemsCollection x:Name="DisplayControls" Grid.Row="1" Grid.Column="1"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas Background="AliceBlue"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </we:CustomItemsCollection> </Grid> </navigation:Page>
In the above xaml you might notice that I used TransitioningContentControl. This is the control I am going to use to show different UserControl based on the user request. By the way TransitioningContentControl is part of Silverlight Tool kit,
Here I used a CustomItemsCollection to add my dynamically added controls. CustomItemsCollection is a control derived from ItemsControl. I will show the implementation of it later.
TabViewModel.cs
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Caliburn.Micro; using MVVMTracer.Adorners; using MVVMTracer.Controls; using MVVMTracer.Messages; namespace MVVMTracer.ViewModels { public class TabViewModel:PropertyChangedBase,IHandle<Messages.ImageSelectionChanged> { public TabViewModel() { EventAggregatorContainer.EventAggregator.Subscribe(this); } private object _activeScreen; private Adorner _adorner = new Adorner(); private Canvas _parentCanvas = new Canvas(); public object ActiveScreen { get { return _activeScreen; } set { _activeScreen = value; NotifyOfPropertyChange(() => ActiveScreen); } } public void ShowTextControls() { ActiveScreen = new ShowTextViewModel(); } public void ShowImageControls() { ShowImageControlViewModel imgViewModel = new ShowImageControlViewModel(); ActiveScreen = imgViewModel; } public void Handle(Messages.ImageSelectionChanged message) { ControlBase ctrl = ShapeFactory.GetShape(ShapeType.Image, _parentCanvas, _adorner); ctrl.Source = message.CurrentImage; this.AddToUICollection(ctrl); } private void AddToUICollection(ControlBase control) { if (_displayControls == null) { _displayControls = new System.Collections.ObjectModel.ObservableCollection<UIElement>(); _adorner.Height = 0; _adorner.Width = 0; _parentCanvas.Children.Add(_adorner); _displayControls.Add(_parentCanvas); } control.DrawControl(); control.Height = 100; control.Width = 100; _parentCanvas.Children.Add(control); NotifyOfPropertyChange(() => DisplayControls); } private System.Collections.ObjectModel.ObservableCollection<UIElement> _displayControls; public System.Collections.ObjectModel.ObservableCollection<UIElement> DisplayControls { get { return _displayControls; } private set { _displayControls = value; NotifyOfPropertyChange(() => DisplayControls); } } } }
The above code explains the ViewModel of TabView. As I mentioned in the beginning it may not completely satisfies MVVM pattern. I will explain the reason. As per my requirement I wanted to add Images to my Canvas and allow the user to reposition it.
To achieve the requirement I initially binded list of UIElement that consist of my Images to the CustomItemsCollection. But the issue I faced is I couldn’’t drag the image to different location. So I find out a work around. The work around is create a Canvas in Viewmodel and add the canvas to DisplayControls. Then add the images to the canvas object. By adding Canvas to DisplayControls will internally bind the Canvas object and its children to CustomItemsCollection in the View.
In this app I used EventAggregator to enable Messaging between ViewModels. To enable Messaging in CaliburnMicro the subscriber should Implement IHandle interface. You can see in the above code I implemented IHandle interface and subscribed to ImageSelectionChanged. Any publisher who publish ImageSelectionChanged message will be notified to TabViewModel.
The custom Control to display Image is not in MVVM structure.
You can find the Image listing control and other piece of codes in the uploaded source code.
You can download the code here