Csharp

Le Layouting dans WPF

Dans un précédent article je parlais de la création d’un application WPF en général, dans cet article on va se concentrer sur le XAML, el langage xml qui permet de disposer les élément WPF dans la fenêtre.

Concevoir son interface en XAML

Au démarrage d’une application XAML, il y a une fenêtre vide, mais le code XAML n’est pas vide pour autant. Il y a un code minimal :

<Window x:Class="PriceGrabber.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PriceGrabber"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        
    </Grid>
</Window>
Fenêtre minimale xaml

Vous voyez une fenêtre blanche. Mais en fait i y a bien des choses en dessous.

Ajouter un StackPanel comme enveloppe de vos éléments plus petits

Sur la gauche de VisualtStudio se trouve la Toolbox, dont vous pouvez glisser-déplacer les élément vers la fenêtre au centre.

WPF Stackpanel

La fenêtre XAML se met à jour automatiquement, avec des valeurs par défaut que vous pouvez changer.

Le stackpanel héberge les éléments dans toute sa largeur.

stack panel exemple

WPF comment ajouter des élément dans une combobox?

Tout simplement en y ajoutant des comboBoxItem.

<ComboBox x:Name="comboBox">
    <ComboBoxItem Content="X"/>
    <ComboBoxItem Content="Y"/>
    <ComboBoxItem Content="Z"/>
</ComboBox>

Ajouter via le code des éléments dans une comboBox

Si vous ne connaissez pas à l’avance les éléments de la comboBox, vous pouvez le faire dynamiquement via code c#, mais c’est très fastidieux.

C# SqlServer Faire Un CRUD simplement

Parmi les choses à savoir faire absolument en programmation : dialoguer avec la base de données ! sans quoi il ne sert à rien de programmer, car on a besoin de persister (sauver) des données, et les réutiliser. Nous allons dans un premier temps entrer en contact avec la base de données, et ensuite requêter des données avec du SQL.

Faire du CRUD en C# avec SQL Server

Connexion à la base de données

Tout d’abord sachez qu’il y a deux façons de se connecter à la base de données, la connexion avec les identifiants Windows, ou la connexion avec les identifiant de l’utilisateur de la base de données

Connexion à Sqlserver avec les identifiants de Windows

using System;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.Data.SqlClient;

namespace scraper
{
    class Program
    {
        // http://lgmorand.developpez.com/dotnet/regex/
        static void Main(string[] args)
        {
            SqlConnection myConnection = new SqlConnection("Server=[myserver];Database=[DBSQL];Trusted_Connection=true");
            myConnection.Open();
           }
    }
}

Il suffit d’ajouter « Trusted_Connection=true » pour indiquer que l’on veut utiliser les identifiants de Windows.

Connexion à Sqlserver avec les identifiants utilisateur

Maintenant, la seconde façon de se connecter est d’utiliser les identifiant de connexion à la base de données de l’utilisateur créé pour la base.

using System;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.Data.SqlClient;

namespace scraper
{
    class Program
    {
        // http://lgmorand.developpez.com/dotnet/regex/
        static void Main(string[] args)
        {

            SqlConnection myConnection = new SqlConnection("Data Source=myservername;Initial Catalog=DBSQL;User ID=UserName;Password=Password");
            myConnection.Open();
           }
    }
}

Ok là c’est écrit un peu à l’arrache, mais pour bien faire, il faut bien prévoir le cas où la connexion à la base échoue, donc on va mettre dans une clause try-catch.

using System;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.Data.SqlClient;

namespace scraper
{
    class Program
    {
        // http://lgmorand.developpez.com/dotnet/regex/
        static void Main(string[] args)
        {
            SqlConnection myConnection = new SqlConnection("Server=[servdom];Database=[SAGESQL];Trusted_Connection=true");
            try {
                myConnection.Open();
            }
            catch(Exception e) {
                Console.WriteLine(E.Message);
                Console.ReadLine();
            }
       }
    }
}

Création d’une table

CREATE TABLE [dbo].[references](
  [id] int IDENTITY(1,1) PRIMARY KEY,
  [reference] [varchar](100) NOT NULL,
  [description] [varchar](50) NULL,
  [regroupement] [varchar](50) NULL
) ON [PRIMARY]

Nous allons utiliser Management Studio pour créer la table. Vous insérez ce script dans une fenêtre de Management Studio, sélectionnez le texte et faites F5 pour créer la table.

Insertion d’un enregistrement

Sans plus tarder, faison un INSERT dans la table « references », à noter que references est un mot clé dans sqlServer ! mais on peut quand même l’utiliser en l’entourant de crochet [references]

public void InsertActivity(string activity)
 {
     string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
     SqlConnection conn = GetConnexion();
     SqlCommand sql = new SqlCommand("INSERT INTO [FREZAL_HISTORY] (UserName,Date,TableName,ColumnName) VALUES (@UserName, @Date,@TableName,@ColumnName)", conn);
     conn.Open();
     sql.Parameters.Add("@UserName", System.Data.SqlDbType.VarChar);
     sql.Parameters.Add("@Date", System.Data.SqlDbType.DateTime);
     sql.Parameters.Add("@TableName", System.Data.SqlDbType.VarChar);
     sql.Parameters.Add("@ColumnName", System.Data.SqlDbType.VarChar);

     sql.Parameters["@UserName"].Value = userName;
     sql.Parameters["@Date"].Value = DateTime.Now;
     sql.Parameters["@TableName"].Value = "PriceGrabber";
     sql.Parameters["@ColumnName"].Value = activity;

     sql.ExecuteNonQuery();
     return;
 }

 

Select d’un enregistrement

public bool HasAlready(string reference)
{
    try
    {
        SqlConnection conn = GetConnexion();
        conn.Open();
        SqlCommand sql = new SqlCommand("SELECT count(*) FROM [FREZAL_PG_REFERENCES] WHERE reference = @reference", conn);
        sql.Parameters.Add("@reference", System.Data.SqlDbType.VarChar);
        sql.Parameters["@reference"].Value = reference.Trim();

        int a = (Int32) sql.ExecuteScalar();
        if (a > 0)
        {
            return true;
        } else
        {
            return false;
        }
    }
    catch(Exception e)
    {
        Console.WriteLine(e.Message);
    }

    return false;

}

 

Update d’un enregistrement

public void UpdateVendorUrl(Url url)
{
    SqlConnection conn = GetConnexion();
    SqlCommand sql = new SqlCommand("UPDATE [OMG_PG_URLS] SET url = @url WHERE id = @id",conn);
    sql.Parameters.Add("@url", System.Data.SqlDbType.VarChar);
    sql.Parameters.Add("@Id", System.Data.SqlDbType.Int);


    sql.Parameters["@url"].Value = url.url;
    sql.Parameters["@id"].Value = url.id;

    conn.Open();
    sql.ExecuteNonQuery();

    return;
}

 

Delete d’une enregistrement

public void DeleteReference(string reference)
{
    try
    {
        SqlConnection conn = this.GetConnexion();
        conn.Open();
        SqlCommand sql = new SqlCommand("delete from [OMG_PG_REFERENCES] where reference = '" + reference + "'",conn);
        sql.ExecuteNonQuery();
        sql.Dispose();
        conn.Close();
    } catch(Exception e)
    {
        Console.WriteLine(e.Message);
        Console.ReadLine();
    }
}

 

csharp green logo

C# écrire un programme qui s’exécute à intervalles réguliers

 

Utiliser Timer pour exécuter à interval régulier une méthode

Il faut utiliser l’objet Timer :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace returning
{
    class Program
    {
        static void Main(string[] args)
        {
            Timer timer = new Timer(TimerCallback, null,0,5 * 1000);// five seconds
            Console.ReadLine();
        }

        private static void TimerCallback(object sender)
        {
            Console.WriteLine("il s'est écoulé 5 secondes");
           
        }


    }
}

 

csharp green logo

NewtonSoft la librairie très puissante pour manipuler les chaines JSON

Librairie NewtonSoft pour manipuler les JSON

En C# vous disposez d’une très puissante librairie pour manipuler les chaines et objets JSON. Pour utiliser cette librairie, aidez vous de Nuget pour la télécharger dans votre projet.

Certes .Net possède des méthode pour manipuler les JSON, mais ce n’est pas très pratique. Il est vivement conseillé d’utiliser NewtonSoft.

Cette librairie sert à convertir une chaine JSON en un objet C# s’il existe une classe équivalente. On l’utilise principalement pour lire les informations d’une chaîne JSON.

Nous allons voir comment transformer une chaîne JSON en un objet C# manipulable facilement, comment extraire un tableau d’objets JSON, la  librairie LINQtoJSON permet de traverser un objet construit à partir d’une chaine JSON.

Nous allons partir d’une chaine JSON assez représentative de ce qu’on peut trouver dans la vraie vie, voici ci-dessous une chaine JSON extrait de Mailchimp.

Il existe un outil en ligne qui permet de visualiser le JSON sérialisé, sous forme hiérarchique, très utile pour vous aider à voir plus clair dans une chaine JSON que vous ne connaissez pas => Online JSON Viewer.

Obtenir la librairie NewtonSoft avec Nuget dans Visual Studio

Clickez avec le bouton droit de la souris sur le projet, « Gérer les packages Nuget », tapez le nom « Newtonsoft », et installez. Normalement dans le projet courant une référence à cette librairie est faite.

Obtenir une chaîne JSON (Mailchimp dans notre cas)

Mailchimp a sans doute l’un des services web REST les plus plus facile à utiliser. Avec lui pas besoin d’authentification Oauth2, il suffit d’obtenir une clé secrète (à ne pas dévoiler ! donc exit les usages avec du Javascript côté client).

Vous pouvez télécharger le fichier JSON échantillon pour faire des tests.

Copiez le contenu et allez sur le site Online JSON Viewer et visualisez le contenu de la chaine.

Une fois l’arbre reconstitué, vous pouvez naviguer dans le contenu. On voit un tableau de JSON « lists ».

Chinae JSON

On continue à inspecter le contenu, nous pouvons voir la structure qui s’affiche clairement, les éléments de ce tableau sont des JSON, en langage C# ce sont des IEnumerable, dont les objets pourront être appelé List. Mais comme en C# List est déjà existant, ici on va plutôt appeler MailChimpList. Cette MailchimpList contient des propriétés dont certaines sont des objets (Contact, CampaignDefault,Modules,Stats, Links).

chaine JSON

Bonus : en C# tout est typé, donc il y a des classes pour tout. Dans le cas de Mailchimp, vous recevez le JSON mais ce serait bien de convertir ça en objet, le problème c’est que bien que vous connaissiez la structure du JSON qui vous permettra de créer vos classes, c’est super fastidieux, il existe un site (et sans doute des librairies) qui transforme votre JSON sérialisé en objet C# !

Voici le site qui permet les conversions en ligne :  http://json2csharp.com/

Créer des définitions de classe à partir des chaînes JSON

Objectif : créer une définition de classes pour pouvoir convertir la chaine JSON en objet C# afin de travailler dans le monde de la programmation orientée objet. En effet notre objet MailchimpList pourrait être comme ci-dessous :

MailchimpList{

    Contact contact {get;set;}
    CampaignDefaults campaignDefault {get;set;}
    List<Modules> listModules {get;set;}
    Stats stats {get;set;}  
    List<Link> listLinks {get;set;} 

}

Donc ce qu’il faut faire c’est copier coller un élément de la liste « lists » et non les 6 éléments, et coller dans l’interface du site de conversion.

 

Et voilà magique non? C’est quand j’étais à deux doigts de coder à la main les classes que je me suis mis à chercher une solution qui le ferai pour moi sans efforts. L’image ci-dessus n’est qu’un extrait pour vous montrer.

Parsons la chaîne JSON avec les méthodes de la librairie NewtonSoft

La librairie est riche en méthodes, et l’on peut se perdre facilement, car il y a plusieurs façons de faire les choses, mais essayons de lister des méthodes courantes méthodiquement pour ne pas perdre de temps à chercher.

Dans la première image on voit dans le JSON qu’il peut y avoir deux type de choses : un tableau de JSON, des propriétés.

Conversion de base en JObject

La méthode la plus générique, convertir n’importe quel JSON en JObject. C’est à dire que quelque soit le contenu du JSON (propriété + tableau), ça marche.

JObject o = JObject.Parse(jsonString);

jsonString est une chaîne de caractère qui contient

Désérialisation partielle (partial deserialization)

Très pratique, imaginons que vous ayez un gros objet JSON, amis qu’il n’y a qu’un partie de cet objet JSON qui vous intéresse, grâce à JSONtoLINQ, vous pourrez accéder à une propriété de cet objet JSON et le parser, vous évitant ainsi d’écrire des classes inutilement.

JObject o = JObject.Parse(jsonString)
List<JToken> list = o["lists"].Children().ToList();

Ici on accède à une propriété particulière du JObject, prenons les élément du tableau et on les transforme en List, via la notation Linq.

Sérialiser un JSON

La sérialisation est le contraire de la désérialisation, elle consiste à transformer un objet en une chaine de caractères afin de pouvoir la transporter ou la stocker. En général, des guillemets sont ajoutés.

Product product = new Product();

product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };

string output = JsonConvert.SerializeObject(product);

//{
//  "Name": "Apple",
//  "ExpiryDate": "2008-12-28T00:00:00",
//  "Price": 3.99,
//  "Sizes": [
//    "Small",
//    "Medium",
//    "Large"
//  ]
//}

 

Convertir un JSON Array en « IEnumerable » avec JArray

Pratique lorsque qu’une propriété du JSON est un tableau et que l’on souhaite extraire tous les éléments du tableau.

Si vous disposez d’un tableau de JSON, vous pouvez le transformer en JArray en une seule commande

string json = @"['Small','Medium','Large']";
JArray a = JArray.Parse(json);
var z = a.First().GetType();
Console.WriteLine(a.ToString());
// [
//   "Small",
//   "Medium",
//   "Large"
// ]

SI vous survolez la variable z, vous aurez le type d’un élément du tableau, qui est un JToken.

Last thing : JProperty

JProperty représente une propriété d’un objet JSON.

En notation JSON on met les clé:valeur de cette façon :

{
   nom:"Dupont",
   prenom:"Yves",
   age:34,
   voiture:[{marque:"alfa Romeo",modele:"Giulietta"},{marque:"BMW",model:"Z4"}],
   hobby:["sport","informatique"]
}

La propriété nom a pour valeur « Dupont ». La méthode Properties() permet d’extraire les propriétés, utile si vous voulez sélectionner une en particulier ou tester la présence d’une propriété.

foreach (JProperty property in o.Properties())
{
    Console.WriteLine(property.Name + ":" + property.Value);
}

// sortie console

nom:Dupont
prenom:Yves
age:34
voiture:[
 {
 "marque": "alfa Romeo",
 "modele": "Giulietta"
 },
 {
 "marque": "BMW",
 "model": "Z4"
 }
]

 

JToken, JObject, JArray ?

JToken est une classe abstraite qui sert de base  aux classes JObject, JArray, JProperty et JValue qui sont des données.

Caster un JObject en un objet C#

La partie la plus intéressante ! Enfin nous allons convertir en objet C# la chaine JSON. Pour ce faire on va utiliser JSONConvert.

#j est un objet de type JToken


JObject o = JObject.Parse(Jsonstring);
List<JToken> list = o["lists"].Children().ToList();

List<MCList> mcList = new List<MCList>();

foreach(JToken j in lists){
        mcList.Add(JsonConvert.DeserializeObject(j.ToString()));
    }

En plaçant un espion, nous pouvons voir le résultat de la conversion et le résultat est étonnant :

visual studio espion

Pour un conversion fortement typée, il se peut que le snippet ci-dessus ne marche pas, or ce lien explique qu’il faut la syntaxe suivante :

JObject o = JObject.Parse(Jsonstring); 
List<JToken> list = o["lists"].Children().ToList(); 
List<MCList> mcList = new List<MCList>(); 
foreach(JToken j in lists){     
    mcList.Add(JsonConvert.DeserializeObject<MCList>(j.ToString()));
}

 

La conversion est parfaite ! Nous retrouvons toutes les valeurs de la chaines JSON, la fonction de desérialisation est vraiment puissante ! nous pouvons travailler dans notre univers C# comme si de rien n’était.

csharp green logo

WPF : Retrouver l’index d’une cellule

WPF est un framework MVVM, pour ceux ayant fait du Angular, c’est la même approche. Il y a la View, le Model et le ViewModel.

Le ViewModel et en quelquesorte l’objet qui contient les données et est lié à la vue (View), vient tout de suite la notion de binding (unidirectionnel ou bidirectionnel. Le binding est la lisaison qui assur la synchronisation des valeurs entre le ViewModel et la vue.

Binding bidirectionnel

Toute modification d’une valeur d’un champs texte aura une répercussion sur la même valeur dans le ViewModel. Et toute modification de valeur dans le ViewModel (via du code bien sûr puisque l’utilisateur n’a pas accès au ViewModel) aura une répercussion dans la vue.

Dans AngularJS, on a souvent parlé de cette notion de binding bidirectionnel car c’est ce qui a fait vendre ce framework.

Le problème posé en WPF avec la Datagrid

Le Datagrid est un peu comme une feuille de calcul Excel, c’est un ensemble de lignes et de colonnes. On a souvent besoin de déclencher une action (d’édition par exemple) quand on clique ou double clique sur une cellule, éditer l’entité qui contient cette cellule et mettre à jour dans la base de données cette entité.

La difficulté (ou non) est de retrouver l’id de cette rangée quand on a capturé l’entité. Sur le Net, j’ai vu des tuto alambiqués, qui font peur par leur complexité. En fait c’est plus simple que ça en a l’air. Ceci à condition d’avoir bien balisé  le noeud XAML avec un identifiant unique. Cet identifiant unique, on va le faire intelligemment c’est l’id dans la table de la base de données.

Supposons que vous ayez bindé une List<Url> à Datagrid.ItemsSource, tous vos Url sont mappés aux rangées de la Datagrid. Et chaque propriété de Url est dans une cellule de la rangée.

Utiliser la classe Helper VisualTreeHelper

Cette classe a plusieurs méthodes qui vous aident à traverser le visual tree de votre application WPF, on peut faire le parallèle avec une page HTML. Donc ça peut être très compliqué. Nos allons utiliser la méthode GetParent, qui va retourner le parent de la cellule.

Exemple de pistage de la cellule cliquée

Soit le code XAML suivant :

<TabItem Header="Visualisation des Urls">
    <DataGrid x:Name="ListeUrls" AutoGenerateColumns="False" Margin="1,0,-1,27" >
        <DataGrid.Resources>
            <Style TargetType="{x:Type DataGridCell}">
                <EventSetter Event="MouseDoubleClick" Handler="DataGridCell_MouseDoubleClick"/>
            </Style>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Id}" Visibility="Hidden"></DataGridTextColumn>
            <DataGridTextColumn Header="Vendor" Binding="{Binding Vendor}" Foreground="red" FontWeight="Bold" ></DataGridTextColumn>
            <DataGridTextColumn Header="Url" Binding="{Binding url}" ></DataGridTextColumn>
            
        </DataGrid.Columns>
    </DataGrid>
</TabItem>

On y met un DataGridTextColumn qui contient l’Id, qu’on a bindé, et on le met caché.

Un double Click sur la rangée va nous retourner un objet DataGridCell

private void DataGridCell_MouseDoubleClick(object sender, MouseButtonEventArgs e)
       {
           var dataGridCellTarget = (DataGridCell)sender;
           var parent = VisualTreeHelper.GetParent(dataGridCellTarget);
           .....
       }

La DataGridCell ne contient que la valeur de la cellule (plus d’autres paramètres), en mode débuggage lorsque vous inspectez la valeur de parent, vous verrez les 3 propriétés (Id,Vendor,url), dans la propriété DataContext de parent. Bingo ! Ce DataContext contient le nom de la classe Url.

 

WPF

Les boites de dialogue en WPF : MessageBox

Boite de dialogue en WPF avec MessageBox

 

Ah les boites de dialogue ! Elles sont incontournables en informatique, elles permettent à l’application de … dialoguez avec l’utilisateur. Ce peut être un simple message avec un seul bouton pour fermer le fenêtre, ou une demande de confirmation avec deux boutons Ok ou Annuler, mais ça peut être plus élaboré.

Une premier exemple de MessageBox simple

On va supposer que la boite de dialogue apparait suite à un clic droit de la souris sur une cellule, donc on va mettre le code suivant dans une méthode :

private void TextBlock_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
    MessageBoxResult result = MessageBox.Show("Voulez vous effacer cet enregistrement?", "Ask for delete", MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.No);
    switch (result)
    {
        case MessageBoxResult.Yes:
        statusText.Text = "Suppression acceptée !";
            break;

        case MessageBoxResult.No:
            statusText.Text = "Suppression refusée !";
            break;

        case MessageBoxResult.Cancel:
            statusText.Text = "Suppression annulée!";
            break;
    }
}

Nous avons la boite de dialogue suivante assez complète :

WPF MessageBox

En effet, la méthode MessageBox.Show (qui est une méthode static) prend dans l’exemple 5 arguments. Le minimum est de un argument. Voici par ordre de décroissance les possibilité de personnalisation de la MessageBox

     
MessageBoxResult result = MessageBox.Show("Voulez vous effacer cet enregistrement?", "Ask for delete", MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.No);

MessageBoxResult result = MessageBox.Show("Voulez vous effacer cet enregistrement?", "Ask for delete", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);

MessageBoxResult result = MessageBox.Show("Voulez vous effacer cet enregistrement?", "Ask for delete", MessageBoxButton.YesNoCancel);
         
MessageBoxResult result = MessageBox.Show("Voulez vous effacer cet enregistrement?", "Ask for delete");

MessageBoxResult result = MessageBox.Show("Voulez vous effacer cet enregistrement?");

 

WPF

Premiers pas avec WPF dotNet

WPF est un framework pour développer des application Windows en client lourd. Donc si vous venez du monde du web, ça va vous changer, en effet ici, il n’est plus question de faire une application web, on est dans le traditionnel desktop. Même si le web a le vent en poupe, il ne faut pas oublier les application lourdes, et puis si vous êtes un programmeur digne du nom, vous serez assez curieux de voir comment on fait des application qui « tournent » sous Windows !

WPF c’est quoi?

WPF Windows Presentation Foundation est un framework pour faire des applications graphique. Il contient des objets pour faire des fenêtres, des boutons, des scroll, bref tout ce qui est visuel. En plus de cela il contient les éléments pour associer des données et les afficher dans les éléments visuel, et enfin tout ce qu’il faut pour détecter des actions quand on clique sur certains éléments comme des boutons.

Anatomie d’une application WPF

Lorsqu’on crée un application WPF dans VisualStudio (il n’y a pas d’autres éditeurs en dehors, sauf celui de Jetbrain), on a une série de fichiers créés, dont deux notable, le fichier par défaut MainWindow.xaml et MainWindow.xaml.cs.

MainWindow.xaml est un fichier xml spécifique à WPF qui détaille le visuel et aussi le fonctionnel (pour l’heure on se suffira du visuel) de l’application. Une hiérarchie sous forme d’arbre pour visualiser le déroulé des composants, un peu à la manière du DOM d’un navigateur web.

<Window x:Class="LearnWPF.ItemsControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="ItemsControlPanelSample" Height="150" Width="250">
        <Grid Margin="10">
        </Grid>
</Window>

Puis voici le fichier MainWindow.xaml.cs qui est un fichier de code C#, appelé code behind, c’est le code métier en back office qui va faire fonctionner l’application WPF.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace LearnWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class myItemsControl : Window
    {
        public myItemsControl()
        {
            InitializeComponent();
        }
    }
}

Il est important de voir la correspondance entre le fichier xaml et le fichier C#.

Le namespace qui est ici LearnWPF, que l’on peut voir dans le xaml, avec la notation pointée suivi du nom de la classe, LearnWPF.myItemsControl.

Dans le fichier code behind, on retouve ces références :

namespace LearnWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class myItemsControl : Window
    {
        public myItemsControl()
        {
            InitializeComponent();
        }
    }
}

On retrouve le namespace, la classe myItemsControl qui hérite de Window pour bénéficier de propriétés et méhodes, et aussi le constructeur.

Je poursuis l’exemple en enrichissant le fichier XAML afin d’expliquer certaines notions dans WPF.

Les ItemsControls dans WPF

C’est une notion fondamentale, ce sont les listbox, treeview, et les classes filles des ItemsControl sont les status bar, toolbar, menu.

Les ControlsPanel : qui dit panel dit layout

Ce sont les controls qui vont placer les autres éléments dans la fenêtre,autrement dit ils servent à faire le layout de l’application : Stackpanel (souvent utilisés), wrapPanel, Grid,TabPanel, Canvas.

La notation pointée dans le XAML

On peut faire beaucoup de choses dans le fichier XAML et j’ai mis un peu de temps à comprendre la notation pointée dans certains tags. Exemple ci-dessous :

<ItemsControl>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Content="{Binding}" Margin="0,0,5,5" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <system:String>Item #1</system:String>
                <system:String>Item #2</system:String>
                <system:String>Item #3</system:String>
                <system:String>Item #4</system:String>
                <system:String>Item #5</system:String>
            </ItemsControl>

Notez la notation pointée ItemsControl.ItemsPanel,  il faut faire le parallèle avec une classe, ici ItemsPanel est une propriété de la classe ItemsControl, et c’est comme ça qu’on sette une propriété en XAML !

Pour vous en convaincre, allons faire un tour dans la classe myItemsControl et faire travailler l’auto-complétion de code :

WPF itemspanel

Au passage servez vous de l’auto-complétion pour en apprendre plus sur les membre d’une classe c’est très intéressant !

Donc pour setter une propriété en XAML, on y met du XML logique même si ce n’est pas évident (on aurait pu penser à des attributs).

<ItemsControl.ItemsPanel> 
        <ItemsPanelTemplate> 
                <StackPanel /> 
        </ItemsPanelTemplate> 
 </ItemsControl.ItemsPanel> 

Dans le XAML ci-dessus, on définit le template de ItemsControl avec un Stackpanel.

WPF

WPF Ajouter un popup pour confirmer une action

Les popups (à ne pas confondre avec les Dialog Box) sont utiles pour confirmer qu’une action s’est déroulée bien ou non. Vous avez juste à cliquer pour que la popup se ferme.

Commençons par construire une application WPF de façon classique.

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication3"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
<Popup Margin="10,10,0,13" Name="Popup1" HorizontalAlignment="Left"   
 VerticalAlignment="Top" Width="194" Height="200" IsOpen="True">  
       <TextBlock Name="MonTextBlock"   
             Background="LightBlue" >  
        texte de popup   
       </TextBlock>  
</Popup>

    </Grid>
</Window>

La valeur de l’attribut IsOpen à True veut dire qu’il est visible. Ce que l’on veut faire par exemple c’est de rendre la popup visible avec un bouton et de mettre un bouton dans le popup pour fermer le popup, donc on va jouer sur la propriété IsOpen. Quand on lance l’application ça donne ça:

WPF popup

Vous aurez remarqué que j’ai mis les attributs HorizontalOffset et VerticalOffset, donnés en valeur relatives, sinon la popup est en bas à gauche de la fenêtre de l’application.

Popup offset WPF

Je vous invite à jouer sur les différents attribut. Mais celui qui nous intéresse le plus est l’attribut IsOpen. En effet je m’en sers pour des confirmation d’insertion de données dans une table, j’ai besoin d’être informé quand l’insertion s’est fait avec succès. Quitte à cliquer un bouton supplémentaire pour fermer la popup.

Utiliser le code behind pour ouvrir/fermer la popup

//le nom de la popup est popup1
// pour ouvrir le popup
popup1.IsOpen = True;

//pour fermer le popup
popup1.Isopen = False;

A ne pas confondre avec la propriété popup1.IsVisible qui est en fait un simple getter.

Vou saimeriez aussi savoir comment faire une boite de dialogue

WPF

Faire une application météo WPF Windows Presentation Framework

 

WPF pour créer des applications Windows Desktop

WPF en gros pour faire court c’est une application desktop avec des fenêtres. WPF est le successeur de Winform, personnellement je ne connais pas du tout Winform, disons que c’est la génération avant WPF. On va faire avec WPF, c’est une façon de programmer MVVM (Model-View-ViewModel). En gros ça veut dire qu’il y a une liaison entre le visuel et le modèle de données (les données elles-même appelé ViewModel), si vous modifiez une valeur sur l’interface graphique, cela va se répercuter dans la variable. Cette dernière variable peut être modifiée programmatiquement.

La programmation MVVM existe aussi sur le web, j’en ai pas mal fait avec AngularJS. L’avantage est une simplification de la mise à jour des données entre l’interface et la base de données.

Création d’une application WPF dans VisualStudio

Pour tout projet C#, vous êtes obligé de passer par VisualStudio, je ne pense pas qu’il y ait d’autres alternatives, c’est tant mieux ainsi, on va se concentrer mieux.

Concevoir une fenêtre en WPF

La différence avec une application est la présence d’une interface graphique, qui dit interface graphique de nos jour dit langage xml, dans le cas de WP, Microsoft a pondu une version de xml qui s’appelle le XAML.

A partir de là vous avez le choix de concevoir votre interface de manière graphique avec la souris, ou d’écrire directement en XAML, pour avoir un controle plus fin, mais les deux sont équivalent. Vous devez jongler entre les deux pour voir de quoi il retourne. A terme le design graphique vous permettra d’aller plus vite, mais la programmation en XAML va vous permettre de caler les éléments au pixel près.

xaml

Avec VisualStudio, les deux fenêtres graphique et textuelle se mettent à jour automatiquement dès qu’on modifie un élément d’une des deux fenêtres. Donc si vous êtes débutant, c’est un bon moyen d’apprendre.

Comment faire vivre l’interface graphique?

C’est bien beau de faire une belle interface graphique, quoique vous aurez des surprises car avec le glisser déplacer ce n’est pas si évident, en plus il y a énormément de composants, mais comment allons nous attacher du code à un bouton par exemple?

Il y a deux fichiers, MainWindow.xaml et MainWindow.xaml.cs, celui qui a l’extension cs correspond au code C#, donc vous y écrirez toutes les fonctions, on appelle le code derrière un bouton un code-behind tout simplement. Regardez comment le XAML et le code C# sont liés.

A chaque élément est attaché une fonction, par exemple, pour un bouton :

<Button x:Name="button1" Content="Meteo Toulouse" HorizontalAlignment="Center" Margin="10,10,10,0" VerticalAlignment="Top" Width="75" Click="GetMeteoToulouse_Click" />

On a un attribut Click dont la valeur est égale à GetMeteoToulouse_Click. et dans le code-behind, dans le fichier MainWindow.xaml.cs est automatiquement inséré un code du type :

  private void GetMeteoToulouse_Click(object sender, RoutedEventArgs e)
 { 
   //.......
   // code c# ici
 }

Cette fonction prend en argument deux paramètres. En WPF, il y a la notion très importante d’événement (RoutedEvent, mais en fait c’est un event), un click sur un bouton est un événement, une mise à jour d’une valeur est un événement, il faut donc bien comprendre comment les événements sont gérés.

Si le bouton est cliqué, c’est la méthode ci-dessus qui va s’exécuter.

Pour faire simple voici le code du XAML:

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication3"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <WrapPanel x:Name="wrapPanel1" Height="100" Width="100"/>
        <Label x:Name="label" 
               HorizontalAlignment="Stretch" 
               Margin="10,202,37,10" 
               FontSize="20"
               Padding="5"
               Loaded="label_Loaded" 
               VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5" Height="107">
        </Label>

        <ScrollViewer HorizontalAlignment="Left" Height="195" Margin="10,80,0,0" VerticalAlignment="Top" Width="497">
            <StackPanel Grid.Row="0" Height="110" Orientation="Horizontal" RenderTransformOrigin="0.5,0.5">
                <StackPanel.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform/>
                        <TranslateTransform Y="-30"/>
                    </TransformGroup>
                </StackPanel.RenderTransform>
                <Button x:Name="button" ToolTip="Un café?" Content="Shrink Label Text" HorizontalAlignment="Center" Margin="10,10,0,60" VerticalAlignment="Top" Width="100" Click="button_Click_1"/>
                <Button x:Name="button1" Content="Meteo Toulouse" HorizontalAlignment="Center" Margin="10,10,10,0" VerticalAlignment="Top" Width="75" Click="GetMeteoToulouse_Click" />
            </StackPanel>
        </ScrollViewer>

    </Grid>
</Window>

et le code behind :

using System;
using System.Windows;
using System.Windows.Controls;
using System.IO;
using System.Windows.Media.Imaging;

namespace WpfApplication3
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
        }

        private void label_Loaded(object sender, RoutedEventArgs e)
        {
            var label = sender as Label;

        }


        /// <summary>
        /// demande le temps qu'il fait pour Toulouse
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GetMeteoToulouse_Click(object sender, RoutedEventArgs e)
        {
            //get weather !
            var url = "http://api.openweathermap.org/data/2.5/weather?q=Toulouse&APPID=ici-la-cle-api";
            var webrequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);

            using (var response = webrequest.GetResponse())
            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                var result = reader.ReadToEnd();

                var jObject = Newtonsoft.Json.Linq.JObject.Parse(result);
                this.label.Content = ((string)jObject["weather"][0]["main"]);
                //Console.WriteLine((string)jObject["weather"][0]["icon"]);
                //Console.WriteLine(Convert.ToString(Math.Round((float)jObject["main"]["temp"] - 273.15)));

                //on va chercher l'image sur le serveur de l'api
                var image = new Image();
                var fullFilePath = @"http://openweathermap.org/img/w/"+ (string)jObject["weather"][0]["icon"] + ".png";

                BitmapImage bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.UriSource = new Uri(fullFilePath, UriKind.Absolute);
                bitmap.EndInit();

                image.Source = bitmap;
                wrapPanel1.Children.Add(image);

            }
        }

    }
}

Cette application est un peu codé à la rache, mais est fonctionnelle, il y a juste la clé API de OpenWeather qu’il faut créer pour avoir accès aux informations.

csharp green logo

Introduction aux expression régulière en C#

J’ai fait beaucoup de regex dans ma vie avec le langage de programmation PHP, mais avec le C# je m’aventure dans de nouvelles contrées, le but est de scraper des pages web en C# (nouveau défi !) et de rester dans l’environnement .NET (SQLServer, IIS, WPF etc). Je suppose que vous avez déjà des connaissances élémentaires avec les expressions régulières.

Expression régulière en C#

Globalement, c’est la même chose qu’en PHP, mais il y a des subtilités syntaxiques. L’objet de  ce post est plutôt montrer comment écrire une application console pour tester rapidement un script qui va parser des chaine avec du regex. On ne verra sans doute pas de différence vu que ce que je présente est assez élémentaire.

Utiliser IsMatch pour matcher une séquence

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
namespace scraper
{
    class Program
    {
        // http://lgmorand.developpez.com/dotnet/regex/
        static void Main(string[] args)
        {
            // TODO : match, replace, capture
            
            String chaine = "numéro de téléphone est  06 55 32 22";
            String telephone = "06 88 95 31 22";



            Regex matchPhone = new Regex(@"^[\d]+$");

            
            var test = matchPhone.IsMatch(telephone);
            

            Console.WriteLine("le resultat du match est " + remplace);
            Console.ReadLine();
        }
    }
}

Si nous faisons F5 pour lancer cette application, nous aurons comme sortie d’écran :

le resultat du match est False

En effet la chaine telephone est composé d’espace. Pour avoir le résultat à True, il faut enlever les espaces dans le numéro de téléphone (à moins de matcher aussi les espaces).

Utiliser Replace pour remplacer une chaine de caractères

using System;
using System.Text.RegularExpressions;
namespace scraper
{
    class Program
    {
        // http://lgmorand.developpez.com/dotnet/regex/
        static void Main(string[] args)
        {
            // TODO : match, replace, capture

            String chaine = "numéro de téléphone est  06 55 32 22";
            String telephone = "06 88 95 31 22";



            Regex myRegex = new Regex("^[a-z]$");
            Regex matchPhone = new Regex(@"téléphone");

            var remplace = matchPhone.Replace(chaine, "mobile");

            Console.WriteLine("le remplacement regex a donné " + remplace);
            Console.ReadLine();
        }
    }
}

En jouant ce code vous aurez à al sortie le texte « téléphoneé remplacé par mobile.

le remplacement regex a donné : numéro de mobile est  06 55 32 22

La variable « remplace » contient la  nouvelle chaine.

Capturer une chaine

Nous allons maintenant faire ce pour quoi le rege est le plus utile : extraire une chaine satisfaisant une condition et la stocker éventuellement dans une variable.

using System;
using System.Text.RegularExpressions;
namespace scraper
{
    class Program
    {
        // http://lgmorand.developpez.com/dotnet/regex/
        static void Main(string[] args)
        {
            // TODO : match, replace, capture
              String input = "numéro de téléphone est  0655322288";

            Regex matchDigit = new Regex(@"[\d]+");

            // match
            Match match = matchDigit.Match(input);
            if (match.Success)
            {
                string s = match.Groups[1].Value;
                Console.WriteLine("le match regex a donné : " + match);

            } else
            {
                Console.WriteLine("Pas de match trouvé");
            }
            Console.ReadLine();
        }
    }
}

Vous avez en sortie :

le match regex a donné : 0655322288

Maintenant on sait capturer avec des parenthèses (un classique) une chaine, on va passer à l’autre volet du scraping, le téléchargement d’une page web, le tout en C# bien sûr !

PLUS : Capturer avec les groupes nommés

Il vous est possible d’attribuer u nom au groupe que vous avez capturé. C’est pratique. Voyons comment on peut réécrire l’expression régulière de tout à l’heure.

fgfg

 

WPF

Comment lire un flux JSON d’un webservice en csharp

Vous devez avant tout créer un compte sur le site openweathermap et générer une clé API (automatique normalement) et attendre environ 10 minutes avant qu’elle ne soit active. Ansuite munide votre clé API, composez l’url suivante et collez al sur la barre d’adresse de votre navigateur web:

http://api.openweathermap.org/data/2.5/weather?q=Toulouse&APPID=ma-cle-api

Ensuite créez une application de type console dans Visual Studio, an vous assurant d’avoir installé la librairie NewtonSoft.JSON pour lire facilement un fichier texte JSON et le transformer en un objet JSON.

using System;
using System.IO;
using System.Net;
using Newtonsoft.Json;

namespace Playground
{
    class Program
    {
        static void Main(string[] args)
        {
            var url = "http://api.openweathermap.org/data/2.5/weather?q=Toulouse&APPID=ma-cle-api";
            var webrequest = (HttpWebRequest)System.Net.WebRequest.Create(url);

            using (var response = webrequest.GetResponse())
            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                var result = reader.ReadToEnd();

                var jObject = Newtonsoft.Json.Linq.JObject.Parse(result);
                Console.WriteLine((string)jObject["weather"][0]["main"]);


                //var text = Convert.ToString(result);
                //Console.WriteLine(text);
                Console.ReadLine();
            }
        }
    }
}

Analysons maintenant le code en détail :

var url = "http://api.openweathermap.org/data/2.5/weather?q=Toulouse&APPID=ma-cle-api";
var webrequest = (HttpWebRequest)System.Net.WebRequest.Create(url);

On déclare une variable à la volée, et on y met une chaine qui est le point du service web (endpoint en anglais)

le mot clé var permet de déclarer une variable non typée, pour des raisons de pratique on le fait souvent.

La seconde ligne est la déclaration d’un objet de type HttpWebRequest, créé via la méthode statique Create de la classe System.Net.WebRequest.

using (var response = webrequest.GetResponse())

Cette ligne est sans doute la plus importante, c’est elle qui va chercher le flux en faisant une requête à travers el web.

Encore une fois on utilise le mot clé var parce que c’est pratiue, mais ce n’est pas le plus rigoureux, il faudrait mettre le type de retour de la méthode GetResponse qui retourne un objet de type WebResponse.

En C# ce n’est pas aussi direct qu’en PHP, vous devez transformer response en un flux avec la ligne suivante :

using (var reader = new StreamReader(response.GetResponseStream()))
{
    var result = reader.ReadToEnd();

    var jObject = Newtonsoft.Json.Linq.JObject.Parse(result);
    Console.WriteLine((string)jObject["weather"][0]["main"]);


    //var text = Convert.ToString(result);
    //Console.WriteLine(text);
    Console.ReadLine();
}

On instancie un StreamReader pour lire le flux retourné par le webservice. La méthode ReadToEnd retourne une chaine de caractère.

Enfin on parse cette chaine de caractère qui est un JSON en fait, et on en fait un objet JObject (survolez la variable pour avoir son type).

Enfin on accède facilement à la donnée qu’on veut en connaissant son chemin grâce au requêtage avec le browser (mais vous pouvez aussi faire un dump dans la console).

 

 

Retour en haut