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