Control study -> Drag拖放数据操作示例

该博客展示了一个涉及DragEnter、DragDrop等事件用法的Windows拖放数据操作例子。演示了将按钮文本拖动到文本框的过程,并给出了完整代码,还提供了示例下载链接。

(一).功能描述

   涉及到DragEnter DragDrop DragLeave DragOver QueryContinueDrag事件用法的一个例子

(二).图片示例

演示将按钮Button1.Text文本"Button"拖动到TextBox中

拖动前:

拖动后:

(三).代码

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace 拖放数据操作_按键事件_
{
 /// <summary>
 /// Form1 的摘要说明。
 /// </summary>
 public class Form1 : System.Windows.Forms.Form
 {
  private System.Windows.Forms.Button button1;
  private System.Windows.Forms.TextBox textBox1;
  /// <summary>
  /// 必需的设计器变量。
  /// </summary>
  private System.ComponentModel.Container components = null;

  public Form1()
  {
   //
   // Windows 窗体设计器支持所必需的
   //
   InitializeComponent();

   //
   // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
   //
  }

  /// <summary>
  /// 清理所有正在使用的资源。
  /// </summary>
  protected override void Dispose( bool disposing )
  {
   if( disposing )
   {
    if (components != null)
    {
     components.Dispose();
    }
   }
   base.Dispose( disposing );
  }

  #region Windows 窗体设计器生成的代码
  /// <summary>
  /// 设计器支持所需的方法 - 不要使用代码编辑器修改
  /// 此方法的内容。
  /// </summary>
  private void InitializeComponent()
  {
   this.button1 = new System.Windows.Forms.Button();
   this.textBox1 = new System.Windows.Forms.TextBox();
   this.SuspendLayout();
   //
   // button1
   //
   this.button1.Location = new System.Drawing.Point(112, 72);
   this.button1.Name = "button1";
   this.button1.TabIndex = 0;
   this.button1.Text = "button1";
   this.button1.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.button1_QueryContinueDrag);
   this.button1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.button1_MouseDown);
   //
   // textBox1
   //
   this.textBox1.AccessibleDescription = "aaaaa";
   this.textBox1.AccessibleName = "bbbbbb";
   this.textBox1.AllowDrop = true;
   this.textBox1.Location = new System.Drawing.Point(64, 160);
   this.textBox1.Multiline = true;
   this.textBox1.Name = "textBox1";
   this.textBox1.Size = new System.Drawing.Size(168, 48);
   this.textBox1.TabIndex = 1;
   this.textBox1.Text = "";
   this.textBox1.DragOver += new System.Windows.Forms.DragEventHandler(this.textBox1_DragOver);
   this.textBox1.DragDrop += new System.Windows.Forms.DragEventHandler(this.textBox1_DragDrop);
   this.textBox1.DragEnter += new System.Windows.Forms.DragEventHandler(this.textBox1_DragEnter);
   this.textBox1.DragLeave += new System.EventHandler(this.textBox1_DragLeave);
   //
   // Form1
   //
   this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
   this.ClientSize = new System.Drawing.Size(292, 266);
   this.Controls.Add(this.textBox1);
   this.Controls.Add(this.button1);
   this.Name = "Form1";
   this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
   this.Text = "Form1";
   this.ResumeLayout(false);

  }
  #endregion

  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
  [STAThread]
  static void Main()
  {
   Application.Run(new Form1());
  }

  private void button1_QueryContinueDrag(object sender, System.Windows.Forms.QueryContinueDragEventArgs e)
  {
   if(e.EscapePressed)
    e.Action=DragAction.Cancel;
  }

  private void button1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
  {
   button1.DoDragDrop(button1.Text,DragDropEffects.Copy|DragDropEffects.Move);
  }

  private void textBox1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
  {
   if(e.Data.GetDataPresent(DataFormats.Text))
    e.Effect=DragDropEffects.Copy;
   else
    e.Effect=DragDropEffects.None;
  }

  private void textBox1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
  {
   //使用KeyState属性
   if((e.KeyState&(1<<3))!=0)
   {
    if((e.AllowedEffect&DragDropEffects.Copy)!=0)
    {
     e.Effect=DragDropEffects.Copy;
     textBox1.Text=e.Data.GetData(DataFormats.Text).ToString();
    }
   }
   else
   {
    e.Effect=DragDropEffects.Move;
    textBox1.Text=e.Data.GetData(DataFormats.Text).ToString();
   }
  }

  private void textBox1_DragLeave(object sender, System.EventArgs e)
  {
   textBox1.Text="执行 DragLeave";
  }

  private void textBox1_DragOver(object sender, System.Windows.Forms.DragEventArgs e)
  {
   textBox1.Text="执行 DragOver";
  }
 }
}


 (四). 示例下载

          http://www.cnblogs.com/Files/ChengKing/拖放数据操作(按键事件).rar

 

 

 

 

 

 

 

<think> 问题分析: 错误信息显示:在MainWindow.cpp的第52行,试图访问不完整类型'QDropEvent'的成员。这是因为在包含头文件时,可能没有包含QDropEvent所需的完整定义。 原因: 在头文件中,可能只前向声明了QDropEvent,但没有包含相应的头文件。因此,在cpp文件中,当使用QDropEvent指针并尝试访问其成员时,编译器只知道存在QDropEvent这个类,但不知道其具体成员(即不完整类型)。 解决方案: 在MainWindow.cpp中,确保包含QDropEvent的头文件。通常,QDropEvent类在<QDropEvent>中定义,但根据Qt的模块,它可能包含在QtCore或QtGui中。实际上,QDropEvent属于QtGui模块。 因此,在MainWindow.cpp的顶部添加: #include <QDropEvent> 另外,检查是否在头文件(MainWindow.h)中包含了必要的头文件。但根据错误信息,问题出在cpp文件,所以很可能是在cpp文件中缺少包含。 修改后的代码: 在MainWindow.cpp中,添加对QDropEvent的包含。 注意:如果使用了Qt5,通常包含<QtGui>或直接包含<QDropEvent>。由于Qt将一些类分模块,而QDropEvent属于QtGui模块,所以确保在.pro文件中包含了QT += gui(默认是包含的)。 具体步骤: 1. 打开MainWindow.cpp文件。 2. 在文件顶部添加一行:#include <QDropEvent> 3. 重新编译。 如果问题仍然存在,检查是否有其他遗漏的包含。另外,错误提示的第52行是: if (event->mimeData()->hasFormat("application/x-dnditemdata")) 这里访问了event的mimeData(),所以还需要确保QMimeData的头文件也被包含。不过,QDropEvent的头文件应该已经包含了QMimeData的相关声明。如果还有问题,可以显式包含<QMimeData>。 然而,根据错误信息,主要问题在于QDropEvent是不完整类型,所以先解决这个。 另外,错误信息还提到“fix available”,可能是一些IDE(如Qt Creator)会提示自动修复,即添加包含。 修改后的代码部分: 在MainWindow.cpp的包含部分添加: ``` #include <QDropEvent> ``` 如果使用了QDataStream,还需要包含<QDataStream>,如果还没有包含的话。同样,QPixmap需要包含<QPixmap>,QPoint需要包含<QPoint>。但根据错误信息,当前主要问题是QDropEvent不完整。 所以,在MainWindow.cpp中添加: ``` #include <QDropEvent> #include <QMimeData> // 为了使用mimeData()返回的指针的成员,确保包含,但通常QDropEvent会包含它,但为了安全可以加上 #include <QDataStream> #include <QPixmap> #include <QPoint> ``` 但是,根据提供的代码片段,已经使用了QByteArray,QDataStream,QPixmap,QPoint等,所以这些头文件应该已经包含。如果没有,也需要添加。 总结:主要解决方法是包含QDropEvent的头文件。 修改后的dropEvent函数代码(仅添加头文件,函数体不变): 在cpp文件开头添加必要的包含。 如果问题解决了,那么就可以。如果还有其他错误,需要根据错误提示继续添加包含。 注意:在头文件中,如果只用到某个类的指针或引用,可以使用前向声明(如class QDropEvent;),这样就不必包含整个头文件,加快编译速度。但是在实现文件中(.cpp)中,如果访问该类的成员,就必须包含其头文件。 因此,在MainWindow.h中,可能只有前向声明而没有包含,所以在.cpp中必须包含。 示例: MainWindow.h: ``` #include <QMainWindow> // 前向声明 class QDropEvent; class MainWindow : public QMainWindow { Q_OBJECT public: // ... protected: void dropEvent(QDropEvent *event) override; // ... }; ``` MainWindow.cpp: ``` #include "MainWindow.h" #include <QDropEvent> // 必须包含,因为要访问QDropEvent的成员 #include <QMimeData> // 为了使用mimeData() // ... 其他包含 ``` 这样,在头文件中使用前向声明,在cpp中包含具体头文件,是推荐的做法。 因此,请确保在MainWindow.cpp中包含了<QDropEvent>。 §§相关问题§§: 1. 在C++中,什么是不完整类型(incomplete type)? 2. 为什么在头文件中使用前向声明(forward declaration)是一种好习惯? 3. 在Qt中,如何处理拖放事件(drag and drop events)? 4. 在重写Qt事件处理函数时,有哪些注意事项? </think> ### 问题分析与解决方案 您遇到的编译错误是因为**不完整类型(incomplete type)**问题。错误信息表明编译器只知道`QDropEvent`的前向声明,但无法访问其完整定义。 ### 解决方法 在`MainWindow.cpp`文件中添加必要的头文件包含: ```cpp // 在MainWindow.cpp中添加以下包含语句 #include <QDropEvent> // 解决QDropEvent不完整类型问题 #include <QMimeData> // 访问mimeData()需要 #include <QDrag> // 拖放操作支持 #include <QByteArray> // 二进制数据处理 #include <QDataStream> // 数据流操作 ``` ### 完整修复后的代码 ```cpp #include "MainWindow.h" #include <QDropEvent> // 关键修复 #include <QMimeData> #include <QDrag> #include <QByteArray> #include <QDataStream> void MainWindow::dropEvent(QDropEvent *event) { // 检查拖放数据格式... if (event->mimeData()->hasFormat("application/x-dnditemdata")) { QByteArray itemData = event->mimeData()->data("application/x-dnditemdata"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); // 读取拖放数据... QPixmap pixmap; QPoint offset; dataStream >> pixmap >> offset; // 创建新按钮... DragButton *newBtn = new DragButton(this); newBtn->setIcon(pixmap); newBtn->setIconSize(QSize(40, 40)); newBtn->move(event->pos() - offset); newBtn->show(); newBtn->setAttribute(Qt::WA_DeleteOnClose); // 处理拖放动作... if (event->source()->parent() == this) { event->setDropAction(Qt::MoveAction); event->accept(); } else { event->acceptProposedAction(); } } else { event->ignore(); } } ``` ### 问题原因详解 1. **前向声明(Forward Declaration)问题**: - 头文件可能只包含`class QDropEvent;`这样的前向声明 - 前向声明允许声明指针/引用,但不能访问成员 - `event->mimeData()`需要完整的类定义 2. **Qt事件处理机制**: - 拖放事件处理需要完整包含相关头文件 - `QDropEvent`定义在`<QDropEvent>`中 - `QMimeData`定义在`<QMimeData>`中 3. **编译器处理过程**: ```mermaid graph TD A[编译MainWindow.cpp] --> B{识别QDropEvent} B -->|仅前向声明| C[知道类型存在] B -->|需要成员访问| D[需要完整定义] D --> E[报incomplete type错误] ``` 4. **Qt拖放系统组件关系**: - `QDropEvent` ← 包含于 → `QMimeData` - `QMimeData` ← 包含于 → `QDrag` - `QDataStream` ← 依赖 → `QByteArray` ### 预防此类错误的建议 1. **在实现文件中包含完整头文件** ```cpp // 在.cpp文件中总是包含实际使用的类的头文件 #include <QDropEvent> #include <QMimeData> ``` 2. **头文件中使用前向声明** ```cpp // MainWindow.h #include <QMainWindow> // 前向声明代替包含 class QDropEvent; class QMimeData; class MainWindow : public QMainWindow { // ... protected: void dropEvent(QDropEvent *event) override; }; ``` 3. **使用Qt模块包含**(确保.pro文件配置正确) ```qmake # 项目.pro文件 QT += core gui widgets ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值