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

Ped*_*ery 16 c# drag-and-drop winforms

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

只要它们在同一个表单上,这从一个PictureBox到另一个PictureBox就可以正常工作.

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

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

但是,出于某种原因,它可以拖放到Wordpad(但不是MS Word或画笔).

三个PictureBoxes将它们的事件连接起来如下:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后是这样的四个事件:

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);
}
Run Code Online (Sandbox Code Playgroud)
void pictureBox_GiveFeedback(object sender, GiveFeedbackEventArgs gfea) {
    gfea.UseDefaultCursors = false;
}
Run Code Online (Sandbox Code Playgroud)
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;
    }
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)

任何帮助将不胜感激!

Mic*_*key 10

经过大量咬牙切齿和拔毛之后,我才能想出一个可行的解决方案.似乎有一些无证的陌生感在.NET及其OLE拖放支持下进行.它似乎是在.NET应用程序之间执行拖放时尝试使用.NET远程处理,但这是否记录在任何地方?不,我认为不是.

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

[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;
    }
}
Run Code Online (Sandbox Code Playgroud)

要以支持位图的.NET和非托管接收者的方式使用该类,DataObject类用于拖放操作,如下所示.

要开始拖动操作:

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);
Run Code Online (Sandbox Code Playgroud)

完成操作:

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;
}
Run Code Online (Sandbox Code Playgroud)

首先执行对客户BitmapTransfer的检查,因此它优先于数据对象中常规Bitmap的存在.BitmapTransfer类可以放在共享库中,以便与多个应用程序一起使用.它必须标记为可序列化,如图所示,用于在应用程序之间拖放.我通过在应用程序中,应用程序之间以及从.NET应用程序到Wordpad中拖放位图来测试它.

希望这可以帮助你.


dar*_*ins 7

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

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);
Run Code Online (Sandbox Code Playgroud)


Ped*_*ery 6

经过几个小时的沮丧,我的耳朵里传来了蒸汽,我终于找到了解决这个问题的第二个方案.究竟哪种解决方案最优雅的可能是在旁观者的眼中.我希望迈克尔和我的解决方案能够帮助受挫的程序员,并在他们开始类似任务时节省时间.

首先,有一件事让我感到震惊的是,Wordpad能够立即接收拖放图像.因此,文件的打包可能不是问题,但接收端可能还有一些可疑的东西.

还有鱼腥味.事实证明,有一些关于.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事件中,以下代码解决了问题:

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;
Run Code Online (Sandbox Code Playgroud)

两个GetData函数只共享相同的名称.一个返回一个对象,另一个被定义为返回void,而是将信息传递给stgMedium out参数:

(dea.Data as System.Runtime.InteropServices.ComTypes.IDataObject).GetData(ref formatEtc, out stgMedium);
Bitmap remotingImage = Bitmap.FromHbitmap(stgMedium.unionmember);

(sender as PictureBox).Image = remotingImage;
Run Code Online (Sandbox Code Playgroud)

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

ReleaseStgMedium(ref stgMedium);
Run Code Online (Sandbox Code Playgroud)

该功能可以包括如下:

[DllImport("ole32.dll")]
public static extern void ReleaseStgMedium([In, MarshalAs(UnmanagedType.Struct)] ref System.Runtime.InteropServices.ComTypes.STGMEDIUM pmedium);
Run Code Online (Sandbox Code Playgroud)

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

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