Dessiner un contrôle qui soit indépendant de la résolution d'écran
-
martedì 20 aprile 2010 16:03
Bonjour,
Je développe une appli WinForm en VB.NET sous VS 2008, pour le Framework 2.0.
J'ai réalisé un contrôle dont le contour est un cercle. J'utilise pour le dessiner un DrawEllipse(Pen, Rectangle). L'origine et les dimensions du rectangle sont en fait l'origine et les dimensions du contrôle, exprimés en pixels lors de sa création (Width=Height pour avoir un cercle).
Mais la restitution n'est pas toujours correcte en fonction de la taille et de la résolution de l'écran cible : Le cercle apparait parfois comme une ellipse et n'est pas placé au bon endroit sur la Form alors que les contrôles standards sont bien conformes.
Comment puis-je faire pour palier ces inconvénients ?
Merci de vos suggestions.
Alain
Tutte le risposte
-
martedì 20 aprile 2010 19:47
Bonjour,
si ton Width=Height, alors je trouve cela bizarre de ne pas voir un cercle. Ma seule idée est que tu n'as pas une bonne résolution d'écran, celle-ci étire tout les éléments.
Es-ce que tu aurais un bout de code?
Microsoft MVP C# || gabrielmongeon.com -
martedì 20 aprile 2010 21:14
Voici le code de ma classe UCB qui implémente ce contrôle.
Public Class UCB #Region "Contexte de la classe" Private cr1 As Pen = New Pen(Color.Black, 1) Private pt1 As New Pen(Color.Red, 2) Private cr2 As Pen = New Pen(Color.DarkGray, 1) Private pt2 As New Pen(Color.DarkGray, 2) Private radians As Single ' Angle en radians Private rayon As Double ' Rayon du cercle Private pPoint As PointF ' Matérialisation du point Private centre As PointF ' Centre du cercle Private zéro As PointF ' Origine des angles (axe des X) Private retrait As Double = 4 ' Rayon du cercle = Width/2 - retrait Private coup As Integer = 1 ' Pour le MouseMove #End Region #Region "Propriétés" ''' <summary> ''' Angle exprimé en degrés ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property Angle() As Single Get Return radians * (180 / Math.PI) End Get Set(ByVal value As Single) radians = Math.PI * value / 180.0 With Me.ClientRectangle pPoint = New PointF(centre.X + rayon * Math.Cos(radians), _ centre.Y - rayon * Math.Sin(radians)) End With Me.Invalidate() End Set End Property ''' <summary> ''' Activation / désactivation ''' </summary> ''' <value>Treu / False</value> ''' <returns></returns> ''' <remarks></remarks> Public Property Enable() As Boolean Get Return Me.Enabled End Get Set(ByVal value As Boolean) If Me.Enabled <> value Then Me.Invalidate() Me.Enabled = value End Set End Property #End Region #Region "EventHandler" ' Evenement émis vers l'objet qui a créé l'instance Public Event AngleChange(ByVal obj As Control, ByVal angle As Single) #End Region ''' <summary> ''' Création d'une instance ''' </summary> ''' <param name="coté">coté du carré</param> ''' <remarks></remarks> Public Sub New(ByVal coté As Double) ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. Size = New Size(coté, coté) End Sub Private Sub UCB_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown If e.Button = Windows.Forms.MouseButtons.Left Then Dim r As Rectangle = New Rectangle(pPoint.X - 1, pPoint.Y - 1, 4, 4) If r.Contains(e.Location) Then Cursor = Cursors.Hand End If End If End Sub Private Sub UCB_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove If e.Button = Windows.Forms.MouseButtons.Left AndAlso Cursor = Cursors.Hand Then If coup > 0 Then coup -= 1 Else coup = 1 ' Calculer le nouvel angle Angle = GetArc(e.Location) RaiseEvent AngleChange(Me, Angle) End If End If End Sub Private Sub UCB_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp If e.Button = Windows.Forms.MouseButtons.Left Then Cursor = Cursors.Arrow End If End Sub Private Sub UCB_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint Dim cr, pt As Pen '-- Choisir les outils = f(Enabled) If Me.Enabled Then cr = cr1 pt = pt1 Else cr = cr2 pt = pt2 End If '-- Dessiner e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality With Me.ClientRectangle ' Le cercle e.Graphics.DrawEllipse(cr, New Rectangle(retrait, retrait, .Width - retrait * 2, .Height - retrait * 2)) ' L'axe e.Graphics.DrawLine(cr, centre, pPoint) ' Le point e.Graphics.DrawPie(pt, pPoint.X - 1, pPoint.Y - 1, 4.0F, 4.0F, 0.0F, 360.0F) End With End Sub Private Sub UCB_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.SizeChanged With Me.ClientRectangle rayon = Width / 2 - retrait centre = New PointF(.Width / 2, .Height / 2) ' Centre du cercle zéro = New PointF(.Width - retrait, .Height / 2) ' Origine des angles End With End Sub ''' <summary> ''' Get arc ''' </summary> ''' <param name="p"></param> ''' <returns>L'angle en degrés</returns> ''' <remarks></remarks> Private Function GetArc(ByVal p As PointF) As Double Dim x, y, radians As Double x = p.X - centre.X y = centre.Y - p.Y radians = Math.Atan2(y, x) Return radians * (180 / Math.PI) End Function End ClassLe New créé et dimensionne le contrôle. Le Paint dessine le cercle avec un rayon et un point.
L'application créée le controle de la façon suivante :
'-- Initialiser l'onglet Commentaires -- Boule = New BouleAch.UCB(30) ' Créer à 30 de côté Boule.Location = New Point(18, 93) ' Placement en pixels Boule.Angle = My.Settings.Angle ' En degrés GroupBox6.Controls.Add(Boule) ' Add toEn fonction de l'écran utilisé, le contrôle est parfois mal positionné dans le GroupBox6 et apparait comme une ellipse plus haute que large.
Je ne sais pas joindre une image...
Merci
Alain -
giovedì 22 aprile 2010 09:03
Bonjour AchLog,
J’ai testé votre contrôle pour toutes les résolutions que mon écran supporte. Pour quelques résolutions inferieures (ex. 1280x720) je vois une déformation du contrôle et il semble plus haut que large.
En ce qui concerne la position par rapport aux autres contrôles, avez-vous essayé de vérifier en mode debug les coordonnées des contrôles ‘correctes’ et les comparer avec les coordonnées de votre contrôle ? Le déplacement est-il horizontale ou verticale ?
Moi, à partir de mes observations, je crois qu’il s’agit d’une question de résolution, qui fait que pour certaines configurations les contrôles s’allongent en haut. Mais si vous observez des coordonnées différentes, il s’agit peut-être d’une autre cause.
Cordialement,
Alex
Appel à contribution ! http://social.msdn.microsoft.com/Forums/fr-FR/vbasicfr/thread/bd974e0e-5519-4122-b8fc-3b998207c34f -
giovedì 22 aprile 2010 20:14
Merci Alex pour ce travail d'investigation que vous avez fait.
J'ai un lointain souvenir d'une question d'unité dans les dialogues, justement concernant les contrôles afin qu'ils soient indépendants de la résolution d'écran. Mais je ne sais plus où trouver cette information ni si elle est toujours à l'ordre du jour.
Pour répondre à vos questions :
Je n'ai pas fait d'examen comparatif de la question des coordonnées mais je vais regarder ça. Quant au déplacement, il est essentiellement vertical ainsi que l'allongement, ce qui donne une ellipse verticale mal placée.
Mais il faut noter que certaines résolution d'écran fonctionnent bien.
Avez-vous des précisions sur le système de coordonnées utilisés pour placer et dimensionner les contrôles ?
Je réponds ici rapidement mais je ferai une réponse plus complète dès que je pourrai faire des essais plus poussés.
Cordialement
Alain -
venerdì 23 aprile 2010 08:21
Bonjour,
Avez-vous tenu compte de fait que votre contrôle est en fait un rectangle transparent avec un cercle dessiné, et sa propriété Location signifie les coordonnées du point ‘top-left’ de ce rectangle ? Par contre, je vois que dans votre contrôle vous utilisez le centre du cercle. Y a-t-il peut-être une confusion dans cet aspect ?
Cordialement,
Alex
Appel à contribution ! http://social.msdn.microsoft.com/Forums/fr-FR/vbasicfr/thread/bd974e0e-5519-4122-b8fc-3b998207c34f -
venerdì 23 aprile 2010 21:57
Bonsoir Alex,
Oui, mon contrôle est un carré et le cercle est dessiné dedans, avec un léger retrait de 4 pixels sur chaque coté, comme le montre le code du Paint :
e.Graphics.DrawEllipse(cr, New Rectangle(retrait, retrait, .Width - retrait * 2, .Height - retrait * 2))
J'ai fait quelques observations qui me rendent perplexe...
Sur le moniteur (19" à tube) où j'ai observé le problème, sous W7, j'ai essayé plusieurs résolutions d'écran : 1600x1200, 1360x1024, 1360x768, 1280x720, 1280x960, 1280x800 et 1024x768 ; j'observe toujours une ovalisation +/- importante de mon cercle et un décalage dans son positionnement X,Y. Ce qui est plus choquant et totalement inattendu c'est que j'observe également une ovalisation des boutons radios standards de l'IHM, lorsque Q=X/Y est > 1,333 !!
Comment expliquer cela ? Sous W7 l'IHM est donc déformée en fonction de la résolution d'écran choisie ??
Sur un ordinateur portable 1440x900 (Q=1,6) sous W7, tout va bien mais lorsque je change de résolution (1280x720 - Q=1,777 et 1152x864 - Q=1,333) alors une légère déformation des boutons radios apparait sous forme d'un étirement respectivement vertical et horizontal. Dans tous ces cas mon contrôle est bien positionné mais subit aussi la même faible déformation que les BR.
Sur un autre portable en 1024X768 et 800x600, sous XP, tout est correct (les Q=X/Y sont ici identiques, = 1,333).
J'en conclus, et j'aimerais avoir votre avis :
- la carte graphique qui pilote le moniteur 19" à tube ne fonctionne pas correctement sous W7 car aucune résolution ne donne de résultat correct
- le rendu d'une application WinForm dépend de la résolution d'écran choisie, c'est à dire que le GDI de W7 n'est pas indépendant de celle-ci.
Merci de votre analyse.Bien cordialement
Alain- Contrassegnato come risposta Alex PetrescuMicrosoft Employee lunedì 26 aprile 2010 08:57
-
sabato 29 maggio 2010 17:39
Bonjour,
1/Oui, pour vous en rendre compte, faites une capture d'écran, enregistrez là dans un fichier et regardez sur votre poste sous XP...
2/Oui, c'est d'ailleurs pour cela que nouveau moteur graphique WPF résout ce problème.
Cordialement
Gilles TOURREAU - MVP C# - MCP - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr -
sabato 29 maggio 2010 19:49
Bonsoir Gilles,
Merci de votre message de confirmation. Il me semble me souvenir que Windows utilise une unité spéciale, non les pixels, pour implanter les contrôles sur une Form, mais c'est un peu vague et lointain...
Avez-vous connaissance de cela ?
Cordialement.
Alain -
domenica 30 maggio 2010 21:44
Bonjour,
Non... Pas à ma connaissance.
Cordialement
Gilles TOURREAU - MVP C# - MCP - Architecte .NET/Consultant/Formateur - http://gilles.tourreau.fr -
lunedì 14 giugno 2010 22:42
Bonsoir,
Je reviens sur ce problème car j'en ai trouvé l'origine : C'est une question de police de caractères de Form associée à la propriété AutoScaleMode = Font.
Je rappelle que mon appli est une Form qui incorpore un contrôle. Or ce contrôle est lui-même une Form et il dessine un cercle à sa surface (voir message initial et le code transmis le 20 avril). Les deux Form ont leur propriété AutoScaleMode = Font mais n'ont pas la même Font et c'est là la source du problème.
Si on définit les deux Font identiques, alors le cercle tracé est bien toujours un cercle rond ! Ouf.
Sur la plupart des écrans (et carte graphiques) la différence de Font ne pose pas de problème et l'appli fonctionne correctement, sur d'autres le cercle devient ovale.
Reste encore à savoir pourquoi...
Bien cordialement
Alain

