FAQ C++/CLI et VC++.Net
FAQ C++/CLI et VC++.NetConsultez toutes les FAQ
Nombre d'auteurs : 29, nombre de questions : 248, création le 22 février 2013
- Comment créer et afficher une nouvelle Winform ?
- Comment changer le curseur de mon application ?
- Comment annuler la fermeture de la session Windows ?
- Comment permettre à l'utilisateur de choisir un répertoire ?
- Comment permettre à l'utilisateur de choisir un fichier pour ouvrir un document ?
- Comment permettre à l'utilisateur de choisir un fichier pour enregistrer un document ?
- Comment accèder à une méthode publique d'une form à partir d'une autre form créé par la première ?
- Comment récupérer une valeur d'un contrôle depuis une autre Form (inclusions croisées et déclaration anticipée) ?
- Comment suspendre la capture d'un évènement ?
- Comment rendre un contrôle transparent ?
- Comment détecter la fermeture intempestive de l'application ?
- Comment créer des contrôles dynamiquement et gérer leurs événements ?
- Comment modifier dynamiquement l'icône d'un NotifyIcon ?
- Comment déplacer un contrôle avec la souris ?
- Comment charger une image dans un pictureBox tout en libérant les ressources ?
- Que faire avec l'erreur de compilation C3352 ?
- Comment mettre à jour un contrôle d'une winform depuis un thread ?
- Comment forcer une fenêtre à apparaître à l'avant plan ?
- Comment intercepter n'importe quelle exception non catchée dans une application Windows ?
- Comment connaître la longueur en pixel d'une chaîne de caractères ?
- Comment lister toutes les forms d'un projet ?
- Comment utiliser un raccourci clavier sur une form pour effectuer une action ?
- Comment fermer un formulaire en fondu ?
- Comment effectuer un binding bidirectionnel ?
- Comment écrire des informations dans une console dans mon projet Windows Forms ?
- 5.2.1. TextBox
(6)
- Comment intercepter les touches du clavier dans mon TextBox ?
- Comment ne saisir que des caractères numériques dans mon TextBox ?
- Comment assurer la saisie de nombres corrects dans une textbox avec les exceptions ?
- Comment assurer la saisie de nombres corrects dans une textbox avec les expressions régulières ?
- Comment mettre en place un système de suggestion ?
- Comment placer le curseur à la fin d'un textbox multiligne ?
- 5.2.2. TreeView (3)
- 5.2.3. ListView (2)
- 5.2.4. Label (1)
- 5.2.5. Button (2)
Il faut dans un premier temps créer une nouvelle Winform :
Click droit sur le projet --> Add -- New Item --> UI --> Winforms Form.
Là, nommez-la comme bon vous semble, par exemple Form2. Visual Studio génère donc une nouvelle classe, et la winform est modifiable également à travers l'IDE.
Pour afficher cette nouvelle form, par exemple depuis un click sur un bouton de la première form, il faut inclure le Form2.h dans le fichier .h de notre première Form (Form1.h par défaut).
#include
"Form2.h"
Ensuite dans l'événement du click sur le bouton (ou autre), il faut instancier la classe et appeler la méthode Show() sur l'objet créé.
Form2 ^
maForm2 =
gcnew Form2();
maForm2->
Show();
Notez que la méthode Show() affiche la fenêtre simplement. On peut également utiliser ShowModal() pour qu'elle soit modale comme une boîte de dialogue.
Lors de la réalisation d'applications, il peut être utile de montrer à l'utilisateur que le traitement demandé est en cours (et qu'il faut patienter).
Un bon moyen de réaliser ceci est de changer la forme du curseur, pendant la durée du traitement.
Pour ce faire, vous devez utiliser la Classe Cursors.
private
:
System::
Void button1_Click(System::
Object^
sender, System::
EventArgs^
e)
{
// On passe le curseur en sablier
Cursor =
Cursors::
WaitCursor;
System::Threading::Thread::
Sleep(4000
);
// On repasse le curseur en normal
Cursor =
Cursors::
Arrow;
}
N'hésitez pas à vous reporter à la classe Cursors pour voir les types de curseurs disponibles et utilisables par votre application.
Il faut tout d'abord gérer l'évènement FormClosing (depuis l'IDE ou par code).
this
->
FormClosing +=
gcnew System::Windows::Forms::
FormClosingEventHandler(this
, &
Form1::
Form1_FormClosing);
Ensuite, insérer ce petit bout de code dans notre classe, ce qui permet de détecter une tentative de fermeture de session en surchargeant la méthode de traitement des messages windows et en interceptant le message de fermeture de session.
Il ne reste plus qu'à l'annuler dans le FormClosing.
private
: bool
_systemShutdown; // a initialiser à false dans le constructeur
protected
:
virtual
void
WndProc(Message %
m) override
{
// Mise dans systemShutdown la présence du message fermeture Windows
if
(m.Msg ==
0x11
) // ou utiliser WM_QUERYENDSESSION de windows.h
_systemShutdown =
true
;
Form::
WndProc(m);
}
private
:
System::
Void Form1_FormClosing(System::
Object^
sender, System::Windows::Forms::
FormClosingEventArgs^
e)
{
//Si le message fermeture Windows a été envoyé, on l'annule !
if
(_systemShutdown)
{
e->
Cancel =
true
;
_systemShutdown =
false
;
MessageBox::
Show("Fermeture de session windows annulée"
);
}
}
Pour tester ce code, éxecutez le en dehors de l'IDE.
On utilise le composant FolderBrowserDialog pour cela.
private
:
System::
Void button1_Click(System::
Object^
sender, System::
EventArgs^
e)
{
folderBrowserDialog1->
Description =
"Choisissez votre répertoire"
;
if
(folderBrowserDialog1->
ShowDialog(this
) ==
System::Windows::Forms::DialogResult::
OK)
{
MessageBox::
Show(this
,
"Vous aves choisi "
+
folderBrowserDialog1->
SelectedPath,
"Repertoire"
,
MessageBoxButtons::
OK,
MessageBoxIcon::
Information);
}
}
Le composant System::Windows::Form::OpenFileDialog permet à l'utilisateur de choisir interactivement un fichier afin d'y lire des données.
Créez une form et placez-y un bouton nommé button1, un composant RichTextBox nommé richTextBox1 et un composant OpenFileDialog nommé openFileDialog1.
Un clic sur le bouton1 permet de lire le fichier choisi et d'afficher son contenu dans le RichTextBox.
private
:
System::
Void button1_Click(System::
Object^
sender, System::
EventArgs^
e)
{
// Titre
openFileDialog1->
Title =
"Chargement"
;
// Extension par défaut
openFileDialog1->
DefaultExt =
"txt"
;
// Filtre sur les fichiers
openFileDialog1->
Filter =
"fichiers textes (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
;
openFileDialog1->
FilterIndex =
1
;
// Ouverture boite de dialogue OpenFile
if
(openFileDialog1->
ShowDialog(this
) ==
Windows::Forms::DialogResult::
OK)
{
// On vide le TextBox
richTextBox1->
Text =
String::
Empty;
// Ouverture du fichier sélectionné
// son nom est dans openFileDialog1->FileName
IO::
StreamReader ^
sr =
gcnew IO::
StreamReader(openFileDialog1->
OpenFile(), System::Text::Encoding::
Default);
try
{
richTextBox1->
Text =
sr->
ReadToEnd();
}
finally
{
if
(sr!=
nullptr
)
sr->
Close();
}
}
}
Le composant System::Windows::Form::SaveFileDialog permet à l'utilisateur de choisir interactivement un fichier afin d'y écrire des données.
Créez une form et placez-y un bouton nommé button1, un composant RichTextBox nommé richTextBox1 et un composant SaveFileDialog nommé saveFileDialog1.
Un clic sur le bouton1 permet de sauvegarder le contenu du RichTextBox vers le fichier choisi.
private
:
System::
Void button1_Click(System::
Object^
sender, System::
EventArgs^
e)
{
// Demande de confirmation
if
(MessageBox::
Show(this
,
"Sauvegarder le document?"
,
"Sauvegarde"
,
MessageBoxButtons::
YesNo,
MessageBoxIcon::
Question) ==
Windows::Forms::DialogResult::
No)
return
;
// Sauvegarde du document
saveFileDialog1->
Title =
"Sauvegarde"
;
saveFileDialog1->
DefaultExt =
"txt"
;
saveFileDialog1->
Filter =
"fichiers textes (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
;
saveFileDialog1->
FilterIndex =
1
;
// Ouverture boite de dialogue Enregistrer
if
(saveFileDialog1->
ShowDialog(this
) ==
Windows::Forms::DialogResult::
OK)
{
// StreamWriter pour écrire dans le fichier sélectionné
IO::
StreamWriter ^
sw =
gcnew IO::
StreamWriter(saveFileDialog1->
OpenFile(), System::Text::Encoding::
Default);
try
{
for
(int
i=
0
; i<
richTextBox1->
Lines->
Length; i++
)
sw->
WriteLine(richTextBox1->
Lines[i]);
}
finally
{
// Fermeture writer
if
(sw!=
nullptr
)
sw->
Close();
}
}
}
Afin de pouvoir communiquer à partir d'une form nouvellement ouverte vers la form créatrice vous devez passer la form créatrice à la nouvelle form.
//Dans Form1
public
void
TraitementForm1()
{
}
public
void
OuvertureForm2()
{
form2 ^
maForm2 =
gcnew form2();
maForm2->
Owner =
this
;
maForm2->
Show();
}
//Dans MaForm2
private
void
ButtonOk_Click(object sender, System.EventArgs e)
{
Form1 ^
maForm1 =
safe_cast<
Form1 ^>
(this
->
Owner);
maForm1->
TraitementForm1();
}
...
Lorsqu'une winform 1 doit instancier une winform 2 qui doit connaitre la winform 1, on est obligé de faire un include de form1.h dans form2.h et inversement de form2.h dans form1.h.
Ceci amène à des références croisées qui provoquent des erreurs de compilation.
Imaginons que nous voulions faire une application qui ouvre une fenêtre qui possède un textBox et un bouton. Lors du click sur le bouton, on affiche une deuxième form qui possède elle aussi un bouton et un textBox (ça aurait pu être un label). On souhaite lors du clic sur le bouton de la deuxième form afficher la valeur saisie dans le textBox de la première form...
Voici comment on peut faire :
Dans la form1, je dépose le textBox1 et le bouton.
J'ajoute une nouvelle form, Form2 dans laquelle je mets le textbox et le bouton.
le but est de pouvoir lire la valeur saisie dans la Form1 depuis la Form2 sachant que c'est la form1 qui va créer la form2.
Donc, en toute logique, dans la form1, on va avoir à un endroit la création et l'affichage de la form2. Comme il faut que la form2 connaisse la form1, on va lui passer l'objet form1 (c'est à dire this), disons dans le constructeur :
Form2 ^
maForm2 =
gcnew Form2(this
);
maForm2->
Show();
Dans la form2, on aura donc fatalement la récupération de la form1 dans le constructeur :
Form2(Form1 ^
laForm1)
{
Form1 ^
currForm1 =
laForm1; // sachant qu'en vrai, on aura currForm1 comme membre privé de la classe
}
et au moment de récupérer la valeur, on aura un
textBox1->
Text =
currForm1->
GetValeur();
GetValeur sera bien sur une méthode publique de form1 :
public
:
String ^
GetValeur()
{
return
textBox1->
Text;
}
Tant est si bien que dans Form1.h, on va devoir faire un
#include "Form2.h"
pour pouvoir instancier la form2
et dans la Form2.h, on va devoir faire un
#include
"Form1.h"
pour pouvoir utiliser la form1.
Si vous essayez de compiler directement comme ca, il va y avoir une tripotée d'erreurs, la plus significative étant qu'il ne connait pas Form2, avec une erreur du genre dans Form1.h :
error C2143: erreur de syntaxe : absence de ';' avant '^'
sur la ligne Form2 ^ currForm2;
Comme on s'en doute, chaque fichier incluant l'autre, il y a des inclusions croisées qui empêchent la bonne compilation.
Pour que ceci compile, il faut plusieurs choses :
Déjà, déporter l'implémentation des méthodes des classes dans un .cpp (plus précisément, toutes celles qui utilisent des méthodes ou des attributs de Form2 pour le fichier form1.h et toutes celles qui utilisent des méthodes ou des attributs de Form1 pour form2.h).
Donc, il faut créer un fichier Form1.cpp (form2.cpp étant généré automatiquement lors de l'ajout d'une nouvelle form).
Dans mon .h, j'ai donc laissé la définition des méthodes :
private
:
System::
Void Form1_Load(System::
Object^
sender, System::
EventArgs^
e);
private
:
System::
Void button1_Click(System::
Object^
sender, System::
EventArgs^
e);
et dans le .cpp, j'ai mis l'implémentation des méthodes :
include "StdAfx.h"
#include
"Form1.h"
namespace
testWinforms {
System::
Void Form1::
Form1_Load(System::
Object^
sender, System::
EventArgs^
e)
{
currForm2 =
gcnew Form2(this
);
}
System::
Void Form1::
button1_Click(System::
Object^
sender, System::
EventArgs^
e)
{
currForm2->
Show();
}
}
NB : ne pas oublier le namespace, ni l'opérateur de résolution de portée. Idem dans form2.h
private
:
System::
Void button1_Click(System::
Object^
sender, System::
EventArgs^
e);
et dans le Form2.cpp
System::
Void Form2::
button1_Click(System::
Object^
sender, System::
EventArgs^
e)
{
textBox1->
Text =
currForm1->
GetValeur();
}
Voilà pour le premier point.
Il reste encore à faire une chose pour que ça compile, c'est faire une déclaration anticipée de Form1 dans le fichier Form2.h.
par exemple, en premier dans le namespace
ref class
Form1;
il restera plus qu'à faire de même dans le fichier form1.h : déclarer form2 de cette facon :
ref class
Form2;
Compilez maintenant, tout est ok
Téléchargez le programme d'exemple
Suite à une action utilisateur sur le clavier, nous devons parfois effectuer un long traitement. Et nous souhaiterions que les actions clavier ne soient pas enregistrées pendant ce traitement. Cette suspension peut être implémentée de la façon suivante :
private
:
System::
Void button1_KeyUp(System::
Object^
sender, System::Windows::Forms::
KeyEventArgs^
e)
{
//Exemple après l'appui de la touche "Enter"
if
(e->
KeyCode ==
Keys::
Enter)
{
//Arrêter la capture d'événements clavier sur le contrôle
this
->
button1->
KeyUp -=
gcnew KeyEventHandler(this
, &
Form1::
button1_KeyUp);
//Traitements longs
//Reprendre la capture d'événements clavier sur le contrôle
this
->
button1->
KeyUp +=
gcnew KeyEventHandler(this
, &
Form1::
button1_KeyUp);
}
}
Dans le constructeur, il suffit d'ajouter :
this
->
SetStyle(ControlStyles::
SupportsTransparentBackColor, true
);
this
->
BackColor =
Color::
Transparent;
après le InitializeComponents().
Parfois, nous avons besoin de distinguer si la fenêtre de l'application a été fermée par un click sur un contrôle prévu à cet effet, ou si le click a été effectué sur la petite croix en haut à droite de la fenêtre.
Pour ce faire, nous avons juste besoin de savoir que l'évènement concerné est lié à l'Application et non au contrôle Form.
Voici un exemple de code test, qui vous démontrera que seule la fermeture intempestive de l'application déclenchera le MessageBox.
Application::
ApplicationExit +=
gcnew EventHandler(this
, &
Form1::
Application_Exit);
.....
private
:
void
Application_Exit(System::
Object^
sender, System::
EventArgs^
e)
{
MessageBox::
Show("Adieu vilaine brute"
);
}
private
:
System::
Void button1_Click(System::
Object^
sender, System::
EventArgs^
e)
{
Application::
Exit();
}
Ce code permet de créer 10 contrôles CheckBox, de les ajouter dans une Listbox et de gérer leur évènements CheckedChanged. Il nécessite la présence d'un contrôe ListBox nommé "listBox1" et d'un bouton nommé "BTN".
private
:
System::
Void BTN_Click(System::
Object^
sender, System::
EventArgs^
e)
{
CheckBox^
x;
for
( int
i =
1
; i <=
10
; i++
)
{
x =
gcnew CheckBox();
x->
Name =
"Macase"
+
Convert::
ToString(i);
// on place les CheckBox les unes en dessous des autres
x->
Left =
10
;
x->
Top =
i *
20
;
x->
Width =
150
;
x->
Text =
"Je suis la case "
+
Convert::
ToString(i);
// abonner CheckedChanged à la méthode commune MesCasesCheckedChanged
x->
CheckedChanged +=
gcnew EventHandler(this
, &
Form1::
MesCasesCheckedChanged);
// ajouter les checkbox à la ListBox
this
->
listBox1->
Controls->
Add(x);
}
}
private
: void
MesCasesCheckedChanged(Object^
sender, EventArgs^
e)
{
CheckBox^
cbx =
safe_cast<
CheckBox^>
(sender);
MessageBox::
Show("L'état de "
+
cbx->
Name +
" est "
+
(cbx->
Checked ? "coché"
: "non coché"
));
}
L'intérêt est ici de pouvoir modifier le NotifyIcon de notre application, afin par exemple d'y faire apparaître un texte en fonction de différentes circonstances.
L'exemple implémenté se contentera de faire ici défiler dans l'objet notifyIcon1 les secondes d'un objet DateTime. La procédure est assez simple puisqu'il s'agit de travailler via un objet Bitmap pour la modification et de le convertir en Icon à assigner à la propriété notifyIcon1->Icon.
Ce qu'il ne faut absolument pas oublier c'est de détruire au fur et à mesure les icônes en mémoire.
Le code suivant ne montre que l'essentiel, il vous faut bien sûr en plus déclarer et instancier les objets Font, Brush, Color et StringFormat souhaités.
private
:
///
<
summary
>
Méthode privée timer1_Tick
///
gestionnaire de l'évènement Tick de timer1
///
<
/summary
>
System::
Void timer1_Tick(System::
Object^
sender, System::
EventArgs^
e)
{
// lancer la mise à jour de notifyIcon1
System::
DateTime^
dt =
System::DateTime::
Now;
this
->
UpdateNotiFyIcon((safe_cast<
Int32^>
(dt->
Second))->
ToString());
}
///
<
summary
>
Méthode privée UpdateNotiFyIcon
///
chargée de mofifier notifyIcon1 dynamiquement
///
<
/summary
>
///
<
param
name
=
"texte"
>
String : représente le texte à afficher
///
<
/param
>
void
UpdateNotiFyIcon(String^
texte)
{
// redessiner iconBitmap
this
->
UpdateBitmap(texte);
// récupérer un icone à patir de iconBitmap
System::Drawing::
Icon^
newIcon =
System::Drawing::Icon::
FromHandle(this
->
iconBitmap->
GetHicon());
// assigner le nouvel icône de NotifyIcon1
this
->
notifyIcon1->
Icon =
newIcon;
// détruire en mémoire newIcon
delete
(newIcon);
}
///
<
summary
>
///
Méthode privée chargée de redessiner iconBitmap
///
en fonction d'un texte à afficher
///
<
/summary
>
///
<
param
name
=
"texte"
>
String : représentant le texte à afficher
///
<
/param
>
void
UpdateBitmap(String^
texte)
{
Graphics^
g =
Graphics::
FromImage(this
->
iconBitmap);
// assigner la couleur de fond
g->
Clear(this
->
iconBackColor);
// dessiner le texte
g->
DrawString(texte, this
->
iconFont,this
->
iconForeBrush,14
,14
,this
->
iconStringFormat);
// libérer l'objet Graphics
delete
(g);
}
Pour déplacer un contrôle, par exemple un PictureBox, nous avons besoin de 2 informations :
- Le bouton gauche vient-il d'être enfoncé ?
- La souris se déplace-t-elle en étant au dessus du PictureBox ?
Le Framework met justement à notre disposition 2 évènements nous permettant de connaître ces 2 informations :
- MouseDown : notifie si le bouton de la souris a été enfoncé mais aussi à quel endroit;
- MouseMove : notifie si la souris se déplace.
Il suffit donc de traiter le premier évènement afin de connaître la position d'origine du clic et dans le second évènement, nous repositionnerons le PictureBox par rapport au déplacement de la souris.
///
<
summary
>
Position de la souris lorsque le bouton a été enfoncé
<
/summary
>
Point ^
positionClick;
///
<
summary
>
///
Notifie si le bouton de la souris a été enfoncé sur le PictuBox
///
<
/summary
>
private
:
System::
Void pictureBox1_MouseDown(System::
Object^
sender, System::Windows::Forms::
MouseEventArgs^
e)
{
// Vérification si bouton gauche de la souris est bien enfoncé
if
(e->
Button ==
System::Windows::Forms::MouseButtons::
Left)
positionClick =
e->
Location;
}
///
<
summary
>
///
Notifie si le curseur se déplace au dessus du PictureBox
///
<
/summary
>
private
:
System::
Void pictureBox1_MouseMove(System::
Object^
sender, System::Windows::Forms::
MouseEventArgs^
e)
{
// Vérification si bouton gauche de la souris est bien enfoncé
if
(e->
Button ==
System::Windows::Forms::MouseButtons::
Left)
{
// Calcul de la nouvelle position du PictureBox
pictureBox1->
Location =
Point(pictureBox1->
Location.X +
e->
X -
positionClick->
X,
pictureBox1->
Location.Y +
e->
Y -
positionClick->
Y);
}
}
A noter que ce code peut être utilisé pour les contrôles qui proposent les évènements MouseDown et MouseMove.
Lors du chargement d'une image dans un PictureBox avec la fonction Image::FromFile, le fichier sous-jascent se trouve en utilisation et donc non disponible jusqu'à la fermeture de l'application. Il est ainsi par exemple impossible de le supprimer.
Pour remédier à ce problème, il faut en conséquence pouvoir libérer la ressource du flux sur ce fichier.
Plusieurs solutions permettent d'atteindre cet objectif. Nous vous proposons ici celui de gérer directement le flux et notamment par l'implémentation de la méthode Image::FromStream.
L'exemple suivant charge une image via un FileStream, l'affecte à un PictureBox (appelé pictureBox1) par Image::FromStream, puis libère les ressources du flux (appelé photoStream) sur le fichier pour pouvoir le supprimer :
// Créer le FileStream sur le fichier monimage.jpg
FileStream ^
photoStream =
gcnew FileStream("C:
\\
monimage.jpg"
, FileMode::
Open);
// affecter l'image à pictureBox1
pictureBox1->
Image =
Image::
FromStream(photoStream);
// libérer les ressources
photoStream->
Close();
// supprimer le fichier monimage.jpg
File::
Delete("C:
\\
monimage.jpg"
);
Lien : System.IO.FileStream
Lien : Image.FromStream
Cette erreur survient lorsqu'on essaie de déclarer un délégate, en s'inspirant sur la syntaxe C#.
Si on part de cette syntaxe, on aura tendance à arriver à :
delegate void
MonDelegateHandler(String ^
chaine);
void
methodeX ()
{
MonDelegateHandler ^
monDelegate =
gcnew MonDelegateHandler(&
Form1::
MethodeDuDelegate);
}
void
MethodeDuDelegate(String ^
chaine)
{
}
Ceci est incorrect et produit l'erreur C3352.
La construction correcte est la suivante :
MonDelegateHandler ^
monDelegate =
gcnew MonDelegateHandler(this
, &
Form1::
MethodeDuDelegate);
Il peut arriver qu'on doive mettre à jour un contrôle, comme une barre de progression ou un textbox, lors d'un traitement de longue durée.
Ce traitement doit bien sûr être mis dans un thread pour éviter de bloquer l'application.
Cependant, on ne peut pas directement mettre à jour les contrôles depuis un thread, il faut passer par une méthode particulière. On utilisera un delegate et la méthode invoke.
Voici un exemple pour une barre de progression. Je déclare un délegate et une méthode qui s'occupe de faire avancer ma barre de progression :
delegate void
ProgressBarDelegateHandler();
ProgressBarDelegateHandler ^
ProgressBarDelegate;
void
MyUpdateProgressBar()
{
progressBar1->
PerformStep();
}
Dans le form_load ou dans le constructeur, on instancie le delegate :
ProgressBarDelegate =
gcnew ProgressBarDelegateHandler(this
, &
Form1::
MyUpdateProgressBar);
Et par exemple sur le click d'un bouton, on initialise la barre de progression et on démarre le thread qui ici, ne fait pas grand chose à part attendre une seconde à chaque itération :
System::
Void button2_Click(System::
Object^
sender, System::
EventArgs^
e)
{
this
->
progressBar1->
Minimum =
0
;
this
->
progressBar1->
Maximum =
10
;
this
->
progressBar1->
Value =
0
;
this
->
progressBar1->
Step =
1
;
Threading::
Thread ^
t =
gcnew Threading::
Thread(gcnew Threading::
ThreadStart(this
, &
Form1::
ExecuteLongTraitement));
t->
Start();
}
void
ExecuteLongTraitement()
{
for
(int
i =
0
; i <
10
; i++
)
{
System::Threading::Thread::
Sleep(1000
);
this
->
Invoke(ProgressBarDelegate);
}
}
Il suffit tout simplement de mettre à true la propriété TopMost de la fenêtre :
this
->
TopMost =
true
;
L'objet application offre la possibilité de s'abonner à l'évènement ThreadException permettant d'être notifié lorsqu'un thread génère une exception non interceptée. Etant donné que la fenêtre est gérée par un thread, vous pourrez ainsi éviter tout "plantage" de votre application ou d'effectuer un traitement adapté avant la fermeture de votre application.
static
void
Application_ThreadException(Object ^
sender, System::Threading::
ThreadExceptionEventArgs ^
e)
{
System::Windows::Forms::MessageBox::
Show("L'exeption générée est : "
+
e->
Exception->
Message);
}
[STAThreadAttribute]
int
main(array<
System::
String ^>
^
args)
{
// Enabling Windows XP visual effects before any controls are created
Application::
EnableVisualStyles();
Application::
SetCompatibleTextRenderingDefault(false
);
Application::
ThreadException +=
gcnew System::Threading::
ThreadExceptionEventHandler(Application_ThreadException);
// Create the main window and run it
Application::
Run(gcnew Form1());
return
0
;
}
La classe Graphics possède une méthode MeasureString permettant de connaître la taille exacte d'une chaîne de caractères en fonction de la Font utilisée :
System::Drawing::
Graphics ^
g =
textBox1->
CreateGraphics();
System::Drawing::
SizeF ^
objSizeF =
g->
MeasureString(textBox1->
Text, textBox1->
Font);
MessageBox::
Show(String::
Format("Votre texte '{0}' a une longueur de {1} pixels."
, textBox1->
Text, objSizeF->
Width));
On pourra utiliser la reflexion pour savoir si un type est de type Form.
List<
Form ^>
^
ListerForms()
{
List<
Form ^>
^
resultat =
gcnew List<
Form ^>
();
Reflection::
Assembly ^
a =
System::Reflection::Assembly::
GetAssembly(GetType());
for
each (Type ^
t in a->
GetTypes())
{
if
(Form::
typeid
->
IsAssignableFrom(t))
{
Form ^
f =
(Form ^
)Activator::
CreateInstance(t);
resultat->
Add(f);
}
}
return
resultat;
}
il faut mettre en préambule l'attribut KeyPreview de la form à true, pour dire que la form doit traiter tous les événements clavier en premier et ensuite surcharger le KeyDown de la form.
Exemple pour faire un raccourci CTRL+M :
System::
Void Form1_KeyDown(System::
Object^
sender, System::Windows::Forms::
KeyEventArgs^
e)
{
if
(e->
Control &&
e->
KeyCode ==
Keys::
M)
{
MessageBox::
Show("Control + M"
);
}
}
Il est facile de fermer un formulaire en fondu. Il suffit de jouer sur l'opacité de la form :
for
(double
dblOpacity =
1
; dblOpacity >
0
; dblOpacity +=
-
0.05
)
{
Opacity =
dblOpacity;
//laisse le formulaire se recolorer
Refresh();
//crée un délai
System::Threading::Thread::
Sleep(50
);
}
Close();
Le binding bidirectionnel consiste à "associer" une propriété de classe à un controle de Formulaire.
Par exemple, j'ai un textbox sur ma form qui est associé à une chaine (String) dans ma classe. Toute modification de ce textbox entraine automatiquement une modification de ma chaine. Inversement, toute modification de cette chaine en code, implique une répercution visuelle sur la valeur du textbox.
Le principe est d'utiliser l'interface INotifyPropertyChanged. L'appel de l'événement PropertyChanged permet d'informer que la valeur a changé et automatiquement répercuter ce changement.
Tout d'abord, la form doit implémenter INotifyPropertyChanged
public
ref class
Form1 : public
System::Windows::Forms::
Form, INotifyPropertyChanged
Ceci implique de définir l'événement suivant
virtual
event PropertyChangedEventHandler ^
PropertyChanged;
Il nous faut ensuite une propriété de type String. C'est cette propriété qui sera bindée à notre TextBox :
private
:
String ^
_chaine;
public
:
property String ^
Chaine
{
String ^
get() {
return
_chaine; }
void
set(String ^
value)
{
if
(value !=
_chaine)
{
_chaine =
value;
NotifyPropertyChanged("Chaine"
);
}
}
}
On note l'appel à la méthode NotifyPropertyChanged pour indiquer une changement de valeur.
private
:
void
NotifyPropertyChanged(String ^
info)
{
PropertyChanged(this
, gcnew PropertyChangedEventArgs(info));
}
La méthode NotifyPropertyChanged appelle l'événement PropertyChanged pour signaler le changement afin qu'il soit répercuté à tous les endroits où le binding est effectué.
Ne pas oublier de définir le binding, au plus tot, dans le form_load par exemple :
textBox1->
DataBindings->
Add("Text"
, this
, "Chaine"
);
Pour faire ceci, il faut déjà disposer d'un projet Console. Pour changer son projet en un projet console, il faut ouvrir les propriétés du projet, dans Linker -> System, il faut régler le subsystem à Console.
Ensuite il faut appeler :
AttachConsole( ATTACH_PARENT_PROCESS );
Pour le permettre, il faudra inclure windows.h :
#define WIN32_LEAN_AND_MEAN
#include
<windows.h>
De manière classique, on pourra afficher une form ainsi :
Application::
EnableVisualStyles();
Application::
SetCompatibleTextRenderingDefault( false
);
Application::
Run(gcnew testConsoleWinform::
Form1());
Tout appel à Console::WriteLine sera désormais écrit dans la console.