Friday, 20 May 2011

Silverlight Wizard Custom Control for sharing

I created a lightweight Silverlight Wizard Custom Control, you can download the dll here:
I also provide a sample project showing you how to use the Wizard dll in your real project, you can download the demo project here:

You can view the live Demo here:

If you want to introduce this control to someone else, please reference my Blog post

Now I briefly introduce how to use this control,
First: you have to reference this Wizard.dll to your project;
Second: you have to host the Wizard in some container, either in a Child Window or just a User Control. I prefer Child Window.
I assuming you are hosting the Wizard in a Child Window

Third: add the following XML namespace: to the Child Window's XAML file:
xmlns:wizard="clr-namespace:Wizard;assembly=Wizard"

Delete the OK and Cancel button, rewrite the XAML codes like this and be careful the core parameters need to be set are
ImagePathUri
TitleText

<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<wizard:ChunXuWizard
x:Name="createUserWizard"
Grid.Row="0"
Margin="0,-2,0,0"
ImagePathUri="/SilverlightApplication18;component/Images/people.png"
TitleText="Add a new student"
Loaded="createUserWizard_Loaded"
/>
</Grid>

please make sure the image /SilverlightApplication18;component/Images/people.png exist in the Image folder, otherwise Wizard will not show anything in the image block(it's not a big deal, isn't it?)

than in the code-behind event method ... you have to insert the pages which the Wizard would like to slide, I created 4 UserControls and I list the codes here:
ObservableCollection<Control> t = new ObservableCollection<Control>();
t.Add(new StudentDataInputBasicInfomation());
t.Add(new StudentDataInputBasicInfomation2());
t.Add(new StudentDataInputBasicInfomation3());
t.Add(new StudentDataInputBasicInfomation4());
this.createUserWizard.NormalControls = t;
Fourth, Create a new Child Window in your code and show the window, you can see the cool slide Wizard living in your screen, like these screen shot:

Click Next Button




The page will slide to the Next page, and by clicking the Back Button, you can go to previous page, the data will be kept.

Next time, I will introduce some detail of how to use the control.

Enjoy the silverlight

Thursday, 19 May 2011

Telerik Silverlight RadGridView ComboBox Column data binding problem

I am using telerik Silverlight GridView control, one of the cool features is that it has a ComboBox Column, which can enable the developer embad a ComboBox in GridView's column
like this:









While of course you can embad such a ComboBox in regular DataGridView(i mean, in micorsoft silverlight toolkit), but Telerik makes things much eazier..

But if you try to bind data through ViewMode to the ComboBox Column of the RadGridView, you will encounter some serious problem.

Let's design a lightweight project to test this cool features of Telerik(this has be existing for long time, I don;t know why Telerik guys doesn't fix it for so long time)

suppose you have two set of entities, People and Country, like this

public class People
{
public int CountryID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

public class Country
{
public int Id { get; set; }
public string Name { get; set; }
}

and we has two set of collections which will be used to bind with the RadGridView and ComboBox column in the GridView

private ObservableCollection<People> peoples = new ObservableCollection<People>();
public ObservableCollection<People> Peoples
{
get { return peoples; }
set { peoples = value; }
}

private ObservableCollection<Country> countries = new ObservableCollection<Country>();
public ObservableCollection<Country> Countries
{
get { return countries; }
set { countries = value; }
}

than we assign some value to the two collections


foreach(var x in
new People[] {
new People { CountryID = 0, FirstName = "Sebastain", LastName = "Vettel" },
new People { CountryID = 1, FirstName = "Fernando", LastName = "Alonso" },
new People { CountryID = 2, FirstName = "James", LastName = "Button" }
})
{
Peoples.Add(x);
}


foreach(var x in
new Country[] {
new Country { Id = 0, Name = "Germany" },
new Country { Id = 1, Name = "Spain" },
new Country { Id = 2, Name = "UK" }
})
{
Countries.Add(x);
}

now the Peoples and Countris are ready to be binded to the XAML objects. Bind the DataContext

this.DataContext = this;

let's see the View XAML codes, the RadGridView like this:

<controls:ChildWindow

<telerik:RadGridView x:Name="radGridView"
AutoGenerateColumns="False" ItemsSource="{Binding Peoples}">
<telerik:RadGridView.Columns>
<telerik:GridViewComboBoxColumn
DataMemberBinding="{Binding CountryID}"
UniqueName="Country"
SelectedValueMemberPath="Id"
DisplayMemberPath="Name"
ItemsSource="{Binding Countries}"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding FirstName}" UniqueName="First Name"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding LastName}" UniqueName="Last Name"/>
</telerik:RadGridView.Columns>
</telerik:RadGridView>


Simple and straightforward, and let see the result, like this:









everything is fine. Now let's click the FirstName column, what you will see is like this:







the ComboBox column's content is empty, what's wrong? Is there anything coding problem?

The answer is nothing wrong, this is one of the Telerik Silverlight GridView's  famous bug.

What you have to do is to remove the ItemsSource binding in the XAML side and move the binding in the code-behind, this is ugly, meaning if you have to use MVVM you have to pass some parameters to the ViewMode(at lest the ComboBox object) and breaking the graceful of MVVM.

Let's do some change to meet Telerik's behaviour, remove the ComboBox Binding
<telerik:GridViewComboBoxColumn
DataMemberBinding="{Binding CountryID}"
UniqueName="Country"
SelectedValueMemberPath="Id"
DisplayMemberPath="Name"
ItemsSource="{Binding Countries}"/>
and add this to the code-behind:
((GridViewComboBoxColumn)this.radGridView.Columns["Country"]).ItemsSource = Countries;

Now run the application, everything is as expected.


Monday, 2 May 2011

How to create a borderless window which can be drag and move in Silverlight Out Of Browser Model?

In out of browser model, if you choose Borderless style, the TitleBar will disappear and you can't drag and move the whole window, to solve this problem there are several solution, one of them is to use
Application.Current.MainWindow.DragMove(); method,

Ok, let's create a small application to test this:

Create a Silverlight Application, make sure it will be running in out of browser model. In the MainPage UserControl's LayoutRoot Grid, add two rows and a Rectangle, fill the Rectangle's content with some solid color(here I cheese Blue) like this:
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<Rectangle Name="rectangle1" Stroke="Black" StrokeThickness="1" Fill="Blue"/>
</Grid>
The blue Rectangle will be our new TitleBar which could be used to drag and move the whole window. Let's add the core code behind.
Add a MouseLeftButtonDown Event to the Rectangle, like this:
<Rectangle Name="rectangle1" Stroke="Black" StrokeThickness="1" Fill="Blue" MouseLeftButtonDown="rectangle1_MouseLeftButtonDown"/>

Implement the event's code behind:
private void rectangle1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Application.Current.MainWindow.DragMove();
}
Now run the application(make sure it's running in out of browser and Bordless Model), you can see a bordless window with a big blue TitleBar, use mouse to drag and move the title bar, it works just as we expected

one thing I have to mention, to make the Rectangle drag and move, you have to Fill this Rectangle with something, ether you fill with some Color or some other Control. Otherwise, it will not react to the mouse click.

Now, the window could be drag and move, but how to close it? How to maxim it? Minim it? Well the answer are all in the Application.Current.MainWindow.WindowState Property The corresponding method are:
Application.Current.MainWindow.WindowState = WindowState.Maximized;
Application.Current.MainWindow.WindowState = WindowState.Minimized;
Application.Current.MainWindow.WindowState = WindowState.Normal;

Now let's add a content menu to the Blue Rectangle and add Maxim, Minim and Normal command, Like this:

<Rectangle Name="rectangle1" Stroke="Black" StrokeThickness="1" Fill="Blue" MouseLeftButtonDown="rectangle1_MouseLeftButtonDown">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem x:Name="menuMaxin" Header="Maxim" Click="menuMaxin_Click"/>
<toolkit:MenuItem x:Name="menuMinim" Header="Minim" Click="menuMinim_Click"/>
<toolkit:MenuItem x:Name="menuNormal" Header="Normal" Click="menuNormal_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</Rectangle>

To use the Content Menu, You have to add System.Windows.Controls.Input.Toolkit to your project's Reference

let's fill the code behind:
private void menuMaxin_Click(object sender, RoutedEventArgs e)
{
Application.Current.MainWindow.WindowState = WindowState.Maximized;
}

private void menuMinim_Click(object sender, RoutedEventArgs e)
{
Application.Current.MainWindow.WindowState = WindowState.Minimized;
}

private void menuNormal_Click(object sender, RoutedEventArgs e)
{
Application.Current.MainWindow.WindowState = WindowState.Normal;
}


that's, when running the application, you can Maxim, Minim and resume by clicking the content menu

this application is not looked nice, it just telling you how to control Windows behaviour in Out of Browser model, you and add some Icon on the Top-Right corner to make the application more reasonable.

Monday, 25 April 2011

How to load and bind Image in MVVM (2)

Now, In the ImageLoadViewModel.cs file, add Interface INotifyPropertyChanged and a public void OnPropertyChanged(string propertyName) method, code like this:


   1:   using System.ComponentModel;

   2:   

   3:   public class ImageLoadViewModel : INotifyPropertyChanged 

   4:   {
   5:   
   6:       public event PropertyChangedEventHandler PropertyChanged;
   7:       public void OnPropertyChanged(string propertyName)
   8:       {
   9:           if (PropertyChanged != null)
  10:           {
  11:               PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  12:           }
  13:       }
  14:   }


Next, Create a folder called Command and add a GenericCommand class in that folder, like the screenshot:









inherit ICommand interface and implement the codes like this:



   1:  public class GenericCommand : ICommand
   2:  {
   3:      private readonly Action<object> _executeAction;
   4:      private readonly Predicate<object> _canExecute;        
   5:   
   6:      public GenericCommand(Action<object> executeAction, Predicate<object> canExecute)
   7:      {
   8:          if (executeAction == null)
   9:              throw new ArgumentNullException("executeAction");
  10:   
  11:          _executeAction = executeAction;
  12:          _canExecute = canExecute;
  13:      }
  14:   
  15:      public void OnCanExecuteChanged()
  16:      {
  17:          if (CanExecuteChanged != null)
  18:              CanExecuteChanged(this, EventArgs.Empty);
  19:      }
  20:   
  21:      public bool CanExecute(object parameter)
  22:      {
  23:          if (_canExecute == null) return true;
  24:          return _canExecute(parameter);
  25:      }
  26:   
  27:      public event EventHandler CanExecuteChanged;
  28:   
  29:      public void Execute(object parameter)
  30:      {
  31:          _executeAction(parameter);
  32:      }
  33:  }


this is a Generic Command, so we can use it quite freely

Because we are going to bind a Image and a Command to the ViewModel, we have to create an Image and Command Property in ViewModel, add these two properties in ImageLoadViewModel.cs, code like these:


   1:  ICommand imageLoadCommand;

   2:  public ICommand ImageLoadCommand

   3:  {
   4:      get { return imageLoadCommand; }
   5:      set { imageLoadCommand = value; OnPropertyChanged("ImageLoadCommand"); }

   6:  }
   1:  private Image image = new Image();
   2:  public Image MyImage
   3:  {
   4:      get { return image; }
   5:      set { image = value; OnPropertyChanged("MyImage"); }
   6:  }

Next, assign imageLoadCommand the values in the constructor fonction:

   1:  public ImageLoadViewModel()
   2:  {
   3:      ImageLoadCommand = new GenericCommand(loadImage, s => true);
   4:  }
you can see there is a method called loadImage, the code is:

   1:  private void loadImage(object obj)
   2:  {
   3:      if (Application.Current.IsRunningOutOfBrowser)
   4:      {
   5:          string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
   6:          OpenFileDialog openFileDialog = new OpenFileDialog();
   7:          openFileDialog.Filter = "PNG Files (*.png;*.png)|*.png;*.png | All Files (*.*)|*.*";
   8:          openFileDialog.FilterIndex = 1;
   9:   
  10:          if (true == openFileDialog.ShowDialog())
  11:          {
  12:              System.IO.Stream stream = openFileDialog.File.OpenRead();
  13:              BitmapImage bi = new BitmapImage();
  14:              bi.SetSource(stream);
  15:              image.Source = bi;
  16:              stream.Close();
  17:   
  18:              OnPropertyChanged("MyImage");
  19:          }
  20:      }
  21:      else
  22:      {
  23:          MessageBox.Show("please choose photo from disk in Out Of Browser Model");
  24:      }
  25:  }

To select a file from the disk, you have to make sure you are in Out Of Browser model, this is Silverlight's limitation. So I add such a condition;

    3:      if (Application.Current.IsRunningOutOfBrowser)

Now, go to the LoadImage.xaml page and update the content there, 
add a static resource for the user control

   1:   <UserControl.Resources>
   2:       <vm:ImageLoadViewModel x:Key="imageLoadViewModel"/>
   3:   </UserControl.Resources>

Bind ImageLoadCommand and MyImage to the Button and the Image Content of the button

   1:  <Button Grid.Column="1" Grid.Row="1" Name="button1" Command="{Binding ImageLoadCommand}">
   2:      <Image Source="{Binding MyImage.Source}" />
   3:  </Button>

Bind Grid's DataContent to the Static Resource we just created

1:  <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource imageLoadViewModel}">

now the XAML should look like this:

   1:  <UserControl x:Class="ImageLoadDemo.View.ImageLoad"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:      mc:Ignorable="d"
   7:      d:DesignHeight="300" d:DesignWidth="400"
   8:      xmlns:vm="clr-namespace:ImageLoadDemo.ViewModel">
   9:      
  10:      <UserControl.Resources>
  11:          <vm:ImageLoadViewModel x:Key="imageLoadViewModel"/>
  12:      </UserControl.Resources>
  13:   
  14:      <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource imageLoadViewModel}">
  15:          <Grid.ColumnDefinitions>
  16:              <ColumnDefinition Width="90*" />
  17:              <ColumnDefinition Width="230*" />
  18:              <ColumnDefinition Width="80*" />
  19:          </Grid.ColumnDefinitions>
  20:          <Grid.RowDefinitions>
  21:              <RowDefinition Height="79*" />
  22:              <RowDefinition Height="146*" />
  23:              <RowDefinition Height="75*" />
  24:          </Grid.RowDefinitions>
  25:          <Button Grid.Column="1" Grid.Row="1" Name="button1" Command="{Binding ImageLoadCommand}">
  26:              <Image Source="{Binding MyImage.Source}" />
  27:          </Button>
  28:      </Grid>
  29:  </UserControl>

Before running the application, you have to set the ImageLoadDemo as the "StartUp Project", because our demo only works in Out Of Browser model, screen sot like this:


















Now run the application, and click the button, a pop up window will appear asking you to choose picture, by selecting the picture, the content of the Button will become the picture you just selected.

I hope this small project will help you understand how to assign an Image's value in runtime and some basic Command, MVVM concept

thanks for reading, 

you can download the demo project here: