2014-09-22 18 views
5

Tôi có một điều khiển tùy chỉnh bao gồm hình chữ nhật tròn được làm đầy với văn bản. (Điều khiển thực tế phức tạp hơn nhưng đoạn code được hiển thị ở đây có các triệu chứng sames). Tôi đã gắn các instance của control vào một Panel, và biến Panel đó thành con của một Panel khác với AutoScroll = true. Tôi nghĩ rằng đó là đủ cho hành vi di chuyển chính xác, nhưng nếu bạn di chuyển như vậy mà phía bên trái của điều khiển của tôi nên đi ra khỏi phía bên trái của bảng điều khiển nó dính và điều khiển co lại. Cùng với việc cuộn như vậy, điều khiển sẽ bị tắt ở trên cùng. (Phía dưới và bên phải dường như không phải là một vấn đề.) Đây là mã mẫu (yêu cầu tham chiếu đến System.Windows.Forms và System.Drawing.) Tôi đang sử dụng Visual Studio 2010 với .NET 4 client trên Windows.Cho phép điều khiển tùy chỉnh để di chuyển chính xác ra khỏi chế độ xem

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

Trong trường hợp bất kỳ ai thắc mắc, không có lớp thiết kế, tôi muốn làm ví dụ hoàn toàn khép kín. (Thông báo không có lớp nào là một phần.) –

Trả lời

6

Hãy thử

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

vấn đề này được khi bạn đang di chuyển về bên phải (dễ phân biệt di chuyển trái, e.ClipRectangle.Width trở nên nhỏ hơn) và hình chữ nhật đi ra khỏi khu vực, các e.ClipRectangle.X là tích cực! Vì vậy, trong trường hợp này, chúng tôi đặt nó bằng 0. e.ClipRectangle.X không được âm, vì vậy ngay cả khi bạn đảo ngược nó (giải pháp trước khi chỉnh sửa) nó sẽ trở thành số không.

EDIT

Bạn chỉ có thể làm

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

Cả hai đều làm việc

Bạn có thể sử dụng các phong cách

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

để tránh nhấp nháy

valter

+0

Tuyệt vời, tôi nghĩ rằng tôi đã "đúng" bằng cách sử dụng vùng clip trái và phải, mặc dù việc sử dụng 0 là dễ dàng hơn. Suy nghĩ đầu tiên của tôi là "đó là vô lý (trên phần của Microsoft)" nhưng bây giờ nghĩ về nó nó có ý nghĩa một khi một phần đi ra bên trái, nó là bên ngoài hình chữ nhật clip. Cảm ơn nhiều! –

Các vấn đề liên quan