2014-09-22 5 views
5

Ich habe ein benutzerdefiniertes Steuerelement, das aus einem gefüllten abgerundeten Rechteck mit Text besteht. (Das eigentliche Steuerelement ist komplizierter, aber der hier gezeigte Code weist die gleichen Symptome auf.) Ich habe Instanzen des Steuerelements an ein Panel angefügt und dieses Panel zu einem untergeordneten Element eines anderen Steuerelements mit AutoScroll = true gemacht. Ich dachte, das wäre genug für das korrekte Scroll-Verhalten, aber wenn Sie so scrollen, dass die linke Seite meines Controls von der linken Seite des Panels abgeht, bleibt es haften und die Kontrolle schrumpft. Dasselbe gilt für das Scrollen, so dass das Steuerelement oben deaktiviert werden sollte. (Unten und rechts scheint kein Problem zu sein.) Hier ist der Beispielcode (erfordert einen Verweis auf System.Windows.Forms und System.Drawing.) Ich verwende Visual Studio 2010 mit .NET 4-Client unter Windows.Lassen Sie das benutzerdefinierte Steuerelement ordnungsgemäß aus der Ansicht scrollen

using System; 
using System.Drawing; 
using System.Drawing.Drawing2D; 
using System.Windows.Forms; 


namespace CustomControlScrollTest 
{ 
    static class Program 
    { 
     [STAThread] 
     static void Main() 
     { 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 
      Application.Run(new Form1()); 
     } 
    } 

    // Form with a Dock = Fill panel with autoscroll turned on, and a nested panel 
    // with few custom roundRectControls. 
    public class Form1 : Form 
    { 
     Panel autoScrollPanel; 
     Panel rectPanel; 

     public Form1() 
     { 
      Size = new Size(300, 200); 

      autoScrollPanel = new Panel(); 
      autoScrollPanel.Dock = DockStyle.Fill; 
      autoScrollPanel.AutoScroll = true; 
      autoScrollPanel.AutoScrollMinSize = new Size(600, 450); 

      autoScrollPanel.Resize += autoScrollPanel_Resize; 
      autoScrollPanel.Scroll += autoScrollPanel_Scroll; 

      Controls.Add(autoScrollPanel); 

      rectPanel = new Panel(); 
      rectPanel.Size = autoScrollPanel.AutoScrollMinSize; 
      rectPanel.Controls.AddRange(new RoundRectControl[] { 
       new RoundRectControl(), 
       new RoundRectControl(), 
       new RoundRectControl(), 
       new RoundRectControl(), 
       new RoundRectControl() 
      }); 

      foreach (Control c in rectPanel.Controls) 
      { 
       c.Click += c_Click; 
      } 

      autoScrollPanel.Controls.Add(rectPanel); 

      placeBoxes(); 
     } 

     // we want to be able to recalculate the boxes position at any time 
     // in the real program this occurs due to model changes 
     void c_Click(object sender, EventArgs e) 
     { 
      placeBoxes(); 
     } 

     void autoScrollPanel_Scroll(object sender, ScrollEventArgs e) 
     { 
      Refresh(); 
     } 

     void autoScrollPanel_Resize(object sender, EventArgs e) 
     { 
      Refresh(); 
     } 

     private void placeBoxes() 
     { 
      for (int i = 0; i < rectPanel.Controls.Count; ++i) 
      { 
       int j = i + 1; 
       var node = rectPanel.Controls[i] as RoundRectControl; 
       if (node != null) 
       { 
        node.Title = "Hello (" + j + ")"; 
        node.Location = new Point(i * 100, j * 75); 
        node.Visible = true;     
       } 
      } 
     } 
    } 

    // A rounded rectangle filled blue with a black border and white text 
    // the size is determined by the text 
    public class RoundRectControl : Control 
    { 
     public RoundRectControl() 
     { 
      var f = SystemFonts.MessageBoxFont; 
      titleFont = new Font(f.Name, f.SizeInPoints + 2, FontStyle.Bold, GraphicsUnit.Point); 
      ResizeRedraw = true; 
     } 

     protected override void OnPaint(PaintEventArgs e) 
     { 
      base.OnPaint(e); 
      var g = e.Graphics; 

      var left = e.ClipRectangle.X; 
      var right = left + titleWidth + 2 * radius; 
      var top = e.ClipRectangle.Y; 
      var bottom = top + nodeHeight + 2 * radius; 

      var r2 = 2 * radius; 

      using (var path = new GraphicsPath()) 
      { 
       path.AddArc(left,  bottom - r2, r2, r2, 90, 90); 
       path.AddArc(left,  top,   r2, r2, 180, 90); 
       path.AddArc(right - r2, top,   r2, r2, 270, 90); 
       path.AddArc(right - r2, bottom - r2, r2, r2, 0, 90); 
       path.CloseFigure(); 

       g.FillPath(titleBrush, path); 
       g.DrawPath(borderPen, path); 
      } 

      g.DrawString(title, titleFont, titleTextBrush, left + radius, top + radius); 
     } 

     private string title; 
     public string Title 
     { 
      get { return title; } 
      set 
      { 
       title = value; 
       Size = getSize(); 
       Invalidate(); 
      } 
     } 

     private Brush titleBrush = Brushes.Blue; 

     private Brush titleTextBrush = Brushes.White; 

     private Pen borderPen = Pens.Black; 

     private Size getSize() 
     { 
      var g = CreateGraphics(); 
      var titleSize = g.MeasureString(title, titleFont); 
      titleWidth = (int)titleSize.Width; 
      nodeHeight = (int)titleSize.Height; 
      return new Size(titleWidth + 2 * radius + 1, nodeHeight + 2 * radius + 1); 
     } 

     public override Size GetPreferredSize(Size proposedSize) 
     { 
      return getSize(); 
     } 

     private int titleWidth; 
     private int nodeHeight; 

     private Font titleFont; 

     private int radius = 5; 
    } 
} 
+0

Falls sich jemand fragt, gibt es keine Designerklasse, ich das Beispiel komplett eigenständig machen wollte. (Beachten Sie, dass keine der Klassen partiell ist.) –

Antwort

6

Versuchen Sie, diese

protected override void OnPaint(PaintEventArgs e) 
    { 
     base.OnPaint(e); 
     var g = e.Graphics; 

     //total width and height of rounded rectangle 
     var width = titleWidth + 2 * radius + 1; 
     var height = nodeHeight + 2 * radius + 1; 

     var left = e.ClipRectangle.X; 
     var top = e.ClipRectangle.Y; 

     //check if clipping occurs. If yes, set to 0 
     if (width > e.ClipRectangle.Width) 
     { 
      left = 0; // *= -1; 
     } 

     //check if clipping occurs.If yes, set to 0 
     if (height > e.ClipRectangle.Height) 
     { 
      top = 0; // *= -1 
     } 

     var right = left + titleWidth + 2 * radius; 
     var bottom = top + nodeHeight + 2 * radius; 

     var r2 = 2 * radius; 

     using (var path = new GraphicsPath()) 
     { 
      path.AddArc(left, bottom - r2, r2, r2, 90, 90); 
      path.AddArc(left, top, r2, r2, 180, 90); 
      path.AddArc(right - r2, top, r2, r2, 270, 90); 
      path.AddArc(right - r2, bottom - r2, r2, r2, 0, 90); 
      path.CloseFigure(); 

      g.FillPath(titleBrush, path); 
      g.DrawPath(borderPen, path); 
     } 

     g.DrawString(title, titleFont, titleTextBrush, left + radius, top + radius); 
    } 

Das Problem ist, wenn Sie nach rechts scrollen (Rects links bewegt, e.ClipRectangle.Width wird kleiner) und das Rechteck geht aus der Fläche, die e.ClipRectangle.X ist positiv! In diesem Fall setzen wir es auf Null. e.ClipRectangle.X kann nicht negativ sein, selbst wenn Sie es invertieren (Lösung vor der Bearbeitung), wird es zu Null.

EDIT

Sie können einfach tun

var left = 0; 
var right = left + titleWidth + 2 * radius; 
var top = 0; 
var bottom = top + nodeHeight + 2 * radius; 

Beide

arbeiten, können Sie diese Stile verwenden

this.SetStyle(ControlStyles.DoubleBuffer, true); 
this.SetStyle(ControlStyles.UserPaint, true); 
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); 

Flimmern zu vermeiden

Valter

+0

Perfekt, ich dachte, ich wäre "richtig", wenn ich den Clip-Bereich links und rechts verwende, obwohl die Verwendung von 0 einfacher ist. Mein erster Gedanke war: "Das ist lächerlich (von Microsoft)", aber jetzt, wenn ich darüber nachdenke, macht es Sinn, wenn ein Teil nach links geht, außerhalb des Clip-Rechtecks. Vielen Dank! –

Verwandte Themen