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