Création d'une winform non rectangulaire en C++/CLI avec Visual Studio 2005
Date de publication : 04/05/2006 , Date de mise à jour : 04/05/2006
Par
Nico-pyright(c) (Page d'accueil) Ce tutoriel présente deux techniques pour construire une winform (form dotnet) non rectangulaire, avec l'apparence de notre choix en C++/CLI avec Visual Studio 2005.
1.Introduction
2.Première méthode : Limitée, mais aucune ligne de code
2.1.Création du projet
2.2.Création de l'image de fond de la form
2.3.Ajouter des contrôles à notre form
2.4.Gérer le déplacement de la form
2.5.Téléchargement du projet
3.Deuxième méthode : un peu de code et des régions
3.1.Création du projet et positionnement des contrôles
3.2.Chargement de l'image et création de la région
3.3.Gérer le déplacement de la form
3.4.Téléchargement du projet
4.Conclusion
Remerciements
Contact
1.Introduction
La programmation Windows classique nous a fortement habitué aux fenêtres rectangulaires.
Même si cela est possible, il est laborieux de construire des fenêtres avec l'API WIn32. Il faut alors se munir de régions et de beaucoup de courage. Cette tache est légèrement facilité avec les MFC, mais elle reste toujours un travail laborieux qui décourage rapidement le développeur.
Avec les winforms par contre, cette laborieuse tache se transforme immédiatement en une partie de plaisir. Je vais vous montrer dans ce tutoriel, deux méthodes pour créer des forms non rectangulaires avec votre langage favori, le C++/CLI.
Les exemples présentés dans ce tutoriel utiliseront le logiciel Visual Studio 2005 de Microsoft et plus particulièrement Visual C++ 2005 et le framework dotnet 2.0.
2.Première méthode : Limitée, mais aucune ligne de code
Je vais vous présenter ici une première façon de faire, qui a l'inconvénient de fonctionner uniquement sur des bureaux Windows dont le nombre maximum de couleurs est obligatoirement inférieur à 24 bits.
Cette résolution est en général bien souvent suffisante, mais cette méthode ne peut être garantie pour des résolutions supérieures.
En contrepartie de cette limitation, cette méthode fonctionne sans une seule ligne de code à ajouter pour la création de la form non rectangulaire.
2.1.Création du projet
Créez donc un nouveau projet Visual C++ 2005 --> CLR --> Windows Form Application.
2.2.Création de l'image de fond de la form
Créez ensuite une image bitmap représentant la fenêtre que vous voulez obtenir.
Le point important est d'avoir un fond d'une couleur choisie, qui va nous servir de couleur transparente. J'ai choisi le bleu de Microsoft Paint, c'est le bleu web classique. Voici mon image :
Associez la à la propriété BackgroudImage de votre form.
Définissez ensuite la propriété TransparencyKey et positionnez la couleur qui a servi de fond (dans mon cas, le bleu web (blue)).
2.3.Ajouter des contrôles à notre form
Vous pouvez ensuite ajouter un ou deux contrôles sur la form. N'oubliez pas de définir la propriété FormBorderStyle à None.
Voici ce que cela donne chez moi : (le label à sa propriété BackColor à Transparent).
Compilez le tout, et lancez, vous obtiendrez alors une superbe form non rectangulaire, correspondant à notre image.
 | Si cela ne fonctionne pas, c'est que vous devez avoir une résolution supérieure ou égal à 24 bits.
|
A l'exécution, on se rend vite compte que c'est insuffisant : on ne peut pas fermer la form ni la déplacer à la souris.
Nous avons effectivement enlevé la barre de titre, ce qui empêche le déplacement de la form. Nous n'avons plus également la croix qui permet de fermer l'application. Rassurez vous, un petit coup de ALT+F4 et vous pourrez fermer la fenetre.
Nous allons donc palier cet inconvénient, en développant notre propre implémentation du déplacement de la form.
Ici plusieurs solutions :
- vous pouvez par exemple traiter l'évènement MouseDown avec ce code :
ReleaseCapture();
SendMessage(Handle, WM_SYSCOMMAND, 0xF012, 0); |
Ceci présente des inconvénients à mon avis. On utilise déjà du code non managé, ce qui implique de compiler avec le mode /clr (uniquement) et de se priver des modes /clr:safe ou /clr:pure. Ensuite, ceci bloque l'utiliation des autres événements souris.
- utiliser une méthode classique où l'on utilise les évènements MouseDown, MouseUp et MouseMove:
Rajoutez ces deux membres privés à la classe.
System::Drawing::Point ^ mouseOffset;
bool isMouseDown; |
Initialisez ensuite le booleen à false dans le constructeur de la classe.
Enfin, traitez les évènements MouseUp, MouseDown et MouseMove comme suit :
private: System::Void Form1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)
{
int xOffset;
int yOffset;
if (e->Button == System::Windows::Forms::MouseButtons::Left)
{
System::Drawing::Size ^ size = SystemInformation::FrameBorderSize;
xOffset = -e->X - size->Width;
yOffset = -e->Y - SystemInformation::CaptionHeight - size->Height;
mouseOffset = gcnew System::Drawing::Point(xOffset, yOffset);
isMouseDown = true;
}
}
private: System::Void Form1_MouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)
{
if (e->Button == System::Windows::Forms::MouseButtons::Left)
isMouseDown = false;
}
private: System::Void Form1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)
{
if (isMouseDown)
{
System::Drawing::Point mousePos = System::Windows::Forms::Control::MousePosition;
mousePos.Offset(mouseOffset->X, mouseOffset->Y);
this->Location = mousePos;
}
} |
2.5.Téléchargement du projet
3.Deuxième méthode : un peu de code et des régions
Je vais vous présenter ici une deuxième façon de faire, qui utilise les régions du framework .Net. Toujours à partir d'une image, cette méthode présente un code relativement simple et adaptable facilement.
La contrepartie ici de cet effort est que vous n'êtes pas limité à une résolution de couleur.
3.1.Création du projet et positionnement des contrôles
On peut pour ce deuxième exemple, utiliser la même image de fond que pour le premier exemple.
Toujours à partir d'un projet Windows Form Application, on positionne les contrôles sur la form, et on change la propriété FormBorderStyle à None.
3.2.Chargement de l'image et création de la région
Passons maintenant au code :
Dans le constructeur, on va charger notre bitmap et l'associer à la propriété BackgroundImage de notre form.
Bitmap ^ imageDeFond = safe_cast<Bitmap ^>(Bitmap::FromFile("fond.bmp"));
this->Width = imageDeFond->Width;
this->Height = imageDeFond->Height;
this->Region = CreeRegionDepuisBitmap(imageDeFond);
this->BackgroundImage = imageDeFond; |
On remarque la fonction CreeRegionDepuisBitmap qui renvoi une région qui va nous servir à définir les contours de notre bitmap.
Voyons de plus prêt cette fonction :
Cette fonction parcours tous les pixels de l'image et crée une région de 1 pixel, si le pixel rencontré n'est pas un pixel que l'on défini comme transparent.
System::Drawing::Region ^ CreeRegionDepuisBitmap(Bitmap ^ image)
{
GraphicsUnit ^ aPixel = GraphicsUnit::Pixel;
GraphicsUnit % r_aPixel = *aPixel;
RectangleF rectangleImage = image->GetBounds(r_aPixel);
System::Drawing::Size ^ tailleImage = gcnew System::Drawing::Size(
Convert::ToInt32(rectangleImage.Width),
Convert::ToInt32(rectangleImage.Height));
System::Drawing::Drawing2D::GraphicsPath ^ regionGraphique = gcnew System::Drawing::Drawing2D::GraphicsPath();
System::Drawing::Size ^ size = SystemInformation::FrameBorderSize;
int deltaX = size->Width - 1;
int deltaY = SystemInformation::CaptionHeight + 3;
Color ^ couleurTransparente;
for (int y = 0; y < tailleImage->Height; y++)
{
for (int x = 0; x < tailleImage->Width; x++)
{
if (!couleurTransparente)
couleurTransparente = image->GetPixel(x, y);
if (image->GetPixel(x, y).ToArgb() != couleurTransparente->ToArgb())
{
Rectangle regionPixelAAjouter(x + deltaX, y + deltaY , 1, 1);
regionGraphique->AddRectangle(regionPixelAAjouter);
}
}
}
return gcnew System::Drawing::Region(regionGraphique);
} |
On commence par récupérer les coordonnés de l'image, puis on parcours l'image. On choisit que le premier pixel rencontré représente la couleur transparente.
Ensuite on compare le pixel en cours au pixel transparent et s'ils sont différents, on ajoute une région de 1 pixel sur 1.
Notez le delta en (X,Y) correspondant aux dimensions de la bordure. J'avoue avoir été obligé de tâtonner pour avoir un réglage exact, que je ne m'explique pas vraiment...
3.4.Téléchargement du projet
4.Conclusion
J'espère que cette article vous aura permis de comprendre comment créer une form non rectangulaire en C++/CLI.
Remarque : Il est en général assez inopportun de modifier les habitudes des utilisateurs en proposant des fenêtres non classiques. Il est important de veiller à l'ergonomie d'un logiciel et de ne pas perturber le fonctionnement classique des habitudes de l'utilisateur.
Cependant, il peut être très agréable de mettre un peu de fantaisie dans son application...
Remerciements
Je remercie toute l'équipe C++ pour leur relecture attentive du document.
Si vous constatez une erreur dans le tutorial, dans le source, dans la programmation ou pour toutes informations, n'hésitez pas à me contacter par mail, ou par le forum.
 
|