Graphiques existants dans Bitmap

J’écris un plugin pour un logiciel de trading (C #, Winforms, .NET 3.5) et j’aimerais tracer un curseur en croix sur un panneau ( ChartPanel ) qui contient des données qui pourraient être coûteuses à peindre. Ce que j’ai fait jusqu’à présent c’est:

  1. J’ai ajouté un CursorControl au panneau
    • ce CursorControl est positionné sur le panneau de dessin principal de sorte qu’il couvre toute sa surface
    • Enabled = false pour que tous les événements d’entrée soient transmis au ChartPanel parent
    • sa méthode Paint est implémentée de manière à tracer des lignes de haut en bas et de gauche à droite à la position actuelle de la souris
  2. Lorsque l’événement MouseMove est déclenché, j’ai deux possibilités:
    • A) Appelez ChartPanel.Invalidate() , mais comme je l’ai dit, les données sous-jacentes risquent d’être coûteuses à peindre, ce qui entraînerait une nouvelle mise à jour de la souris chaque fois que je déplace une souris, ce qui est faux (mais c’est le seul moyen de réussir ce travail à présent)
    • B) Appelez CursorControl.Invalidate() et avant que le curseur ne soit dessiné, je prendrais un instantané des données actuellement dessinées et le conserverais comme arrière-plan du curseur qui serait simplement restauré à chaque fois que le curseur devait être repeint … le problème avec c’est … je ne sais pas comment faire ça .

2.B. Cela voudrait dire:

  • Transformez l’object Graphics existant en Bitmap (il (le Graphics) m’est donné par la méthode Paint et je dois y peindre, je ne peux donc pas créer un nouvel object Graphics … peut-être que je me trompe, mais c’est la comme je le comprends)
  • avant que le réticule ne soit peint, restaurez le contenu graphique du bitmap et repeignez le réticule

Je ne peux pas contrôler le processus de repérage des données coûteuses. Je peux simplement accéder à mon CursorControl et à ses méthodes appelées via l’API.

Y a-t-il un moyen de stocker le contenu graphique existant dans Bitmap et de le restaurer plus tard? Ou existe-t-il un meilleur moyen de résoudre ce problème?


RÉSOLU: Après plusieurs heures d’essais et d’erreur, j’ai trouvé une solution efficace. Le logiciel que j’utilise comporte de nombreux problèmes qui ne peuvent pas être discutés en général, mais les principes fondamentaux sont clairs:

  • Les graphiques existants comportant des éléments déjà peints ne peuvent pas être convertis directement en bitmap, mais je devais utiliser la méthode panel.DrawToBitmap mentionnée pour la première fois dans la réponse de @ Gusman. J’étais au courant, je voulais l’éviter, mais j’ai finalement dû l’accepter, car cela semble être le seul moyen
  • Je souhaitais également éviter le double dessin de chaque image. La première peinture en forme de croix est toujours dessinée directement sur ChartPanel . Après que la souris se soit déplacée sans changer l’image du graphique, je trace un instantané de DrawToBitmap et procède comme décrit dans la réponse choisie.
  • Le contrôle doit être opaque (arrière-plan transparent non activé) afin que son actualisation n’appelle pas Paint sur ses contrôles parents (ce qui entraînerait le repeint de l’ensemble du graphique).

Je ressens toujours des clignotements occasionnels toutes les quelques secondes environ, mais je suppose que je peux comprendre cela d’une façon ou d’une autre. Bien que j’ai choisi la réponse de Gusman, je voudrais remercier toutes les personnes impliquées, car j’ai utilisé beaucoup d’autres astuces mentionnées dans d’autres réponses, telles que Panel.BackgroundImage, l’utilisation de la méthode Plot () à la place de Paint () pour verrouiller l’image, etc.

Pourquoi ne clonez-vous pas tous les graphiques dans ChartPanel sur votre CursorControl?

Tout le code ici doit être placé dans votre CursorControl.

Commencez par créer une propriété qui contiendra une référence au graphique et un événement à sa peinture, comme ceci:

 ChartPanel panel; public ChartPanel Panel { get{ return panel; } set{ if(panel != null) panel.Paint -= CloneAspect; panel = value; panel.Paint += CloneAspect; } } 

Définissez maintenant la fonction CloneAspect qui rendra l’apparence du contrôle au format bitmap chaque fois qu’une opération Paint aura été effectuée dans le panneau Graphique:

 Bitmap aspect; void CloneAspect(object sender, PaintEventArgs e) { if(aspect == null || aspect.Width != panel.Width || aspect.Height != panel.Height) { if(aspect != null) aspect.Dispose(); aspect = new Bitmap(panel.Width, panel.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); } panel.DrawToBitmap(aspect, new Rectangle(0,0, panel.Width, panel.Height); } 

Ensuite, dans la méthode OnPaint overriden, procédez comme suit:

 public override void OnPaint(PaintEventArgs e) { e.Graphics.DrawImage(aspect); //Now draw the cursor (...) } 

Et enfin, partout où vous créez le graphique et le curseur personnalisé que vous faites:

 CursorControl.Panel = ChartPanel; 

Et voila, vous pouvez redessiner autant de fois que nécessaire sans recalculer le contenu du graphique.

À votre santé.

Cela peut être fait de plusieurs manières, en stockant toujours les graphiques sous forme de Bitmap . Le moyen le plus direct et le plus efficace est de laisser le Panel faire tout le travail à votre place.

L’idée est la suivante: la plupart des Controls Winforms ont un affichage à deux couches.

Dans le cas d’un Panel les deux couches sont BackgroundImage et sa surface de Control . Il en va de même pour de nombreux autres contrôles, tels que Label, CheckBox, RadioButton ou Button .

(Une exception intéressante est PictureBox , qui possède en plus une image (au premier plan).)

Ainsi, nous pouvons déplacer les éléments coûteux dans BackgroundImage et tracer le réticule sur la surcafe. Dans notre cas, le Panel , tous les extras intéressants sont en place et vous pouvez sélectionner toutes les valeurs de la propriété BackgroundImageLayout , y compris les Tile, Stretch, Center et le Zoom . Nous choisissons None .

Maintenant, nous ajoutons un drapeau à votre projet:

 bool panelLocked = false; 

et une fonction pour le régler au besoin:

 void lockPanel( bool lockIt) { if (lockIt) { Bitmap bmp = new Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Width); panel1.DrawToBitmap(bmp, panel1.ClientRectangle); panel1.BackgroundImage = bmp; } else { if (panel1.BackgroundImage != null) panel1.BackgroundImage.Dispose(); panel1.BackgroundImage = null; } panelLocked = lockIt; } 

Ici, vous pouvez voir la magie à l’œuvre: avant d’empêcher le Panel de se charger des tâches coûteuses, nous lui demandons de créer un instantané de ses graphiques et de le placer dans la BackgroundImage .

Nous devons maintenant utiliser l’indicateur pour contrôler l’événement Paint :

 private void panel1_Paint(object sender, PaintEventArgs e) { Size size = panel1.ClientSize; if (panelLocked) { // draw a full size cross-hair cursor over the whole Panel // change this to suit your own needs! e.Graphics.DrawLine(Pens.Red, 0, mouseCursor.Y, size.Width - 1, mouseCursor.Y); e.Graphics.DrawLine(Pens.Red, mouseCursor.X, 0, mouseCursor.X, size.Height); } // expensive drawing, you insert your own stuff here.. else { List pens = new List(); for (int i = 0; i < 111; i++) pens.Add(new Pen(Color.FromArgb(R.Next(111), R.Next(111), R.Next(111), R.Next(111)), R.Next(5) / 2f)); for (int i = 0; i < 11111; i++) e.Graphics.DrawEllipse(pens[R.Next(pens.Count)], R.Next(211), R.Next(211), 1 + R.Next(11), 1 + R.Next(11)); } } 

Enfin, nous MouseMove le MouseMove du Panel :

 private void panel1_MouseMove(object sender, MouseEventArgs e) { mouseCursor = e.Location; if (panelLocked) panel1.Invalidate(); } 

en utilisant une variable de deuxième classe:

  Point mouseCursor = Point.Empty; 

Vous appelez lockPanel(true) ou lockPanel(false) selon vos besoins.

Si vous implémentez cela directement, vous remarquerez un scintillement. Cela disparaît si vous utilisez un Panel double tampon:

 class DrawPanel : Panel { public DrawPanel() { this.DoubleBuffered = true; } } 

Cela déplace le réticule sur les Panels de manière parfaitement lisse. Vous voudrez peut-être activer et désactiver le curseur de la souris sur MouseLeave et MouseEnter .

entrez la description de l'image ici