|
In VB6 you could drag a control (say a button) from one panel to another at run time, as you dragged a button sized rectangle would be displayed and in the DragOver and DragDrop events of the panel you could test to see if what was being dragged/dropped was what you required.
How would I do that in C#? I am finding the Drag/Drop model in .NET to be very confusing. Any help would be much appreciated.
Thanks in anticipation
JP
England.
|
|
|
|
|
Yeah, you do have to use the drag and drop routines in .NET, but it doesn't have to be as hard as you might think. Clipboard formats (which apply both to the clipboard and drag-n-drop, though for the latter nothing is place in the clipboard per se) are just a string (as far as .NET is concerned) so as long as your application understands the clipboard format (so that the source and destination use the same format), you can put anything into the IDataObject implementation (you can use the provided DataObject , which - btw - isn't the IDataObject defined for COM but uses it internally).
So, in your source control, you could do something like this:
DataObject do = new DataObject(".NETControl", this);
this.DoDragDrop(do, DragDropEffects.Move); In your destination, handle the DragOver and DragDrop events (or override the respective methods in a derived class as in the following example) at least:
protected override void OnDragOver(DragEventArgs e)
{
if (e.Data.GetDataPresent(".NETControl") &&
(e.AllowedEffect & DragDropEffects.Move) != 0)
e.Effect = DragDropEffects.Move;
}
protected override void OnDragDrop(DragEventArgs e)
{
if (e.Data.GetDataPresent(".NETControl") &&
(e.AllowedEffect & DragDropEffects.Move) != 0)
{
Control c = (Control)e.Data.GetData(".NETControl");
c.Location = new Point(e.X, e.Y);
}
} It's a simple example, but it should work (it's similar to what I've done in the past for dragging and dropping controls at run-time).
To note, the DataFormats class contains common clipboard formats for dragging and dropping strings, HTML, etc. Read about it in the .NET Framework SDK for more information.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Thank you - thats's been a great help.
Rugby League: The Greatest Game Of All.
|
|
|
|
|
How do you automatically select the first row of the datagrid? I have searched the intellisense and do not see anything that I would use to set this first row.
Thanks!
Code Toad
|
|
|
|
|
Have you tried DataGrid.Select(rownumber) ?
Mazy
"Man is different from animals in that he speculates, a high risk activity." - Edward Hoagland
|
|
|
|
|
I have implemented an IStream COM object that reads chars / bytes off the OLEClipboard. However, i'm still stuck with reading one charachter at a time, and this is bothering me a great deal (This has to be really innefficient swapping to and from managed code with 1 item at a time, especialy for objects over a meg...).
Does anyone know where I can find some sample code where the user forwards a pointer to an initialised array and gets back the array with initialised content?
Cheers
Cata
|
|
|
|
|
Ugh...I suppose you could preload the whole mess into a SAFEARRAY, and then marshal that...
You could also try using a global memory write, then a global memory read on the .NET side, but that's a whole 'nother can of worms to open up.
Jeremy Kimball
|
|
|
|
|
"Ugh" - ye, my sentiments exactly. Oh well, too bad, it must be done (because i want to).
Can you tell me anymore about these things? Global Memory write i've never really encountered .NET side before. I can look up safearrays, but to be honest, i'm slightly confused about the mechanics behind the whole operation.
I can handle in arrays, and out arrays, but ref arrays are just bugging the hell out of me.
Cata
|
|
|
|
|
[EDIT]Out of curiosity, what are you trying to do with all this?[/EDIT]
SAFEARRAY's are, to be painfully blunt, a f'ing mess to work with. Their original purpose was to handle cross-process and cross-apartment marshalling of "array-ish" data.
Here be the mess:
typedef struct FARSTRUCT tagSAFEARRAY {
unsigned short cDims;
unsigned short fFeatures;
#if defined(WIN32)
unsigned long cbElements;
unsigned long cLocks;
#else
unsigned short cbElements;
unsigned short cLocks;
unsigned long handle;
#endif
void HUGEP* pvData;
SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY;
In the mystical land of Interop, these things generally are auto-marshalled into System.Array's, which is nice. Problem is working with the original SAFEARRAY's.
Let me play around with some stuff when I get home from work (tis 2pm here now), and I'll see if you can even efficiently "get at" global memory from .NET.
Jeremy Kimball
|
|
|
|
|
Well, i was thinking, if i allocated 2 bytes to global memory, get the intptr to them, forward them to the char array, and read it out as a char (not sure how to cast that part). I could see if it works with one char.
Following that, if i create a block of memory say, 100 bytes, i could then read each of the byte pairs out, to get a total of 50 chars?
Again, i'm not sure how to cast the two bytes, but i'll have a tinker.
As for what i'm doing. I was looking at how easy it would be to implement managed to unmanaged drag and drop. Mainly handling a stream of bytes.
Turns out, not so easy, but really interesting anyhow, and a valuable learning experience.
Cata
|
|
|
|
|
Putting data in a SAFEARRAY isn't always an option - you can't be sure what's in an IStream unless you're explicitly putting it there.
Using the correctly marshalling attributes is the first step. Again, keep in mind that a pointer to an array is just the address of the first element. If you look at ALL the native APIs that use arrays (even those that use LPTSTR , which is nothing more than array of TCHAR s), they always require an [in] or [in/out] specifying how many elements to expect. This is because you get the address of the first element and you keep offsetting the address by the bit width of the processor (typically) X number of times where X is the number of elements.
Also keep in mind that an array is already a reference type (even if it is an array of value types), so including the out or ref keywords is often unnecessary. Pass an instance of the array. Take, for instance, the GetComputerName API (off the top of my head):
BOOL GetComputerName( LPTSTR lpBuffer, LPDWORD lpnSize); In .NET, you'd simply declare and use it like:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern bool GetComputerName(string buffer,
[MarshalAs(UnmanagedType.U4)] ref int size);
int size = 16;
string buffer = new string(size, '\0');
GetComputerName(buffer, ref size); The same is true for arrays.
The problem is once you get your array of bytes (don't ever assume their characters), now you must get it into a managed object or read/write it as is.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
I think i've been doing something horendously retarded. I've been trying to marshal my arrays using ref and out. It didn't work. That's been a major problem with my way of thinking.
I just ditched the ref, and it's working... kind of.
I can see the problem with my char array. I've declared an array of char (that's all i'm worried about at the moment, byte buffers can wait until i get to that point, for my test porgram i know it's going to be chars.) Now, i send this array of 10 char elements in, and marshal it as an LPArray of size 10. However, i'm getting 5 elements in the array of 4 bytes in size, (They are all over 127 value). The rest is blank.
How do i get it to use Uni instead of Ansi charachters?
I tried your idea with strings, but all i get back in the string, regardless of the number of elements i ask for (and duly get), is 4 blank charachters, this is the same for LPStr and BStr marshaling. Not entirely sure why.
Cata
|
|
|
|
|
First, don't marshal a char array as an array - marshal it as the appropriate string (since a string is a char array anyway). When you get a byte buffer back, you can use the appropriate Encoding class to get the string from the bytes.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Heath is right, of course (damn you Heath! )
When you pass a reference type from unmanaged space through the interop layer, it automagically pins or copies the referenced value types so you can work with them in the managed space.
Having a second to peek through MSDN, however, turned this up:
UCOMIStream
Which may, in fact, be exactly what you're looking for...
Jeremy Kimball
|
|
|
|
|
lol!
bugger - all that work
oh well, at least i know I can marshal IStream....
Cheers mate
Cata
|
|
|
|
|
*sticks tongue out at Heath* There! *I* answered one for once!
[EDIT:] BTW Heath, wasn't around to congratulate you on your "MVP-ness"[/EDIT]
|
|
|
|
|
Oh yeah...well...well...I have over 3,400 posts! Hah!
Thanks, BTW!
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
This really sucks.
It doesn't give you a valid result for how many elements were retrieved, always a zero IntPtr, so you don't know when you reach the end of the stream, and you can't collect the error value because it returns void.
wtf are Microsoft playing at?
I have to copy the buffer on each iteration, and compare the array of bytes now to see if they are identical. THEN i have to figure out where the stream ends in the frickin buffer. That's LAME man. I did a better job than that. *GRRRR*
Cata
|
|
|
|
|
|
I've decided i'm going to stick with my own implementation, as having no value is of little use. The resultant intptr from read is always zero (null). I spent a while with it, but MS descided that no return value was nescessary... lovely.
I've got it so that it reads the byte array, and converts it into a string. Nifty. I am a very happy little computer geek
Here is what I have:
pIStream.Seek(pInZero,STREAM_SEEK_Flags.STREAM_SEEK_SET, out pSeekResult);<br />
<br />
sbyte[] buffer = new sbyte[bufferSize];<br />
<br />
string charReader = "";
int read;
<br />
do<br />
{<br />
pIStream.Read(buffer, bufferSize, out read);<br />
<br />
if (read != 0)<br />
{<br />
unsafe<br />
{<br />
fixed(sbyte* pChars = buffer)<br />
{<br />
charReader += new string(pChars,0,read,System.Text.Encoding.ASCII);<br />
}<br />
}<br />
}<br />
}while (read != 0);
it works fine, reading huge text documents.
I'm interested as to what else I can do with it.
I think i need to start building a decent layout, my test program has got pretty big, and ugly as sin. That said... IT WORKS! ^^
Cata
|
|
|
|
|
If you don't want to convert the VB.NET code yerself, here's a quick try at it:
UCOMIStream stream = ...;
int offset = ....;
int count = ....;
byte[] resultBuffer;
if( stream == null ) throw new ObjectDisposedException("stream");
int bytesRead = 0;
object boxBytesRead = bytesRead;
GCHandle hObject;
try {
hObject = GCHandle.Alloc(boxBytesRead, GCHandleType.Pinned);
IntPtr pBytesRead = hObject.AddressOfPinnedObject();
if( offset != 0 ) {
byte[] tmpBuffer = new byte[count - 1];
stream.Read( tmpBuffer, count, pBytesRead );
bytesRead = (int)boxBytesRead;
System.Array.Copy( tmpBuffer, 0, resultBuffer, offset, bytesRead);
} else {
stream.Read( resultBuffer, count, pBytesRead );
bytesRead = (int)boxBytesRead;
}
} finally {
if( hObject.IsAllocated() ) {
hObject.Free();
}
}
}
Not tested, of course....
Jeremy Kimball
|
|
|
|
|
I take it by doing it the way i posted above, i'm getting an int from a pointer, and not handling it properly.
That will lead to memory leaks? Unless i unallocate it?
Cata
|
|
|
|
|
I hope I can explain this well enough to make sense, I am writing a C#/ASP app where I have a grid (4x3) of buttons on a page. Each one is named btnMonth1, btnMonth2 - btnMonth12 I am working with some database stuff where they may only be working on certain months at a time so depending on what number is in the data I would like to make it visible... so if I had a variable mth I would want to somehow say btnMonth(mth).visible = true; AFAIK in C++ you can do this by incrementing it but I don't think forms use resource ID's so I am not sure this would work.
I'm trying to learn something at the same time while trying to cut a big switch statement out of the mix that seems unnecessary. Maybe that's how it should be done though?
Any help is appreciated
Thanks!
|
|
|
|
|
Is this on the web page? If so, you can use:
System.Web.UI.Control.FindControl(string id)
If it's on a windows form, you'd probably have to do some funky iteration using GetNextControl or something...
Jeremy Kimball
|
|
|
|
|
Yes it's on a webpage. Thanks! I'll give this a try!
|
|
|
|
|