首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在同一Windows窗体应用程序的实例之间拖放

在同一Windows窗体应用程序的实例之间拖放
EN

Stack Overflow用户
提问于 2009-07-29 17:23:21
回答 4查看 14.4K关注 0票数 17

我已经创建了一个小型Windows窗体测试应用程序来试用一些拖放代码。表单由三个PictureBoxes组成。我的意图是从一个PictureBox抓取一张图片,在拖动操作过程中将其显示为自定义光标,然后将其放到另一个PictureBox目标上。

只要它们位于相同的表单上,就可以从一个PictureBox切换到另一个。

如果我打开同一个应用程序的两个实例,并尝试在它们之间拖放,我会得到以下隐含的错误:

此远程处理代理没有通道接收器,这意味着服务器没有正在侦听的已注册服务器通道,或者此应用程序没有合适的客户端通道来与服务器通信。

然而,由于某些原因,拖放到写字板(而不是MS Word或画笔)是可行的。

这三个PictureBoxes将它们的事件连接在一起,如下所示:

代码语言:javascript
复制
foreach (Control pbx in this.Controls) {
    if (pbx is PictureBox) {
        pbx.AllowDrop = true;
        pbx.MouseDown    += new MouseEventHandler(pictureBox_MouseDown);
        pbx.GiveFeedback += new GiveFeedbackEventHandler(pictureBox_GiveFeedback);
        pbx.DragEnter    += new DragEventHandler(pictureBox_DragEnter);
        pbx.DragDrop     += new DragEventHandler(pictureBox_DragDrop);
    }
}

然后有四个这样的事件:

代码语言:javascript
复制
void pictureBox_MouseDown(object sender, MouseEventArgs e) {
    int width = (sender as PictureBox).Image.Width;
    int height = (sender as PictureBox).Image.Height;

    Bitmap bmp = new Bitmap(width, height);
    Graphics g = Graphics.FromImage(bmp);
    g.DrawImage((sender as PictureBox).Image, 0, 0, width, height);
    g.Dispose();
    cursorCreatedFromControlBitmap = CustomCursors.CreateFormCursor(bmp, transparencyType);
    bmp.Dispose();

    Cursor.Current = this.cursorCreatedFromControlBitmap;

    (sender as PictureBox).DoDragDrop((sender as PictureBox).Image, DragDropEffects.All);
}

代码语言:javascript
复制
void pictureBox_GiveFeedback(object sender, GiveFeedbackEventArgs gfea) {
    gfea.UseDefaultCursors = false;
}

代码语言:javascript
复制
void pictureBox_DragEnter(object sender, DragEventArgs dea) {
    if ((dea.KeyState & 32) == 32) { // ALT is pressed
        dea.Effect = DragDropEffects.Link;
    }
    else if ((dea.KeyState & 8) == 8) { // CTRL is pressed
        dea.Effect = DragDropEffects.Copy;
    }
    else if ((dea.KeyState & 4) == 4) { // SHIFT is pressed
        dea.Effect = DragDropEffects.None;
    }
    else {
        dea.Effect = DragDropEffects.Move;
    }
}

代码语言:javascript
复制
void pictureBox_DragDrop(object sender, DragEventArgs dea) {
    if (((IDataObject)dea.Data).GetDataPresent(DataFormats.Bitmap))
        (sender as PictureBox).Image = (Image)((IDataObject)dea.Data).GetData(DataFormats.Bitmap);
}

任何帮助都将不胜感激!

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2009-07-30 04:40:47

在苦口婆心和拔掉头发之后,我终于想出了一个可行的解决方案。似乎在.NET及其OLE拖放支持的背后有一些未经记录的奇怪之处。它似乎试图在.NET应用程序之间执行拖放操作时使用.NET remoting,但是是否有文档记录这一点?不,我不这么认为。

因此,我提出的解决方案涉及到一个帮助类来帮助在进程之间编组位图数据。首先,这是类。

代码语言:javascript
复制
[Serializable]
public class BitmapTransfer
{
    private byte[] buffer;
    private PixelFormat pixelFormat;
    private Size size;
    private float dpiX;
    private float dpiY;

    public BitmapTransfer(Bitmap source)
    {
        this.pixelFormat = source.PixelFormat;
        this.size = source.Size;
        this.dpiX = source.HorizontalResolution;
        this.dpiY = source.VerticalResolution;
        BitmapData bitmapData = source.LockBits(
            new Rectangle(new Point(0, 0), source.Size),
            ImageLockMode.ReadOnly, 
            source.PixelFormat);
        IntPtr ptr = bitmapData.Scan0;
        int bufferSize = bitmapData.Stride * bitmapData.Height;
        this.buffer = new byte[bufferSize];
        System.Runtime.InteropServices.Marshal.Copy(ptr, buffer, 0, bufferSize);
        source.UnlockBits(bitmapData);
    }

    public Bitmap ToBitmap()
    {
        Bitmap bitmap = new Bitmap(
            this.size.Width,
            this.size.Height,
            this.pixelFormat);
        bitmap.SetResolution(this.dpiX, this.dpiY);
        BitmapData bitmapData = bitmap.LockBits(
            new Rectangle(new Point(0, 0), bitmap.Size),
            ImageLockMode.WriteOnly, bitmap.PixelFormat);
        IntPtr ptr = bitmapData.Scan0;
        int bufferSize = bitmapData.Stride * bitmapData.Height;
        System.Runtime.InteropServices.Marshal.Copy(this.buffer, 0, ptr, bufferSize);
        bitmap.UnlockBits(bitmapData);
        return bitmap;
    }
}

若要以同时支持位图的.NET和非托管收件人的方式使用该类,请将DataObject类用于拖放操作,如下所示。

要启动拖动操作,请执行以下操作:

代码语言:javascript
复制
DataObject dataObject = new DataObject();
dataObject.SetData(typeof(BitmapTransfer), 
  new BitmapTransfer((sender as PictureBox).Image as Bitmap));
dataObject.SetData(DataFormats.Bitmap, 
  (sender as PictureBox).Image as Bitmap);
(sender as PictureBox).DoDragDrop(dataObject, DragDropEffects.All);

要完成操作,请执行以下操作:

代码语言:javascript
复制
if (dea.Data.GetDataPresent(typeof(BitmapTransfer)))
{
    BitmapTransfer bitmapTransfer = 
       (BitmapTransfer)dea.Data.GetData(typeof(BitmapTransfer));
    (sender as PictureBox).Image = bitmapTransfer.ToBitmap();
}
else if(dea.Data.GetDataPresent(DataFormats.Bitmap))
{
    Bitmap b = (Bitmap)dea.Data.GetData(DataFormats.Bitmap);
    (sender as PictureBox).Image = b;
}

首先执行对customer BitmapTransfer的检查,因此它优先于数据对象中是否存在常规的位图。BitmapTransfer类可以放在一个共享库中,以便与多个应用程序一起使用。它必须标记为可序列化,如图所示,以便在应用程序之间拖放。我通过在应用程序内、应用程序之间以及从.NET应用程序到写字板的位图拖放对其进行了测试。

希望这能帮到你。

票数 10
EN

Stack Overflow用户

发布于 2009-08-12 22:26:45

我最近遇到了这个问题,并且在剪贴板中使用了自定义格式,这使得Interop变得更加困难。无论如何,通过一点光反射,我能够到达原始的System.Windows.Forms.DataObject,然后调用GetData并像正常一样从它中取出我的自定义项目。

代码语言:javascript
复制
var oleConverterType = Type.GetType("System.Windows.DataObject+OleConverter, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
var oleConverter = typeof(System.Windows.DataObject).GetField("_innerData", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(e.Data);
var dataObject = (System.Windows.Forms.DataObject)oleConverterType.GetProperty("OleDataObject").GetValue(oleConverter, null);

var item = dataObject.GetData(this.Format);
票数 8
EN

Stack Overflow用户

发布于 2009-07-31 17:10:00

在经历了数小时的沮丧之后,我终于找到了这个问题的第二个解决方案。究竟哪种解决方案是最优雅的,可能是旁观者的眼神。我希望Michael的和我的解决方案都能帮助沮丧的程序员,并在他们开始类似的任务时节省他们的时间。

首先,让我印象深刻的一件事是写字板能够接收开箱即用的拖放图像。因此,文件的打包可能不是问题所在,但可能在接收端发生了一些可疑的事情。

确实有可疑的地方。事实证明,关于.Net框架,有几种类型的IDataObjects。正如迈克尔指出的,OLE拖放支持在应用程序之间交互时尝试使用.Net远程处理。这实际上将一个System.Runtime.Remoting.Proxies.__TransparentProxy放在图像应该在的位置。显然,这并不(完全)正确。

下面的文章给了我一些正确的方向:

http://blogs.msdn.com/adamroot/archive/2008/02/01/shell-style-drag-and-drop-in-net-wpf-and-winforms.aspx

Windows窗体的默认值为System.Windows.Forms.IDataObject。然而,由于我们在这里处理的是不同的进程,所以我决定给System.Runtime.InteropServices.ComTypes.IDataObject一次机会。

在dragdrop事件中,下面的代码解决了这个问题:

代码语言:javascript
复制
const int CF_BITMAP = 2;

System.Runtime.InteropServices.ComTypes.FORMATETC formatEtc;
System.Runtime.InteropServices.ComTypes.STGMEDIUM stgMedium;

formatEtc = new System.Runtime.InteropServices.ComTypes.FORMATETC();
formatEtc.cfFormat = CF_BITMAP;
formatEtc.dwAspect = System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT;
formatEtc.lindex = -1;
formatEtc.tymed = System.Runtime.InteropServices.ComTypes.TYMED.TYMED_GDI;

这两个GetData函数只共享相同的名称。一个返回一个对象,另一个定义为返回空,并将信息传递给stgMedium输出参数:

代码语言:javascript
复制
(dea.Data as System.Runtime.InteropServices.ComTypes.IDataObject).GetData(ref formatEtc, out stgMedium);
Bitmap remotingImage = Bitmap.FromHbitmap(stgMedium.unionmember);

(sender as PictureBox).Image = remotingImage;

最后,为了避免内存泄漏,调用OLE函数ReleaseStgMedium可能是个好主意:

代码语言:javascript
复制
ReleaseStgMedium(ref stgMedium);

该函数可以包含如下内容:

代码语言:javascript
复制
[DllImport("ole32.dll")]
public static extern void ReleaseStgMedium([In, MarshalAs(UnmanagedType.Struct)] ref System.Runtime.InteropServices.ComTypes.STGMEDIUM pmedium);

...and这段代码似乎可以很好地处理两个应用程序之间的(位图)拖放操作。代码可以很容易地扩展到其他有效的剪贴板格式,也可能是自定义剪贴板格式。由于打包部分没有执行任何操作,因此您仍然可以将图像拖放到写字板,并且由于它接受位图格式,因此您还可以将图像从Word拖动到应用程序中。

作为附注,直接从IE拖放图像甚至不会引发DragDrop事件。真奇怪。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1201812

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档