Print Page | Close Window

Beginners Question: how to port my YearCalendar Control to Cocktail?

Printed From: IdeaBlade
Category: Cocktail
Forum Name: Community Forum
Forum Discription: A professional application framework using Caliburn.Micro and DevForce
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=3471
Printed Date: 28-Apr-2024 at 5:15am


Topic: Beginners Question: how to port my YearCalendar Control to Cocktail?
Posted By: stipps
Subject: Beginners Question: how to port my YearCalendar Control to Cocktail?
Date Posted: 03-Jun-2012 at 3:44am
Hi,

I am a Hobby programmer and recently started to get to know the mvvm pattern with calibre.micro when i stumbled upon Cocktail.

I have a small wpf calendar application that we use in my Office to organize our Holidays, Business travels etc. To Display all the Events I created a custom yearcalendar user control, which simply calculates the days of the year, public Holidays etc. and build the Interface from the code behind file of the usercontrol.

I now try to rebuild the whole thing with Cocktail.
In General this worked nicely until now - i have a shellviewmodel that inherits from Conductor<YearCalendarViewModel>, and the YearCalendarViewModel calls the refresh method on my yearcalendar trough the IViewAware Interface.

The first time the calendar builds up nicely, no Problem. But when i try to Change the year displayed, and therefore habe to dispose all the generated controls and recreate them, the view does simply not react at all. It goes through the method that creates the calendar, but the UI does not update, it stays on the previous year. I as well tried to deactivate the YearCalendarViewModel on my ShellViewModel and add it again, but with no visible result.

Could anyone give me a hint how to resolve this, or how I should construct such a control so it works well with mvvm and Cocktail in particular?

Any help will be appreciated. I hope my english and the description of my Problem is understandable.

Regards,

Fredrik



Replies:
Posted By: mgood
Date Posted: 03-Jun-2012 at 5:09pm
The guiding rule of MVVM is that the ViewModel doesn't know the View. That's not always possible, so that's why Caliburn.Micro has OnViewLoaded through which you can get a reference to the view. This should only be used if the proper ways fail for reasons out of your control. The ViewModel doesn't need a reference to the view in general. The VM and V communicate which each other through the binding machinery. If you have your own custom control it should have custom DependencyProperties and Events that you can bind to data properties and actions in your ViewModel. You use a custom control just like a regular control. Since I don't know your custom control let me demonstrate this on a small example. In the following View we have a text box and a button. The TextBox Text DependencyProperty is two-way bound to the ViewModel and for the Button we bind the Click event to the ClearText action in the ViewModel.
<Window x:Class="SimpleMVVM.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="http://www.caliburnproject.org"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <TextBox Text="{Binding Text, Mode=TwoWay}" Width="200" Height="25" />
            <Button Content="Clear" cal:Message.Attach="[Event Click] = [Action ClearText]" Width="100"/>
        </StackPanel>
    </Grid>
</Window>
 
The corresponding ViewModel looks like this.
 
namespace SimpleMVVM
{
    [Export]
    class MainViewModel : Screen
    {
        private string _text;
 
        public string Text
        {
            get { return _text; }
            set
            {
                _text = value;
                NotifyOfPropertyChange(() => Text);
            }
        }
 
        public void ClearText()
        {
            Text = "";
        }
    }
}
 
So, whenever the Text property changes in the ViewModel, the ViewModel triggers a PropertyChanged event, which tells the TextBox to refresh. That's why we can simple assign an empty string in the ClearText action to clear the TextBox.  Also, because we have defined the binding of Text as a Two-Way binding, if you type and tab out of the TextBox, the current value is automatically assigned to the Text property in our ViewModel. This is fundamentaly how View and ViewModels communicate. The two interfaces that make this all work are INotifyPropertyChanged and INotifyCollectionChanged for collections. Screen and Conductor in Caliburn.Micro implement INotifyPropertyChanged, so you can just call the NotifyOfPropertyChange method to trigger the event. BindableCollection which extends ObservableCollection implement INotifyCollectionChanged, which will refresh the View if for example you have a list box bound to a collection in your view model and you add or remove an item from the collection.
 
Now, Caliburn.Micro defines conventions that let you write less XAML. There are no conventions out of the box for your custom control, but you could write them yourself. For the standard controls like TextBox and Button, there are conventions, so the above XAML can be simplifed.
 
<Window x:Class="SimpleMVVM.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="http://www.caliburnproject.org"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <TextBox x:Name="Text" Width="200" Height="25" />
            <Button x:Name="ClearText" Content="Clear" Width="100"/>
        </StackPanel>
    </Grid>
</Window>

Now, Caliburn.Micro will perform the binding automatically. The TextBox is named the same as the property in the ViewModel, so Caliburn.Micro automatically establishes a two-way binding, since our property has a setter. Same for the button. The button's default event, which is Click, is automatically bound to the ClearText action method.

Having said all this, in MVVM you rarely create custom controls. Instead you leverage view composition. You can see that demonstrated in TempHire. TempHire is built using a number of smaller Views and ViewModels, which at runtime are composed together to the final application.

 



Print Page | Close Window