2008-08-27 28 views
18

Tôi đang tạo một biểu mẫu nhỏ được sử dụng trong ứng dụng Winforms. Nó về cơ bản là một thanh tiến trình các loại. Nhưng tôi muốn người dùng có thể nhấp vào bất kỳ đâu trong biểu mẫu và kéo nó để di chuyển nó trên màn hình trong khi nó vẫn đang được hiển thị.Winforms - Nhấp/kéo bất kỳ vị trí nào trong biểu mẫu để di chuyển nó như thể được nhấp vào chú thích biểu mẫu

Làm cách nào để triển khai hành vi này?

Trả lời

23

Microsoft KB Article 320687 có câu trả lời chi tiết cho câu hỏi này.

Về cơ bản, bạn ghi đè phương thức WndProc để trả về HTCAPTION cho thư WM_NCHITTEST khi điểm được kiểm tra nằm trong vùng máy khách của biểu mẫu - điều này có nghĩa là, yêu cầu Windows xử lý nhấp chuột chính xác giống như nó đã xảy ra trên chú thích của biểu mẫu.

private const int WM_NCHITTEST = 0x84; 
private const int HTCLIENT = 0x1; 
private const int HTCAPTION = 0x2; 

protected override void WndProc(ref Message m) 
{ 
    switch(m.Msg) 
    { 
    case WM_NCHITTEST: 
     base.WndProc(ref m); 
     if ((int)m.Result == HTCLIENT) 
     { 
     m.Result = (IntPtr)HTCAPTION; 
     } 

     return; 
    } 

    base.WndProc(ref m); 
} 
+0

Điều này làm cho menu ngữ cảnh không khả dụng. –

+0

Tác phẩm này hoạt động, nhưng nếu màn hình giật gân có các nút điều khiển (nói nhãn), thì việc nhấp vào nhãn sẽ không di chuyển nó. Làm thế nào để quản lý đặc biệt này nếu giật gân có một số điều khiển? – supafly

3

Các mã sau đây giả định rằng các hình thức ProgressBarForm có một điều khiển ProgressBar với Dock bất động sản thiết lập để Điền

public partial class ProgressBarForm : Form 
{ 
    private bool mouseDown; 
    private Point lastPos; 

    public ProgressBarForm() 
    { 
     InitializeComponent(); 
    } 

    private void progressBar1_MouseMove(object sender, MouseEventArgs e) 
    { 
     if (mouseDown) 
     { 
      int xoffset = MousePosition.X - lastPos.X; 
      int yoffset = MousePosition.Y - lastPos.Y; 
      Left += xoffset; 
      Top += yoffset; 
      lastPos = MousePosition; 
     } 
    } 

    private void progressBar1_MouseDown(object sender, MouseEventArgs e) 
    { 
     mouseDown = true; 
     lastPos = MousePosition; 
    } 

    private void progressBar1_MouseUp(object sender, MouseEventArgs e) 
    { 
     mouseDown = false; 
    } 
} 
12

Dưới đây là một cách để làm điều đó bằng cách sử dụng P/Invoke.

public const int WM_NCLBUTTONDOWN = 0xA1; 
public const int HTCAPTION = 0x2; 
[DllImport("User32.dll")] 
public static extern bool ReleaseCapture(); 
[DllImport("User32.dll")] 
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); 

void Form_Load(object sender, EventArgs e) 
{ 
    this.MouseDown += new MouseEventHandler(Form_MouseDown); 
} 

void Form_MouseDown(object sender, MouseEventArgs e) 
{       
    if (e.Button == MouseButtons.Left) 
    { 
     ReleaseCapture(); 
     SendMessage(Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); 
    } 
} 
+0

Bạn cần có: using System.Runtime.InteropServices; để [DllImport()] hoạt động. –

+1

Trong dự án của tôi, tôi đã sử dụng mã tương tự như Timothy Fries, nhưng tôi thích mã này vì bạn có thể sử dụng chính xác kỹ thuật tương tự để tạo biểu mẫu có thể kéo được bằng các nút điều khiển, như nhãn. Kỹ thuật khác tạo biểu mẫu có thể kéo được, nhưng nếu bạn nhấp vào nhãn, nó sẽ không kéo. – flamey

+0

Điều này cũng đẹp hơn vì nó cho phép bạn vẫn xử lý các sự kiện nhấp chuột phải (cho các menu ngữ cảnh). Bạn cũng có thể sử dụng có chọn lọc nhấp chuột trái như đã nói ở trên hoặc sử dụng đúng/nhấp chuột giữa thay vì nhấp chuột trái. Nhìn chung linh hoạt hơn nhiều. – coderforlife

0

Câu trả lời được chấp nhận là một thủ thuật mát mẻ, nhưng nó không phải lúc nào hoạt động nếu mẫu được bao phủ bởi một điều khiển con Fill-cập cảng giống như một Panel (hoặc derivates) ví dụ, bởi vì sự kiểm soát này sẽ ăn tất cả hầu hết các tin nhắn Windows.

Đây là một phương pháp đơn giản mà làm việc cũng trong trường hợp này: lấy được sự kiểm soát trong câu hỏi (sử dụng lớp này thay vì một trong những tiêu chuẩn) một tay cầm thông điệp chuột như thế này:

private class MyTableLayoutPanel : Panel // or TableLayoutPanel, etc. 
    { 
     private Point _mouseDown; 
     private Point _formLocation; 
     private bool _capture; 

     // NOTE: we cannot use the WM_NCHITTEST/HTCAPTION trick because the table is in control, not the owning form... 
     protected override void OnMouseDown(MouseEventArgs e) 
     { 
      _capture = true; 
      _mouseDown = e.Location; 
      _formLocation = ((Form)TopLevelControl).Location; 
     } 

     protected override void OnMouseUp(MouseEventArgs e) 
     { 
      _capture = false; 
     } 

     protected override void OnMouseMove(MouseEventArgs e) 
     { 
      if (_capture) 
      { 
       int dx = e.Location.X - _mouseDown.X; 
       int dy = e.Location.Y - _mouseDown.Y; 
       Point newLocation = new Point(_formLocation.X + dx, _formLocation.Y + dy); 
       ((Form)TopLevelControl).Location = newLocation; 
       _formLocation = newLocation; 
      } 
     } 
    } 
0

VC++ 2010 Version (của FlySwat's):

#include <Windows.h> 

namespace DragWithoutTitleBar { 

    using namespace System; 
    using namespace System::Windows::Forms; 
    using namespace System::ComponentModel; 
    using namespace System::Collections; 
    using namespace System::Data; 
    using namespace System::Drawing; 

    public ref class Form1 : public System::Windows::Forms::Form 
    { 
    public: 
     Form1(void) { InitializeComponent(); } 

    protected: 
     ~Form1() { if (components) { delete components; } } 

    private: 
     System::ComponentModel::Container ^components; 
     HWND hWnd; 

#pragma region Windows Form Designer generated code 
     void InitializeComponent(void) 
     { 
      this->SuspendLayout(); 
      this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); 
      this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; 
      this->ClientSize = System::Drawing::Size(640, 480); 
      this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::None; 
      this->Name = L"Form1"; 
      this->Text = L"Form1"; 
      this->Load += gcnew EventHandler(this, &Form1::Form1_Load); 
      this->MouseDown += gcnew System::Windows::Forms::MouseEventHandler(this, &Form1::Form1_MouseDown); 
      this->ResumeLayout(false); 

     } 
#pragma endregion 
    private: System::Void Form1_Load(Object^ sender, EventArgs^ e) { 
        hWnd = static_cast<HWND>(Handle.ToPointer()); 
       } 

    private: System::Void Form1_MouseDown(Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { 
        if (e->Button == System::Windows::Forms::MouseButtons::Left) { 
         ::ReleaseCapture(); 
         ::SendMessage(hWnd, /*WM_NCLBUTTONDOWN*/ 0xA1, /*HT_CAPTION*/ 0x2, 0); 
        } 
       } 

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