lundi 15 avril 2019

C# Windows Form - Working with DataBases let's make a CRUD

Je me retrouve à vouloir connecter mon application Windows Form avec une Base de Données pour y travailler, comment je fais ? Je sais utiliser EntityFramework puis-je faire la même chose avec Visual Studio Community ?

Voilà le contexte de mon projet, ce n'est pas si simple j'ai "perdu" ma licence Visual Studio Professionnelle, j'ai du oublier de payer ... bref, je désinstalle Visual Studio Pro et Résinstalle Visual Studio Community pour voir.

Je créé mon premier projet C# Windows Form et maintenant j'ajoute une source de données, je créé ainsi le fichier MyDB.mdf et j'obtiens la structure du projet suivant :


C# Windows Form working with DataBases
C# Windows Form working with DataBases
Ok, on va dire que je viens de créer une connexion à une base de données mais maintenant comment je travaille avec cette base de données, il me faut faire un CRUD (Create Read Update Delete).

Dans la "boite à outils" je vois le "BindingSource" :

Windows Form working with DataBase - BindingSource
On dirait d'après l'aide que c'est le bon objet à utiliser. Mais allons voir, allons chercher un peu d'aide sur Internet :

Guru99 - C# Database Connection Tutorial with Example
Ici, il ne précise pas qu'il travaille avec Windows Form mais le CRUD est complet, on utilise un SqlDataReader et on passe des SqlCommand de façon très classique.

Connect to DataBase

A l'aide de la "Connection String" on se connecte à la Base de Donnée. Lors de la création de ma base de données Visual me donne la chaine de connexion à la base :

Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=D:\Users\Braby\Documents\MyBD.mdf;Integrated Security=True;Connect Timeout=30

Hum hum que faire ensuite ...

Du coup sur ma "Form1" je créé un bouton "Connect" et un labelMessage :

C# Windows Form - Working with DataBases - Connect
J'ajoute un peu de code derrière mon bouton "Connect" et lors de l'exécution cela fonctionne de la façon suivante :

C# Windows Form - Working with DataBases - Connect to database
La suite de l'exemple de Guru99 utilise un SqlDataReader pour lire la base de donnée. Il utilise un SqlDataAdapter pour insérer dans la base grâce au SqlCommand pour la mise à jour et pour la suppression des données de la base également.

Les SqlCommand sont des chaines de caractères, c'est souple et c'est la base mais c'est un peu fastidieux au niveau du traitement des chaînes de caractères.

Create into DataBase

On aura besoin sans cesse de chaîne de connexion je propose donc le code suivant pour coder la chaîne de connexion utilisable partout dans l'application :

namespace WindowsFormsApp1
{
    public static class ConnectionString
    {
        public static string MyDB
        {
            get { return @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=D:\Users\Braby\Documents\MyBD.mdf;Integrated Security=True;Connect Timeout=30"; }
            set {; }
        }
    }


Gros problème avec Visual Studio Community comment créer ma data base ...


Visual Studio Community Création de la Base de Données
C'est curieux, si je ne me retenais pas, je dirais que c'est bien pourri, j'ai du passer par la création de la Table :

CREATE TABLE [dbo].[Table]
(
 [Id] INT NOT NULL PRIMARY KEY,
    [Name] NVARCHAR(50) NULL,
    [Surname] NVARCHAR(50) NULL
)


Puis impossible d'exécuter ce script, j'ai donc du faire : Générer le script ... là c'est le délire habituel de SQLServer c'est un tas de trucs vraiment inutiles dans le script ...

Un conseil gardez soigneusement les scripts ainsi générés pour les pouvoirs le rejouer. Bref ma table est crée elle s'appelle "Table" et oui j'ai pas eu le temps de changer son nom ... Grrrr !

En fait ça ne va pas fonctionner, je vous passe les détails.

Création de la Base de Données

Nouveau Table Script et la solution est dans le bouton "Mettre à jour"


Visual Studio Community - Création de la Base de Données - 1
Le bouton "Mettre à jour" génère le script :

Visual Studio Community - Création de la Base de Données - 2
Il faudrait être idiot pour renoncer si prêt du but, il n'y a plus qu'à cliquer sur "Générer le script" :

Visual Studio Community - Création de la Base de Données - 3
C'est là que je disais que le script est vraiment stupide depuis les années 2000 sql server de Microsoft est comment dire pour ne pas froisser ... POURRI ! Imbécile, idiot ... si vous avez une fois dans votre vie utilisé autre chose comme par exemple MySQL qui est si bon et si facile ... bref.

C'est lié au fait que cet idiot de SQL Server il veux toujours mettre les fichiers là où il veut et pas là ou l'utilisateur voudrait qu'ils soient. D'ailleurs si vous ne faite pas attention de là où vous créez votre base de données, je vous met au défit de la retrouver. Certain apprennent par cœur le nom du répertoire :

D:\Users\UserName\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\MSSQLLocalDB\master.mdf

Pour ne pas perdre leurs bases de données, je ne vous dit pas ce que cela peut entrainer d'erreurs au déploiement.

Read de la Database

Ben oui, une fois créé l'article il faudra le lire, c'est l'objectif du CRUD les JavaIste commencent donc par créer un article pour lire ensuite.

Evidemment, avec tout ça, la chaine de connexion a changée :

Data Source = (LocalDB)\MSSQLLocalDB;Initial Catalog = MyDB; Integrated Security = True; Connect Timeout = 30; Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False

Voici le code derrière le bouton Read :

         private void buttonRead_Click(object sender, EventArgs e)
        {
            SqlDataReader dataReader;
            String sql = "Select Name, Surname, Id from TableCustomer";
            SqlCommand command = new SqlCommand(sql, Connection);


            dataReader = command.ExecuteReader();

            while (dataReader.Read())
            {
                labelRead.Text += labelRead.Text + dataReader.GetValue(0) + "-" + dataReader.GetValue(1) + Environment.NewLine;
            }
        }


Et ma table s'appelle "TableCustomer" moyennant quoi je peux passer à la création d'un Item de la table :


Visual Studio Community - Create Item
Et le code derrière bouton Create :

        private void buttonCreate_Click(object sender, EventArgs e)
        {
            SqlDataAdapter adapter = new SqlDataAdapter();
            String sql = "Insert into TableCustomer(Name, Surname) values(";
           
            // Vérifications du formulaire
            if (    textBoxName.Text != string.Empty
                 && textBoxSurname.Text != string.Empty )
            {
                sql += "'" + textBoxName.Text + "','"
                    + textBoxSurname.Text + "')";


                adapter.InsertCommand = new SqlCommand(sql, Form1.Connection);
                adapter.InsertCommand.ExecuteNonQuery();
                adapter.InsertCommand.Dispose();
            }
            else
            {
                labelMessage.Text = "Error: Name vide ou Surname vide !";
            }
        }


Update d'un article de Base

Le formulaire de mise à jour d'un article de la base est de loin de plus difficile à faire il faut aller lire permettre la mise à jour avec de faire un update avec la syntaxe SQL :

Update Table set Surname = 'aaa' where Id=2

Je vous laisse le faire ;-)

Delete en article

Delete Table where Id=3

idem ...

Utiliser les Bindings pour contrôler les data

Voilà, nous sommes au cœur du sujet, maintenant que nous savons manipuler les données nous allons les Binder directement sur une source de données pour les parcourir et les mettre à jour.

J'ai créé les trois TextBox qui vont me permettre d'afficher les données.

Je drag and drop un BindingNavigator dans ma Form3 :

Visual Studio Community - BindingNavigator
Cela doit se faire tout seul, il faut arriver à créer une source de données MyDBDataSet :
Visual Studio Community - BindingNavigator
Problème à chaque fois que j'essayais de créer ma source de données le programme me répondait "le fichier MyDB.mdf est déjà utilisé, fermé le programme qui utilise ce fichier".

nian, nian, nian ... toujours les même problèmes avec MSSqlServer ! Bref, j'ai du redémarrer ma machine certainement que pendant les tests de ce programme une connexion à la BD s'est mal terminée.

Ensuite une fois la "Sources de données" configurée, j'ai pu binder chaque TextBox de mon formulaire sur les colonnes de ma table.

Il ne me restait plus qu'à Binder mon bindingNavigator1 sur la tableCustomerBindingSource créé par le programme pour que cela fonctionne.

Winform - Binding on DataSource
Mon formulaire est binder sur ma source de données, c'est très joli mignon tout plein :
Winform Binding
Voilà, nous avons fait le tour de toutes les fonctionnalités qui permettent de manipuler des enregistrements de données à partir d'une Winform Application. C'est un peu fastidieux car il y a différentes techniques mais je montre que l'on s'en sort avec Visual Studio Community.

Have fun!








mardi 11 février 2014

MODBUS Calcul du CRC sur 16 bits en C#

Voici un exemple simple d'application WinForm en C# de Calcul du CRC sur 16 bits d'une trame en hexadécimal. Il y a beaucoup de littérature sur ce sujet mais pas d'exemple simple d'implémentation de l'algorithme de calcul du CRC véritablement en C#.



http://portelatine.chez-alice.fr/electronique/ecc_cce/ecc_crc.html
Code Correcteur d'Erreur

Je me suis donc lancé dans l'exercice. Voici donc l'interface de notre application WinForm :

WinForm C# de Calcul du CRC d'une trame (Requête)
Requête : C'est une trame en hexadécimal dont je vais calculer le CRC MSB (most significant byte ou poid fort) et LSB (poid faible) en cliquant sur le bouton "Calculer CRC".

Au delà d'implémenter et de valider l'algorithme de calcul du CRC sur 16 bits d'une trame par exemple de protocole ModBus, cet exemple est intéressant à plusieurs titres :
  • manipulation des strings en chars et conversion en byte[] et réciproquement
  • filtrage des caractères dans une TextBox pour n'obtenir que les caractères hexadécimaux
Tout se passe dans la TextBox, on va filtrer les caractères pour ne laisser "passer" que les caractères hexadécimaux et les contrôles. Sur le bouton on implémentera l'algorithme de calcul d'un CRC sur 16 bits.

Petit rappel rapide sur le calcul hexadécimal

En binaire, sur 4 bits, on peut compter jusqu'à (1111) binaire = (15) décimal = (F) hexadécimal
Sur un octet (8 bits = 1 Byte = 2 fois 4 bits) (1111 1111) binaire = (255) décimal = (FF) hexadécimal
Notation : binaire/hexadécimale :
1 = 1
11 = 3
111 = 7
1000 = 8
1010 = A
1011 = B
1100 = C
1101 = D
1110 = E
1111 = F
Sur un octet : FF que l'on notera (0xFF) hexadécimal = (255) décimal

Donc dans notre TextBox, on ne pourra entrer qu'un nombre pair de caractères correspondant à un nombre d'octets de la trame (ou Requête) en notation hexadécimal.

Algorithme du CRC 16 en C#

En C#, c'est à dire très proche du C ou du C++, j'écris l'algorithme de la façon suivante :

int CalculCRC16(byte[] octets)
{
   int crc = 0xFFFF;
   int i, done = 0;
   byte todo;
   int nb = octets.Length;

   if (nb >= 0)
   {
    do 
    {
     todo = octets[done];
     crc ^= todo;
     for (i = 0; i < 8; i++)
     {
      if (crc % 2 != 0)
      {
       crc = (crc >> 1) ^ 0xA001;
      }
      else
      {
       crc = crc >> 1;
      }
     }
     done++;
    } while (done < nb);
   }

return crc;
}

Afin d'utiliser les opérateurs de l'algèbre de bool :
^ : XOR ou OU exclusif
>> 1 : décalage à droite de 1 bit
l'algorithme prend en entré un tableau de byte : byte[] et retourne le CRC en int sur 16 bits.

Conversion d'une string en byte[]

Mon TextBox possède une propriété Text que je transforme en byte[] grâce à la fonction :

static public byte[] StringHexaToByteArray(string str)
{
char[] s = str.ToCharArray();
byte[] b = new byte[s.Length/2];

int oct = 0;
for (int i = 0; i < s.Length/2; i++)
{
string octet = string.Concat(s[oct], s[oct + 1]);
oct = oct + 2;
int hexa = int.Parse(octet, System.Globalization.NumberStyles.HexNumber);
b[i] = (byte)hexa;
}

return b;
}

On voit ici apparaître le faite qu'un byte est une paire de caractères de la chaîne str passée en paramètre c'est à dire la string TextBox.Text.

On note au passage une instruction bien sympa qui permet d'afficher un int en hexa :

string hexa = crc.ToString("X");

Simple, non ?

Filtrage des caractères non hexadécimaux

En ce qui concerne les caractères tapés au clavier le filtrage se fait dans la fonction :

private void textBoxRequete_KeyPress(object sender, KeyPressEventArgs e)
{
char [] _hexa = {'A','B','C','D','E','F'};
List<char> hexa = new List<char>(_hexa);

if (char.IsLower(e.KeyChar))
{
e.KeyChar = char.ToUpper(e.KeyChar);


if (char.IsLetterOrDigit(e.KeyChar) == false && char.IsControl(e.KeyChar) == false)
{
e.Handled = true;
}

if (char.IsLetter(e.KeyChar) == true && hexa.Contains(e.KeyChar) == false)
{
e.Handled = true;
}
}

La bibliothèque de fonctions static de char ne possède pas de fonction IsHexa, on est donc obligé d'y palier en ajoutant un petit traitement de List<T>.

Cette fonction est câblée sur le KeyPressEventHandler mais cela ne suffit pas car l'utilisateur peut encore copier/coller une chaîne de texte dans la TextBox. On cablera donc sur EventHandler la fonction :

private void textBoxRequete_TextChanged(object sender, EventArgs e)
{
char[] _hexa = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
List<char> hexa = new List<char>(_hexa);

string filter = string.Empty;
char f;
foreach (char c in textBoxRequete.Text)
{
f = char.ToUpper(c);
if (hexa.Contains(f))
{
filter += f;
}
}
textBoxRequete.Text = filter;
}

Pour les explications du code s'est fini ! Et pour le reste c'est de la tambouille de programmeur.

Quelques exemples de validation :
CRC16(0x00) = 0x40BF

Calcul du CRC de la trame 0x00
Sur un site que personnellement, je trouve fantastique : Sitelec Calcul du CRC16 on trouvera la trame :

CRC16(0x02062329000D) = 0x7092

Donc pour vérifier l'implémentation (valider) :

Calcul du CRC de la trame 0x02062329000D
On peut penser que l'implémentation de notre algorithme est correcte.

Bibliographie CRC


Pour optimiser cet algo il faut précalculer des tables dont certaine exeplications se trouvent ici :

Et sur ce site bien plus que le calcul de CRC :

Download source code in C#

Requirements :
  • Visual Studio 2010 
  • Langage C#

 

lundi 27 janvier 2014

Minimize Windows Application into System Tray

Comment gérer la minimisation d'une application Windows sous forme d'icône dans la partie état de la barre des tâches afin qu'elle apparaisse icônisée ? Voici une problématique que l'on aime bien résoudre en tant que développeur car cela donne un aspect très professionnel à nos applications.

Requirements :
Visual Studio 2010
Langage C#

Voici donc une petite application Windows Form qui démontre comment utiliser, gérer les différents éléments concernant ces problématiques deux boutons :
"Show In Syst Tray" : pour faire apparaître ou disparaître l'application dans la barre d'état
"Show In Task Bar" : pour faire apparaître ou disparaître l'application de la barre des tâches
richTextBoxMessage : pour afficher les messages à l'utilisateur

Minimize Windows Application into System Tray
Autre aspect intéressant de cet exemple c'est la gestion du menu contextuel de l'application icônisée dans la barre d'état de la barre des tâche ; j'ai choisi d'utiliser le menu contextuel de l'application et de le faire apparaître une fois que l'application est icônisée dans la barre d'état :

Menu contextuel de la barre d'état
Le menu contextuel est ainsi facile à gérer depuis l'application principale :

Contextuel Menu in Syst Tray

Download Source Code

Je vous laisse découvrir le reste dans le code source :

  • utilisation d'icônes personnalisées
  • gestion des ballons et de leurs contenus ...



mercredi 2 octobre 2013

TabControl Sample

Qu'elles sont les manipulations de base que l'on doit savoir facilement effectuer avec un TabControl. Voici un exemple de code source. Que se passe t-il lors du redimensionnement de la fenêtre principale ?

On a vu précédemment dans TabControl and TabPage Visible que l'on rencontre rapidement de petits soucis. Voici donc un exemple de code source de manipulation rapide des TabPages dans un TabControl.

Requirements :
Visual Studio 2010
Langage C#

Gestion des TabPages dans un TabControl

Un TabControl permet de gérer une collection de TabPages. Pour ajouter une TabPage, on se rend sur la Form principale et l'on sélectionne le TabControl :

Gestion des TabPages d'un TabControl
Dans la fenêtre des propriétés du TabControl on sélectionne la collection de TabPage :

Propriétés TabPages du TabControl 
En cliquant sur le bouton on obtient une fenêtre de gestion des TabPages :

Gérer la collection de TabPages dans un TabControl
Un autre moyen rapide de gestion des TabPages c'est en cliquant sur le petit triangle en haut à droite du TabControl :

Gestion rapide des TabPages d'un TabControl - TabControl Tasks
Voilà c'est tout pour le côté graphique.

TabControl code source 

La propriété TabPages du TabControl est une collection TabPageCollection et se gère comme telle, d'où le code pour changer l'ordre des TabPages :
private void buttonChangerOrdre_Click(object sender, EventArgs e)
{
tabControlMain.SuspendLayout();
tabControlMain.TabPages.Clear();
tabControlMain.TabPages.Add(tabPage3);
tabControlMain.TabPages.Add(tabPage2);
tabControlMain.TabPages.Add(tabPage1);
tabControlMain.ResumeLayout();
}

Sur un ordinateur rapide les fonctions SuspendLayout et ResumeLayout ne changent pas grand chose pour moi il y a encore quelques "sursauts graphiques".

Pour passer programmatiquement d'un TabPage à l'autre :
private void afficherTabPage2()
{
tabPage1.Hide();
tabPage2.Show();
tabControlMain.SelectedIndex = 1;
}

Pensez à sélectionner le bon index.

TabControl redimensionnement de la fenêtre principale

Le redimensionnement des contrôles dans les TabPages lors du changement de taille de la fenêtre principale Form1 est assuré par les deux fonctions :
private void Form1_ResizeBegin( object sender, EventArgs e )
{
    // Sauver les données de taille et de place au départ du déplacement
}

Et :
private void Form1_ResizeEnd( object sender, EventArgs e )
{
     // Calculer les nouvelles places et taille à la fin du déplacement
}

TabPage mauvais redimensionnement lors du Resize de la page

Problème rencontré : Lors du déploiement de cet exemple sur une plateforme Windows XP et lors du redimensionnement de la fenêtre principale voici le résultat :

TabControl problème lors de Resize de la fenêtre principale
On remarquera le mauvais redessinement du TabPage en bas.

Solution

C'est problèmes d'incompatibilité des composants standards multiplateforme windows sont assez délicats pour que l'on prenne le temps de s'y attarder. Ils engendrent souvent un délais non maîtrisable supplémentaire lors du développement. On ne les découvre souvent que trop tard une fois le client mécontent.

Il faut effectuer un Refresh du TabPage actif de la façon suivante :

private void Form1_ResizeEnd( object sender, EventArgs e )
{
    tabControlMain.TabPages[tabControlMain.SelectedIndex].Refresh();

Download Source Code


Have fun !



mardi 1 octobre 2013

Search Text in RichTextBox - Find function

Comment rechercher du Texte dans un contrôle RichTextBox. Voici une application Windows  un peu aboutie permettant d'ouvrir un fichier en édition dans une RichTextBox, de modifier ce fichier et de le sauvegarder.

Requirements :
Visual Studio 2010 C#
.NET Framework 3.5
Contrôle : RichTextBox

Structure et spécifications de l'application

Vous avez certainement déjà joué avec des fichiers sur disque dur afin de les ouvrir, de les traiter, de les afficher. Ici on va ouvrir un fichier et faire des recherches de texte dans ce fichier.

Il y a bon nombre de choses à faire lors de l'ouverture de la modification et de la sauvegarde d'un fichier, tous ces cas seront traité par l'application qui se compose d'un TabControl principal tabControlMain et de deux TabPages, tabPageFichier qui va permettre d'ouvrir de modifier et sauver le fichier dans le contrôle RichTextBox et tabPageConsole qui va permettre d'afficher le comportement de l'application et certains messages d'erreur lors de l'utilisation de l'application :

Application RichTextBox Find - Structure
Je vais créer un composant utilisateur dérivé RichTextBox et encapsuler mon contrôle dans une DLL ceci afin de créer un contrôle utilisateur que je pourrais à la fois utiliser dans mon application principale puis réutiliser dans une autre application.

A ma solution principale nommée RichTextBoxFind j'ajoute donc une Class Librairie WindowsFormsControlLibraryRichTextBoxFind qui va contenir une boite de dialogue DialogFind et mon nouveau contrôle utilisateur dérivé d'un contrôle RichTextBox, RichTextBoxFind :

Application RichTextBox Find - Visual Studio Solution
Ma boite de dialogue créé un Event particulier auquel mon nouveau composant utilisateur RichTextBoxFind va pouvoir s'abonner afin d'être prévenu lorsque l'utilisateur cliquera sur le bouton "Chercher". DialogFind est toute simple et propose les RichTextBoxFinds Options à l'utilisateur :

RichTextBoxFinds Options 
Je veux pouvoir montrer cette boite de dialogue lorsque l'utilisateur pressera les touches "Ctrl-F" dans la RichTextBox.

Détails de l'implémentation

Dans la DLL WindowsFormsControlLibraryRichTextBoxFind de ma RichTextBoxFind Windows Application, je créé un composant utilisateur que je nomme RichTextBoxFind :

Contrôle ou Composant Utilisateur RichTextBoxFind
Je dérive mon contrôle d'un contrôle RichTextBox de base :
public partial class RichTextBoxFind : RichTextBox // Component,

Dans ce contrôle, je créé un KeyEventHandler : RichTextBoxFind_KeyUp :
private void RichTextBoxFind_KeyUp( object sender, System.Windows.Forms.KeyEventArgs e )
{
// If key pressed in RichTextBox is "Ctrl+F" then Find
if ( e.Modifiers == Keys.Control & e.KeyCode == Keys.F )
{
if (findDialog == null)
{
findDialog = new DialogFind();
findDialog.Finded += new FindEventHandler(OnDialogFind);
}
this.HideSelection = false;

// Recuperer le texte sélectionné dans le RTB
string sel = this.SelectedText;
if ( sel != string.Empty )
{
TextBox tb = ( TextBox )GetControl( findDialog, "textBoxFind" );
tb.Text = sel.Trim();
}
findDialog.ShowDialog();
}
}

Qui s'occupe de capter les touches pressées par l'utilisateur et récupère le "Ctrl-F" pour afficher la boite de dialogue DialogFind et au passage on abonne la fonction OnDialogFind à l'événement Finded. 
L'algorithme de recherche de texte dans un RichTextBox se trouve dans la fonction OnDialogFind de ce nouveau composant utilisateur.

Ma boite de dialogue DialogFind créé un User Event Handler : Finded :
namespace WindowsFormsControlLibraryRichTextBoxFind
{
    // A delegate type for hooking find notifications.
    public delegate void FindEventHandler( string findWhat, RichTextBoxFinds findOption );
    
    public partial class DialogFind : Form
    {
        public event FindEventHandler Finded;
        ...

Qui va permettre de déclencher un événement Finded à destination de mon contrôle RichTextBoxFind lorsque l'utilisateur cliquera sur le bouton "Chercher".

Ma boite de dialogue possède ainsi un EventHandler qui s'occupe de gérer les options de recherche et de déclencher l'événement Find à destination de la RichTextBox :
private void buttonFind_Click( object sender, EventArgs e )
{
if ( textBoxFind.TextLength > 0 )
{
RichTextBoxFinds findOption = new RichTextBoxFinds();
if ( checkBoxMatchCase.Checked ) 
findOption = RichTextBoxFinds.MatchCase;
if ( checkBoxWholeWord.Checked ) 
findOption = findOption | RichTextBoxFinds.WholeWord;
if ( radioButtonUp.Checked ) 
findOption = findOption | RichTextBoxFinds.Reverse;
if ( Finded != null )
{
this.Finded( textBoxFind.Text, findOption );
}
}
}

Pour utiliser mon nouveau composant utilisateur dans mon application principale. J'utilise une possibilité de la ToolBox bouton droit -> "Choose Items" :

ToolBox de Visual Studio Ajouter un composant utilisateur
Qui me permet de browser ma DLL WindowsFormsControlLibraryRichTextBoxFind.dll et ainsi d'ajouter dans la ToolBox mon nouveau composant :

ToolBox Nouveau composant RichTextBoxFind
En drag and dropant ce nouveau contrôle dans la fenêtre de mon application je peux l'utiliser :

RichTextBoxFind - Fenêtre principale
L'application principale s'occupe de jouer avec le fichier de l'afficher et lorsque je presse les touches "Ctrl-F" de mon application en ayant au préalable sélectionné le texte que je souhaite rechercher voici l'exécution :


RichTextBoxFind - Windows Application - Exécution

Download Source Code

Ce code source en C# fait bien d'autres choses qui ne sont pas détaillées ici concernant le traitement des fichiers ... à vous de creuser. Tout l'intérêt de cet exemple réside dans l'utilisation de la fonction Find() du RichTextBox au sein de l'algorithme OnDialogFind car j'ai trouvé sur internet d'autres exemples bien plus hasardeux.


mercredi 18 septembre 2013

TabControl and TabPage visible

Je travaille actuellement avec un TabContol et Visual Studio 2008 en C# et je veux programmatiquement changer de TabPage. Je suis très surpris par ce que je trouve sur ce sujet sur Internet :

http://www.thejoyofcode.com/Show_and_Hide_tab_pages_in_Windows_Forms.aspx
Là on me raconte que plein de gens disent que c'est impossible que de toutes les façons les méthodes Show() et Hide() de l'objet TabPage n'ont aucune action ! Joy of Code part ensuite dans des considérations hallucinantes avec du code pour gérer la collection de TabPage de mon TabControl :
// "hides" tab page 2
_tabControl.TabPages.Remove(_tabPage2);

// "shows" tab page 2
if (_tabControl.TabPages.Contains(_tabPage2))
{
    _tabControl.TabPages.Add(_tabPage2);
}

Il fait encore plus fort avec ce genre de code :
// Suspend and resume layout to avoid flickering.
_tabControl.SuspendLayout();
_tabControl.TabPages.Clear();
_tabControl.TabPages.Add(_tabPage1);
_tabControl.TabPages.Add(_tabPage2);
_tabControl.ResumeLayout();

Bref en plus cela ne fonctionne pas et ce n'est pas ce que l'on veut !

http://stackoverflow.com/questions/552579/how-to-hide-tabpage-from-tabcontrol
Ici également on nous dit que cela n'est pas possible ...avec WinForms 2.0.

Bref je perds mon temps et les choses ont dues être corrigées depuis car le code est tout simple.

Solution pour changer de TabPage dans un TabControl

Ayant pris soin de nommer mon TabControl : tabControlMain
Et dans la collection de TabPages j'ai deux TabPage : tabPage1 et tabPage2 voici le code :

private void buttonShowTabPage1_Click( object sender, EventArgs e )
{
tabPage2.Hide();
tabPage1.Show();

tabControlMain.SelectedIndex = 0;
}

private void buttonShowTabPage2_Click( object sender, EventArgs e )
{
tabPage1.Hide();
tabPage2.Show();

tabControlMain.SelectedIndex = 1;
}

C'est tout simple finalement, non ?

Have fun !

mardi 23 avril 2013

WinForm Customized UserControl

A propos de la customisation des contrôles utilisateurs en WinForm, je tombe sur ce pauvre exemple du MSDN :

http://code.msdn.microsoft.com/CSWinFormControls-2bee4cbc

C'est un bien pauvre début. Alors j'aimerais prendre cet exemple de ComboBox à multiple colonnes pour en faire un "vrai" contrôle utilisateur.

Je prends mon VisualStudio 2010 et je créé un petit projet WindowsApplication1 et j'y ajoute un projet Class Library que j'appelle MultiColumnComboBox :
Création du projet de Class Library MultiColumnComboBox
Je peux maintenant ajouter dans mon projet de Class Library un UserControl :
Ajout d'un UserControl MultiColumnComboBox
Me voici paré pour ajouter dans ma WindowsApplication1, l'utilisation de mon nouveau contrôle MultiColumnComboBox. Je compile et je vois apparaître dans ma Toolbox mon nouveau UserControl que je vais pouvoir ajouter dans Form2 simplement en glissant déplaçant mon nouveau contrôle.

Ajout de mon nouveau contrôle MultiColumnCombox dans ma Form2
Une partie du code de l'exemple MSDN m'intéresse, il s'agit de la fonction frmCtrlCustomization_Load qui créée une DataTable pour instancier la propriété DataSource de la ComboBox.

Spécification de mon UserControl MultipleColumn ComboBox

Ce que j'aimerais pouvoir faire avec mon nouveau contrôle :
1 - Remplir la Table depuis la Form2
2 - Pouvoir ajouter autant de colonnes que je le souhaite
3 - Associer depuis la Form2 un événement qui se déclenchera sur le SelectedIndexChanged de la ComboBox de mon nouveau contrôle.
4 - Récupérer l'item sélectionné de la ComboBox afin de pouvoir l'afficher dans la Form2

1 - Remplir la Table du contrôle utilisateur depuis la Form2

Pour exposer des propriétés publiques d'un UserControl on a vu dans l'exemple zCheckBox qui faut écrire quelque chose du genre :

namespace MultiColumnComboBox
{
    public partial class MultiColumnComboBox : UserControl
    {
        private DataTable _table = new DataTable();
        [Category("MultiColumnComboBox"), Description("Table of columns."), Browsable(true)]
        public DataTable Table
        {
            get { return _table; }
            set { _table = value; }
        }

Ce qui permet ensuite dans le code de la Form2 d'écrire quelque chose du genre :

namespace WindowsFormsApplication1
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();

            multiColumnComboBox1.Table.Columns.Add("ID", typeof(int));
            multiColumnComboBox1.Table.Columns.Add("Name", typeof(string));
            multiColumnComboBox1.Table.Columns.Add("Surname", typeof(string));

            multiColumnComboBox1.Table.Rows.Add(1, "John", "Joe");
            multiColumnComboBox1.Table.Rows.Add(2, "Any", "Amy");
            multiColumnComboBox1.Table.Rows.Add(3, "Tony", "Smith");
            multiColumnComboBox1.Table.Rows.Add(4, "Bruce", "Willice");
            multiColumnComboBox1.Table.Rows.Add(5, "Allen", "John");
            multiColumnComboBox1.Table.Rows.Add(6, "C'est Bibi", "Coucou");

            multiColumnComboBox1.SelectedIndexChanged += new EventHandler(multiColumnComboBox1_SelectedIndexChanged);
        }

Voilà je viens de remplir les colonnes de ma ComboBox qui est devenue une Multiple ComboBox.

2 - Pouvoir ajouter autant de colonnes que l'on souhaite

La fonction qui construit la table : frmCtrlCustomization_Load devient dans mon contrôle utilisateur :

\\WindowsFormsApplication1\MultiColumnComboBox\MultiColumnComboBox.cs

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

Je remplace le 2 qui était "en dur" car la multiple combobox de l'exemple n'avait que 2 malheureuses colonnes par _table.Columns.Count afin de pouvoir créer le nombre de colonne que je souhaite.
Je vous laisse regarder les détails dans le code source à la fin de cet article.

3 - L'événement sur le SelectedIndexChanged de la ComboBox

Dans mon UserControl MultiColumnComboBox je cable un événement : comboBox1_SelectedIndexChanged et j'expose une propriété externe de mon contrôle utilisateur de la façon suivante :

namespace MultiColumnComboBox
{
    public partial class MultiColumnComboBox : UserControl
    {
        [Category("Comportement"), Description(""), Browsable(true)]
        public event EventHandler SelectedIndexChanged;

Ainsi dans ma Form2 je peux faire alors :

namespace WindowsFormsApplication1
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();

            multiColumnComboBox1.SelectedIndexChanged += new EventHandler(multiColumnComboBox1_SelectedIndexChanged);
        }

4 - Récupérer l'item sélectionné au niveau de la Form2

Il ne reste plus qu'à exécuter multiColumnComboBox1_SelectedIndexChanged dans l'événement SelectedIndexChanged dans mon UserControl de la façon suivante :

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            SelectedItem = (sender as ComboBox).SelectedItem;
            EventHandler handler = this.SelectedIndexChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

Au passage je récupère l'item sélectionné dans une propriété exportée SelectedItem qui me permettra d'afficher le choix utilisateur dans la Form2. Et le tour est joué, j'ai bien un nouveau contrôle utilisateur que je pourrais utiliser de projets en projets :
Custom UserControl MultipleColumns ComboBox

Download Source Code

Customized UserControl in Winform