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:

No comments:

Post a Comment