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:

Thursday, 21 April 2011

How to load and bind Image in MVVM (1)

In last post, I briefly described how to load image in run time and bind it to the ViewMode, now I will explain in detail how to do it.


  1. Create a Silverlight Application name it "ImageLoadDemo", see the screen shot:














  2. Create two folders in ImageLoadDemo project, naming them View and ViewModel
    1. In View folder, add a user control named ImageLoad.xaml
    2. inViewModel folder, add a class called ImageLoadViewModel.cs, see the screen shot:










  3. Open the ImageLoad.xaml user control in Visual Studio 2010 and add 3 Rows and 3 Columns to the Grid "LayoutRoot", and put a button in the middle cell, setting its width and length to be full, code 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:      
       9:      <Grid x:Name="LayoutRoot" Background="White">
      10:          <Grid.ColumnDefinitions>
      11:              <ColumnDefinition Width="90*" />
      12:              <ColumnDefinition Width="230*" />
      13:              <ColumnDefinition Width="80*" />
      14:          </Grid.ColumnDefinitions>
      15:          <Grid.RowDefinitions>
      16:              <RowDefinition Height="79*" />
      17:              <RowDefinition Height="146*" />
      18:              <RowDefinition Height="75*" />
      19:          </Grid.RowDefinitions>
      20:          <Button Content="Button" Grid.Column="1" Grid.Row="1" Name="button1" />
      21:      </Grid>
      22:  </UserControl>

Tuesday, 19 April 2011

How to load and bind Image in MVVM?

I was getting overwhelmed today by a little issue, very little, but enough to waste you several hours, -- the problem is, I have an application  using MVVM, and I have an Image binded to the ViewModel, codes is here:

XAML:

   1:  <Button Height="119" Width="128" BorderBrush="#FFF51B1B" BorderThickness="2" Canvas.Left="413" Command="{Binding ChoosePhotoCommand}" Background="White">
   2:      <Image x:Name="imgPhoto" Source="{Binding MyImage}" />
   3:  </Button>

In the ViewMode, I have a Property MyImage like this:


   1:   Image myImage;
   2:   public Image MyImage
   3:   {
   4:       get { return myImage; }
   5:       set { myImage = value; RaisePropertyChanged("MyImage"); }
   6:   }

The ChoosePhotoCommand Command's code is like this:



   1:  private RelayCommand choosePhotoCommand;
   2:  public RelayCommand ChoosePhotoCommand
   3:  {
   4:      get { return choosePhotoCommand; }
   5:      set
   6:      {
   7:          choosePhotoCommand = value;
   8:          RaisePropertyChanged("ChoosePhotoCommand");
   9:      }
  10:  }

you can see that I am using RelayCommand which is MVVMLight framework, but it's really the same as the normal .net ICommand onject

so in construction, i assign the value for this command, like this:


   1:   public MyViewModel()
   2:   {
   3:       ChoosePhotoCommand = new RelayCommand(choosePhoto, canExecuteChoosePhoto);
   4:   }



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


OK, now if I select any photo from disk(first you have to make sure you are running in Out of Browser model), the binded Image doesn't change, it keep blank, what's wrong?

the problem is here:

2:      <Image x:Name="imgPhoto" Source="{Binding MyImage}" />

You can't bind an Image's Source property to another Image object, that doesn't make sense, you have to bind the Source to another Source, modify the code like this:

2:      <Image x:Name="imgPhoto" Source="{Binding MyImage.Source}" />
this time, the binding is ok. run the application, and everything is as expected

Tuesday, 12 April 2011

I am going to teach you how to create a Silverlight Wizard Control

I created a Wizard Customer Control by using Silverlight 4, the procedure is quite interesting, I have decided to write it down, to teach you step by step how to build an Applied Wizard Control using Silverlight

the screenshot is like this



As I know so far, there is no such control in Silverlight Toolkit or Telerik Controls

I will continue this post this evening....