Silverlight+WPF

Blog d'Alexandre Arnaudet et de ses collègues chez CLT-Services autour de WPF, de Silverlight et des RIA

Recent posts

Tags

Categories

Navigation

Pages

    Archive

    Blogroll

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    Glissez des fichiers dans votre application Silverlight depuis l’explorateur windows

    Depuis la version 4 de Silverlight, vous avez la possibilité de glisser un ou plusieurs fichiers depuis l’explorateur windows vers une application Silverlight. Nous allons voir ensemble comment procéder.

    Ce billet sera découpé en deux parties:

    1. La première utilisera un event handler créé dans le code behind pour répondre à l’event Drop
    2. Dans le second cas nous verrons comment implémenter une Commande pour utiliser cette fonctionnalité dans une utilisation avec le pattern MVVM

    1. Event Handler - CodeBehind

    Nous voulons une application dans laquelle nous avons la possibilité de glisser des images au format jpg.

    La première chose à faire est d’ajouter AllowDrop=”True”, sur l’élément où l’on désire glisser le ou les fichiers. La seule contrainte ici est que l’élément soit un UIElement.

     <Grid x:Name="LayoutRoot" AllowDrop="True" Drop="LayoutRoot_Drop">
    

    Comme vous pouvez le remarquer nous devons également nous abonner à l’évènement Drop. Dans notre cas le handler se nomme LayoutRoot_Drop et se trouve dans le code behind de notre UserControl.

    private void LayoutRoot_Drop(object sender, DragEventArgs e){}

    Le handler prend deux paramètres:

    • Le sender correpondant ici à notre Grid
    • Et un eventArgs DragEventArgs qui contient les données de DragAndDrop et notamment les informations sur les fichiers.

    Via le DragEventArgs, nous récupérons un tableau de FileInfo qui permet de décrire les fichiers glissés dans l’application, dont notamment le nom de l’image et  l’extension sur laquelle nous appliquons un filtre pour ne récupérer que les images au format jpg.

    FileInfo[] files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
    if (files != null)
    {
        foreach (var fileInfo in files)
        {
            if (fileInfo.Extension == ".jpg")
            {
            }
        }
    }

    Avant d’aller plus loin, retournons dans le code xaml afin d’ajouter un contrôle pour afficher les images. Une simple ListBox est suffisante.

     <ListBox SelectionMode="Multiple" HorizontalAlignment="Stretch" 
       VerticalAlignment="Stretch" x:Name="ImagesList" Height="500" >
       <ListBox.ItemsPanel>
         <ItemsPanelTemplate>
          <toolkit:WrapPanel Orientation="Horizontal"  UseLayoutRounding="True"
                          Width="1024"  />
         </ItemsPanelTemplate>
       </ListBox.ItemsPanel>
       <ListBox.ItemTemplate>
        <DataTemplate>
           <Image Width="200" Stretch="Uniform" Source="{Binding}" />
        </DataTemplate>
       <ListBox.ItemTemplate>
    </ListBox>
    

    Nous commençons par lui donner un nom pour y accéder depuis le code behind: ImageList

    Au niveau du ItemsPanelTemplate, nous mettons un WrapPanel provenant du toolkit silverlight, pour que les éléments retournent automatiquement à la ligne une fois la largeur maximale du composant atteinte.

    Enfin, nous modifions le DataTemplate pour indiquer que chacune de nos images aura une largeur maximum de 200, tout en gardant une taille uniforme.

    Pour chaque FileInfo, nous allons créer un BitmapImage et l’ajouter dans une collection que l’on va assigner à la propriété ItemsSource de notre ListBox.

    using (var fileStream = fileInfo.OpenRead())
    {        
        var bitmapImage = new BitmapImage();
        bitmapImage.SetSource(fileStream);                     
        ImagesCollection.Add(bitmapImage);          
        fileStream.Close();
    }

    La collection est de type ObservableCollection. L’avantage d’utiliser ce type de collection est qu’elle va notifier l’interface, dès  qu’une nouvelle BitmapImage est ajoutée ou supprimée.

    Glissez quelques images de votre explorer et vous devriez obtenir le résultat suivant dans votre navigateur web

    Capture

     

    2. Avec MVVM

    Nous allons partir du principe que vous avez déjà mis en place le pattern MVVM dans votre application. Le but ici n’est pas d’expliquer à quoi sert et comment marche MVVM, ça serait hors sujet, mais plutôt de montrer comment utiliser le Drop dans ce cas d’utilisation.

    Silverlight 4 implémente maintenant de manière native la gestion des commandes sur les éléments comme le Button.

    Cependant nous avons ici un besoin spécifique, qui est le déclenchement de la commande dès qu’un ou des éléments sont posés sur notre Grid.

    Une solution possible serait d’étendre le Grid actuel, en lui donnant la possibilité de déclencher une commande lors d’un Drop. Le problème avec ce model, c’est que pour chaque contrôle où vous souhaitez exécuter une commande Drop, vous devez l’étendre. Vous allez vite vous retrouver avec un nombre important de contrôles dans votre projet.

    Deuxième solution: utiliser le MVVM Light Toolkit de Laurent Bugnion http://www.galasoft-lb.ch/mvvm/getstarted/, qui met à notre disposition un EventToCommand behavior. Voici le lien vers son tutorial: http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx. Le principe de base c’est de pouvoir spécifier pour un contrôle donné, l’event qui va jouer le rôle de trigger pour la commande.

    Pour notre exemple, nous allons nous tourner vers une solution fonctionnelle uniquement pour utiliser le Drop Event via une propriété attachée.

    Description de la solution:

    Une commande est représentée par son interface ICommand, point de communication entre l’UI et le ViewModel lors de l’action utilisateur.  Un type de commande que l’on retrouve régulièrement est la DelegateCommand, également appelée RelayCommand. Son rôle est de déléguer la logique métier que l’on avait mis dans le code behind et donc dans la vue, dans une méthode du ViewModel. Cette méthode peut également contenir un argument, DragEventArgs dans notre cas.

    Code de la DelegateCommand:

    public class DelegateCommand : ICommand
       {
           private Func<object, bool> canExecute;
           private Action<object> executeAction;
           bool canExecuteCache;
    
    
           public DelegateCommand(Action<object> executeAction)
               : this(executeAction, null)
           { }
    
           public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute)
           {
               this.executeAction = executeAction;
               this.canExecute = canExecute;
           }
    
           #region ICommand Members
           public bool CanExecute(object parameter)
           {
               bool temp = canExecute(parameter); 
               if (canExecuteCache != temp)
               {
                   canExecuteCache = temp;
                   if (CanExecuteChanged != null)
                   {
                       CanExecuteChanged(this, new EventArgs());
                   }
               }
    
               return canExecuteCache;
           }
    
           public event EventHandler CanExecuteChanged;
    
           public void Execute(object parameter)
           {
               executeAction(parameter);
           }
           #endregion
       }

    Maintenant que nous avons une ICommand, nous allons créer une class Helper avec une propriété attachée nommée DropCommandProperty.

    public class DropHelper
    {
       public static ICommand GetDropCommand(DependencyObject obj)
       {
           return (ICommand)obj.GetValue(DropCommandProperty);
       }
    
       public static void SetDropCommand(DependencyObject obj, ICommand value)
       {
           obj.SetValue(DropCommandProperty, value);
       }
    
       public static readonly DependencyProperty DropCommandProperty =
         DependencyProperty.RegisterAttached("DropCommand",
         typeof(ICommand), typeof(DropHelper),
         new PropertyMetadata(new PropertyChangedCallback(DropCommandChanged)));
    
       private static void
        DropCommandChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
          {
           UIElement element = d as UIElement;
    
           if (element == null)
             throw new Exception("Expected UIElement Type");
    
           if (!element.AllowDrop)
             throw new Exception("AllowDrop Property must be equal to true");
             element.Drop += (s, ev) =>
             {
               ICommand cmd = e.NewValue as ICommand;
    
                   if (cmd != null)
                       cmd.Execute(ev);
             };
          }
      }

    Notez qu’on ajoute une méthode callback DropCommandChanged, de manière à vérifier que la commande est utilisée sur un UIElement et que la propriété AllowDrop a bien été mise à true. On s’abonne également sur l’évènement Drop qui une fois déclenché va exécuter la commande en passant en paramètre le DragEventArgs contenant les données de DragAndDrop.

    Dans la pratique pour l’utiliser, vous avez deux choses à faire.

    Modifier votre ViewModel de manière à ce qu’il fournisse une propriété retournant un ICommand et une méthode private, appelée suite à l’exécution de la commande avec exactement le même code que ce qu’on avait précédemment dans le code behind. Ne pas oublier également d’ajouter une ObservableCollection afin que la ListBox se Binde directement dessus.

    public class HomeViewModel
    {
        private ICommand dropCommand;
    
        public HomeViewModel()
        { 
        }
    
        public ICommand DropCommand
        {
            get
            {
                if (dropCommand == null)
                    dropCommand = 
                        new DelegateCommand(param => Drop(param as DragEventArgs));
    
                return dropCommand;
            }
        }
    
        private ObservableCollection<BitmapImage> imagesCollection = 
            new ObservableCollection<BitmapImage>();
        public ObservableCollection<BitmapImage> ImagesCollection
        {
            get { return imagesCollection; }
        }
    
    
        private void Drop(DragEventArgs e)
        {
            if (e == null)
                throw new ArgumentNullException("e");
            if (e.Data != null)
            {
         FileInfo[] files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
    
                   if (files != null)
                   {
                       foreach (var fileInfo in files)
                       {
                           if (fileInfo.Extension == ".jpg")
                           {
    
                               using (var fileStream = fileInfo.OpenRead())
                               {
    
                                   var bitmapImage = new BitmapImage();
                                   bitmapImage.SetSource(fileStream);
    
                                   ImagesCollection.Add(bitmapImage);
    
    
                                   fileStream.Close();
                               }
                           }
                       }
                   }
               }
           }
       }

    Puis, modifier le code XAML de votre page en y ajoutant une référence vers la librairie contenant le helper, ici le projet courant.

      xmlns:helper="clr-namespace:FileDrop"
    

    Et en faisant le Binding sur la commande et sur la collection de BitmapImage

     <Grid x:Name="LayoutRoot" AllowDrop="True" helper:DropHelper.DropCommand="{Binding DropCommand}"
     <ListBox ItemsSource="{Binding ImagesCollection}"  
    

    Et voilà le tour est joué.

    Posted: May 08 2010, 20:52 by Alexandre Arnaudet | Comments (10) RSS comment feed |
    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    Comments

    Canyonlands National Park HD Wallpaper United States said:

    Hey check out my blog too. I hope i have some cool stuff too

    # November 25 2010, 23:00

    Facebook statuses United States said:

    Great site design!!!! Whattemplate did you use?

    # November 26 2010, 05:18

    Emo United States said:

    Hey check out my blog too. I hope i have some interesting stuff too

    # November 26 2010, 10:00

    Compact refrigerator United States said:

    Thanks for posting this. i really had good time reading this.

    # November 26 2010, 14:45

    Cross cutting shredder United States said:

    This site is cool. i visit here evaryday.

    # November 26 2010, 19:36

    Slik pro 700dx professional tripod with panhead United States said:

    This site is cool. i visit here evaryday.

    # November 27 2010, 00:36

    My 5 current top makeup trickstips a variety that i use United States said:

    This site is great. i visit here evaryday.

    # November 27 2010, 11:17

    Charleston Cruise United States said:

    Hi, I can’t understand how to add your site in my rss reader. Can you tell me what I'm doing wrong, please.

    # November 29 2010, 23:45

    Carnival Cruises 2011 United States said:

    I am always looking for good information on this topic. It can be difficult to find sometimes. Thanks!. I will check back on your site from time to time to see what else you have to offer.

    # November 29 2010, 23:50

    Holland America Cruises 2011 United States said:

    I found your blog on Digg today and really liked it.. i bookmarked it and will be back to check on some more posts later ..

    # November 29 2010, 23:56