WinForms

De Banane Atomic
Aller à la navigationAller à la recherche

Événements Initialize, Load et Activate

  • Initialize: appelé lors de la création de la Form
  • Load: appelé lorsque la Form est chargée en mémoire
  • Activate: appelé à chaque fois que la Form devient la fenêtre active

MSDN

Dock et superposition

ElementA docké en Fill et ElementB docké en bottom.
ElementB se retrouve par dessous ElementA et masque le bas d'ElementA.
Solutions:

  • Right click sur ElementA and select “Bring to Front”
  • Ajouter ce code:
40px
private void Form_Load(object sender, EventArgs e)
{
    ElementA.BringToFront();

Centrer un Label

AutoSize → False
TextAlign → MiddleCenter

Simuler une entrée au clavier

[1]

40px
SendKeys.Send("{Tab}");

Menu Contextuel

Ajouter un ContextMenuStrip → drag-drop depuis la boite à outils.
Lier le CMS à un élément graphique → via la propriété ContextMenuStrip
Ajouter des éléments au menu → via la propriété Items ou via la petite flèches au dessus du CMS dans le bas de la vue du designer
Lier un événement à la sélection d'un menu → sélectionner l'élément du menu dans la vue du designer et ajouter un handler sur l'évènement Click
La propriété ShotcutKeys permet d'associer un raccourci clavier à un élément du menu.

40px
// récupération du control contenant le menu contextuel
private void tsmi_Click(object sender, EventArgs e)
{
    var menu = (ToolStripDropDownItem)sender;
    var strip = (ContextMenuStrip)menu.Owner;
    // SourceControl est null lors de l'utilisation des raccourcis clavier
    var control = (DataGridView)strip.SourceControl;

    // Dans ce cas et si le control n'est pas dans un SplitContainer
    var control = (DataGridView)ActiveControl;
}

Form

Propriétés intéressantes

  • ControlBox → Affiche/cache l'icône en haut à gauche ainsi que les boutons réduire, grandir et fermer en haut à droite.

Interdire le redimensionnement

  • Propriété FormBorderStyle → FixedSingle
  • Propriété MaximizeBox → false

AutoScaleMode

Détermine le comportement du formulaire lors d'un changement de la résolution (dpi) ou de la police (font).
Choisir l'AutoScaleMode du formulaire principal, les sous formulaires peuvent hériter de l'AutoScaleMode de leur parent (Inherit).
La valeur par défaut est font, cela peut causer des problèmes d'affichage. Préférer dpi ou none.

Boutons OK et CANCEL dans une Form

Il faut affecter respectivement les valeurs OK et CANCEL à la propriété DialogResult des boutons.

40px
NewForm form = new NewForm();
if (form.ShowDialog(this) == DialogResult.OK) // fenetre modale
{ ... }
form.Dispose();

Thread graphique et thread de calcul

Pour que le formulaire soit toujours visible et interactif, il est nécessaire de lancer un second thread qui s'occupera des calcul pendant que le premier thread (thread graphique) s'occupera de l'affichage du formulaire.
Attention, le thread de calcul ne peut pas modifier directement les éléments du formulaire car ils appartiennent à un autre thread. Il faut donc utiliser les callback.

40px
Thread thread = new Thread(new ThreadStart(delegate()
{ // code qui doit être réalisé par le thread de calcul }));

// ou
Thread thread = new Thread(methode);

thread.Start();

Callback et thread-safe

Fonction de rappel, permet de passer un pointeur sur fonction en paramètre (délégué).
Dans cet exemple, c’est une méthode qui s’appelle elle-même de manière asynchrone via un délégué.

40px
public class MainForm : Form
{
  delegate void mainFormCallback(string text);

  private void button_Click(object sender, EventArgs e)
  {
    Thread thread = new Thread(new ThreadStart(delegate()
      {
          SetText("OK");
      });
    thread.Start();
  }
  private void SetText (string text)
  {
    // InvokeRequired compares the thread ID of the
    // calling thread to the thread ID of the creating thread.
    // If these threads are different, it returns true.
    if (this.button.InvokeRequired)
    {   // calls itself asynchronously
        mainFormCallback callback = new mainFormCallback(SetText);
        this.Invoke(callback, text);
    }
    else
    {
        button.Text = text;
    }
  }
}

[2]

DataGridView

Coller des données provenant d'Excel

40px
// récupération du contenu du presse papier
string clipboardText = Clipboard.GetText();

// séparation par lignes et suppression des lignes vides
string[] clipboardLines = clipboardText.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

// séparation par cellules afin de connaitre le nombre d'éléments à coller
var clipboardElements = new List<string>();
foreach (var line in clipboardLines)
{
    clipboardElements.AddRange(line.Split('\t'));
}

// trie des éléments dans l'ordre croissant car les SelectedCells sont dans l'ordre de la sélection
// si elle s'est faite de gauche à droite et/ou de haut en bas
// les cellules sont en ordre décroissant d'index
var orderedSelectedCells = new List<DataGridViewCell>();
foreach (DataGridViewCell cell in dataGridView1.SelectedCells)
{
    orderedSelectedCells.Add(cell);
}
orderedSelectedCells.Sort((dgvc1, dgvc2) =>
{
    var rowCompare = dgvc1.RowIndex.CompareTo(dgvc2.RowIndex);
    if (rowCompare != 0) return rowCompare;
    else
    {
        return dgvc1.ColumnIndex.CompareTo(dgvc2.ColumnIndex);
    }
});

// seulement si le nombre d'éléments à copier est <= à l'espace sélectionné pour la copie
if (clipboardElements.Count <= orderedSelectedCells.Count)
{
    for (int i = 0; i < clipboardElements.Count; i++)
    {
        orderedSelectedCells[i].Value =
            Convert.ChangeType(clipboardElements[i], orderedSelectedCells[i].ValueType);
    }
}

// si le mode d'édition est EditOnEnter, la dernière cellule collée est toujours en édition
// et son contenu visuellement pas à jour.
// pour corrigé ça on déplace le focus sur la cellule suivante ou sur la première
int newColumnIndex = 0;
if (dgv.CurrentCell.ColumnIndex + 1 <= dgv.Columns.Count)
{
    newColumnIndex = dgv.CurrentCell.ColumnIndex + 1;
}
// cette ligne peut générer une exception si la cellule est hidden
dgv.CurrentCell = dgv[newColumnIndex, dgv.CurrentCell.RowIndex];

EditMode

EditOnKeyStrokeOrF2 : Mode par défaut, il faut double-cliquer sur une cellule pour l’éditer.
EditOnEnter : Dans ce mode il suffit de cliquer sur la cellule pour l’éditer. Par contre un clique sur le header d’une ligne édite la première cellule, ce qui empêche de supprimer la ligne.
EditOnEnter et suppression de ligne :

40px
private void DGV_MouseClick(object sender, MouseEventArgs e)
{
    DataGridView.HitTestInfo hitInfo = this.DGV.HitTest(e.X, e.Y);
    if (hitInfo.Type == DataGridViewHitTestType.RowHeader)
    {
        this.DGV.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
        this.DGV.EndEdit();
    }
    else
    {
        this.DGV.EditMode = DataGridViewEditMode.EditOnEnter;
    }
}

Tester les données des cellules dès leurs saisies

La validation bloque l’utilisateur tant qu’il n’a pas saisie de valeur valide et affiche un message d’erreur.

40px
private void dataGridView_CellValidating(object sender,
  DataGridViewCellValidatingEventArgs e)
{
    DataGridView dgv = (DataGridView)sender;
    if (dgv.Columns[e.ColumnIndex] == "ColumnName")
    {
        if (string.IsNullOrEmpty(e.FormattedValue.ToString())
        {
            dgv.Rows[e.RowIndex].ErrorText = "Cell must not be empty";
            e.Cancel = true;
        }
    }
}
private void cduDataGridView_CellEndEdit(object sender,
  DataGridViewCellEventArgs e)
{
    ((DataGridView)sender).Rows[e.RowIndex].ErrorText = String.Empty;
}

ComboBox

L'événement SelectionChangeCommitted, à la différence des événements SelectedIndexChanged ou SelectedValueChanged, ne se produit que lorsque l'utilisateur et non le programme modifie la sélection de la liste déroulante.

Pop-up message

40px
MessageBox.Show("message", "titre", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);

Progress Bar et Timer

40px
public partial class Form1 : Form
{
    ToDO toDO;
    public Form1()
    {
        progressBar1.Minimum = 1;
        progressBar1.Maximum = 20;
        progressBar1.Value = 1;
        progressBar1.Step = 1;
        toDO = new ToDO();
        toDO.ReportStatusUpdated +=
            new ToDO.Delegate(toDO_ReportStatusUpdated);
        toDO.EndOfWork += new ToDO.Delegate(toDO_EndOfWork);
    }
    void toDO_EndOfWork()
    {
        button1.Text = "OK";
    }
    void toDO_ReportStatusUpdated()
    {
        progressBar1.PerformStep();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        toDO.DoWork();
    }
}
public class ToDO
{
    public delegate void Delegate();
    public event Delegate ReportStatusUpdated;
    public event Delegate EndOfWork;
    Timer timer;
    int count = 1;
    void timer_Tick(object sender, EventArgs e)
    {
        if (count++ > 19)
            WorkFinished();
        else
            ReportStatusUpdated();
    }
    private void WorkFinished()
    {
    timer.Enabled = false;
        EndOfWork();
    }
    public void DoWork()
    {
        timer = new Timer();
        timer.Interval = 1000;
        timer.Tick += new EventHandler(timer_Tick);
        timer.Enabled = true;
    }
}