2014-09-22 33 views
5

我有一個自定義控件,由帶填充的圓角矩形和文本組成。 (實際控制比較複雜,但這裏顯示的代碼具有相同的症狀。)我將該控件的實例附加到面板上,並使該面板成爲AutoScroll = true的另一個面板的子面。我認爲這對於正確的滾動行爲就足夠了,但是如果您滾動以使我的控件的左側離開面板的左側並且控件縮小。與滾動一樣,控件應該在頂部停止。 (底部和右側似乎不成問題。)下面是示例代碼(需要System.Windows.Forms和System.Drawing的引用。)我在Windows上使用Visual Studio 2010和.NET 4客戶端。允許自定義控件正確滾動出視圖

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

如果有人想知道,沒有設計師課,我想讓這個例子完全自成一體。 (注意,沒有一個類是局部的。) – 2014-09-22 23:14:49

回答

6

試試這個

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); 
    } 

問題是,當你向右滾動(向左移動rects,e.ClipRectangle.Width變小)和矩形超出區域中, e.ClipRectangle.X是積極的!所以在這種情況下,我們將其設置爲零。 e.ClipRectangle.X不能爲負數,所以即使您反轉它(編輯前的解決方案)它也會變爲零。

編輯

你可以簡單地做

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

兩個正在

您可以使用這些樣式

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

避免閃爍

valter

+0

完美,我認爲我通過左右使用剪輯區域來「正確」,儘管使用0更容易。我的第一個想法是「這是荒謬的(在微軟方面)」,但現在考慮一下,一旦部分向左移動,它就是有意義的,它在剪切矩形之外。謝謝一堆! – 2014-09-23 16:33:15