سحب وإسقاط بين حالات من نفس تطبيق نماذج Windows

StackOverflow https://stackoverflow.com/questions/1201812

  •  05-07-2019
  •  | 
  •  

سؤال

لقد خلق نوافذ صغيرة أشكال تطبيق الاختبار إلى محاولة بعض السحب/إسقاط رمز.النموذج يتكون من ثلاثة PictureBoxes.نيتي كانت أن انتزاع صورة من PictureBox, عرض مثل مخصص المؤشر خلال عملية السحب ، ثم أسقطه على آخر PictureBox الهدف.

هذا يعمل بشكل جيد من PictureBox إلى آخر طالما هم على نفس النموذج.

إذا كنت فتح مثيلين من نفس التطبيق و محاولة السحب/الإفلات بينهما ، أحصل على ما يلي خفي خطأ:

هذا الاتصال عن بعد وكيل له أي قناة الحوض مما يعني إما الملقم لا تسجيل ملقم قنوات الاستماع, أو هذا التطبيق لا يوجد لديه مناسبة العميل قناة التحدث الملقم.

لسبب ما ، ومع ذلك ، فإنه عمل على السحب/الإفلات إلى الدفتر (ولكن ليس 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);
    }
}

ثم هناك أربعة أحداث من هذا القبيل:

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

void pictureBox_GiveFeedback(object sender, GiveFeedbackEventArgs gfea) {
    gfea.UseDefaultCursors = false;
}

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

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

أي مساعدة ستكون موضع تقدير كبير!

هل كانت مفيدة؟

المحلول

وبعد الكثير من صرير الأسنان وسحب من الشعر، وكنت قادرا على التوصل إلى حل عملي. يبدو أن هناك بعض الغرابة لا يحملون وثائق يحدث تحت الأغطية بدعم .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;
    }
}

لاستخدام فئة بطريقة من شأنها أن تدعم كلا. 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);

لإكمال العملية:

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

ويتم تنفيذ الاختيار للBitmapTransfer أول زبون لذلك يأخذ الأسبقية على وجود نقطية العادية في كائن البيانات. يمكن أن توضع الطبقة BitmapTransfer في مكتبة مشتركة للاستخدام مع تطبيقات متعددة. يجب وضع علامة تسلسل كما هو مبين على السحب والإسقاط بين التطبيقات. اختبرته مع سحب وإسقاط الصور النقطية ضمن التطبيق، بين التطبيقات، ومن تطبيق .NET لالدفتر.

وآمل أن يساعد هذا لكم.

نصائح أخرى

وكنت مؤخرا قد جاء عبر هذه المشكلة، وكان يستخدم تنسيق مخصص في الحافظة، مما يجعل إمكانية التشغيل المتداخل أكثر من الصعب بعض الشيء. على أي حال، مع قليل من انعكاس الضوء وكنت قادرا على الحصول على 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);

بعد ساعات وساعات من الإحباط مع البخار يخرج من أذني ، وأخيرا وصلت في الثانية حل هذه المشكلة.بالضبط الحل الأكثر أناقة هو على الارجح في عيني الناظر.أتمنى أن مايكل و حلول كل المساعدات بالإحباط المبرمجين و توفر لهم الوقت عندما تشرع في أسئلة مماثلة.

أولا وقبل كل شيء, الشيء الوحيد الذي لم يبدو لي كان هذا الدفتر كان قادرا على تلقي السحب/إسقاط الصور فقط في الخروج من مربع.وهكذا التعبئة والتغليف الملف ربما ليست المشكلة, ولكن كان هناك ربما شيء مريب يحدث في المتلقي.

و مريب كان هناك.اتضح أن هناك seveal أنواع IDataObjects العائمة حول .Net framework.مايكل أشار OLE السحب والإفلات دعم محاولات لاستخدام .Net remoting عند التفاعل بين التطبيقات.هذا الواقع يضع النظام.وقت التشغيل.الاتصال عن بعد.وكلاء.__TransparentProxy حيث الصورة هو المفترض أن يكون.ومن الواضح أن هذا ليس (تماما) الصحيح.

المقالة التالية أعطاني بعض المؤشرات في الاتجاه الصحيح:

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

ويندوز أشكال التخلف إلى النظام.يقوم نظام ويندوز.أشكال.IDataObject.وبما أننا نتعامل مع مختلف العمليات هنا قررت أن تعطي النظام.وقت التشغيل.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;

اثنين GetData وظائف فقط مشاركة نفس الاسم.واحد بإرجاع كائن ، والآخر هو تعريف العودة باطلة و بدلا من ذلك بتمرير المعلومات في stgMedium بها المعلمة:

(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:

ReleaseStgMedium(ref stgMedium);

هذه الوظيفة يمكن إدراجها على النحو التالي:

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

و هذا الرمز يبدو للعمل تماما مع سحب وإسقاط العمليات (من الصور النقطية) بين اثنين من التطبيقات.رمز يمكن بسهولة أن تمتد إلى أخرى صالحة تنسيقات الحافظة وربما مخصصة الحافظة صيغ جدا.لأن كل شيء تم القيام به مع التعبئة والتغليف جزء ، لا يزال بإمكانك dragdrop صورة الدفتر ، لأنه يقبل نقطية صيغ ، يمكنك أيضا سحب صورة من كلمة في التطبيق.

كملاحظة جانبية, سحب وإسقاط صورة مباشرة من أي حتى لا يرفع الحدث DragDrop.غريب.

وفقط من حب الاستطلاع، في الأسلوب DragDrop، هل حاولت اختبار ما إذا كان يمكنك الحصول على صورة نقطية من DragEventArgs على الإطلاق؟ دون أن تفعل المدلى بها المرسل؟ وأنا أتساءل ما إذا كان الكائن picturebox ليس للتسلسل، والذي يسبب المشكلة عند محاولة استخدام المرسل في مجال التطبيق مختلف ...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top