|
drag and drop and resizing is very useful, and excellent.
With Regards,
Mukesh M
|
|
|
|
|
I can't seem to download your zip files.
|
|
|
|
|
is it possible to set a button to add new blocks ? and add snapping to borders ? for eg. divide work area into 3 columns and 3 rows (that will give us 9 cells) and one block can be only in one full cell ? and then add some other block that will have at the moment 8 cells to chose (available by drag and drop like You had presented) ? i just ask cause i don;t know how to achieve this, i don't expect You just to do this, but maybe You will have some guides for me, thanks in advance
|
|
|
|
|
The MaxWindowTrackSize property indicates the maximum dimensions to which a user can drag resize a window. The value returned by MaxWindowTrackSize refers to the dimensions of the entire desktop.
http://msdn.microsoft.com/en-us/library/system.windows.forms.systeminformation.maxwindowtracksize.aspx[^]
Outline will be limited in MaxWindowTrackSize.
Here is the fixed code.
class Outline: Form
{
#region Attributes
private static Outline outline = null;
private Point m_Delta = new Point(0, 0);
public Point Delta
{
get { return m_Delta; }
set { m_Delta = value; }
}
#endregion
#region Constructor
private Outline()
{
this.SuspendLayout();
this.ClientSize = new System.Drawing.Size(0, 0);
this.StartPosition = FormStartPosition.Manual;
this.BackColor = Color.Black;
this.Opacity = 0.4;
this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;
this.ControlBox = false;
this.TopMost = true;
this.ResumeLayout(false);
}
#endregion
#region Methods
static public void Show(Point location, Size size) {
if(outline == null) {
outline = new Outline();
outline.Show();
}
outline.Location = new Point((location.X > 0 ? location.X : 0),
(location.Y > 0 ? location.Y : 0));
outline.Delta = new Point((location.X < 0 ? location.X : 0),
(location.Y < 0 ? location.Y : 0));
Size displaySize = new Size(outline.Delta.X + size.Width, outline.Delta.Y + size.Height);
outline.Size = displaySize;
outline.Show();
}
new static public void Hide() {
if(outline != null) {
(outline as Control).Hide();
}
}
new static public void Resize(Size size) {
if(outline != null) {
Size displaySize = new Size(outline.Delta.X + size.Width, outline.Delta.Y + size.Height);
(outline as Control).Size = displaySize;
}
}
new static public void Move(Point location)
{
if(outline != null) {
Point originalDelta = outline.Delta;
outline.Location = new Point((location.X > 0 ? location.X : 0),
(location.Y > 0 ? location.Y : 0));
outline.Delta = new Point((location.X < 0 ? location.X : 0),
(location.Y < 0 ? location.Y : 0));
Point shift = new Point(outline.Delta.X - originalDelta.X,
outline.Delta.Y - originalDelta.Y);
Size displaySize = new Size((outline as Control).Size.Width + shift.X, (outline as Control).Size.Height + shift.Y);
(outline as Control).Size = displaySize;
}
}
#endregion
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Outline));
this.SuspendLayout();
resources.ApplyResources(this, "$this");
this.Name = "Outline";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.ResumeLayout(false);
}
} ;P
Welcome to discussion.
puzzlefun@hotmail.com
|
|
|
|
|
I'm trying to enable the use of full positioning control (ala Frontpage) through a custom WinForm app that would alter a ASPX page... Can the same types of things be used here to alter a WebForm?
|
|
|
|
|
Sorry - I have not tested that - so I don't know if similar technique can be used... Please let me know how you solve it!
Mats
|
|
|
|
|
Hi all!
There might be a need to improve the cursor part. At least if large objects are moved. Below is some suggestions of how to do this, but none is really easy. So help me out! I don’t update the code in the article until I have a good solution.
First a bug
In the event method userControl_MouseUp in MyUserControl.cs there should be a line that unregister the MouseMove event! So please add it if you are using this code.
private void userControl_MouseUp(object sender, MouseEventArgs mouseEventArgs) {
...
(sender as Control).MouseMove -= new MouseEventHandler(userControl_MouseMove);
...
}
Second some ways to improve the cursor handling
(1) The smallest change is to really clean up. That is calling Dispose() before we create the next cursor. I found out that using bitmap.GetHicon() will give a resource that is not managed. See this thread. It is possible to add a win32 call to dispose it direct. This way it works with large objects - but it's still ugly and slow. And it’s no fun to add win32 calls. Add/change this in MyUserControl.cs
<code>...
using System.Runtime.InteropServices;
...
[DllImport("user32.dll", EntryPoint="DestroyIcon")]
static extern bool DestroyIcon(IntPtr hIcon);
...
private Graphics cursorGraphics = null;
private Bitmap bitmap = null;
private Cursor cursor = null;
private IntPtr intPtr = IntPtr.Zero;
private Pen cursorPen = new Pen(Color.Black);
private Cursor OutlineCursor() {
if(cursor != null) cursorGraphics.Dispose();
if(cursorGraphics != null) cursorGraphics.Dispose();
if(bitmap != null) bitmap.Dispose();
if(intPtr != IntPtr.Zero) DestroyIcon(intPtr);
bitmap = new Bitmap(this.Width * 2, this.Height * 2);
if(cursorGraphics != null) cursorGraphics.Dispose();
cursorGraphics = Graphics.FromImage(bitmap);
cursorGraphics.DrawRectangle(cursorPen, this.Width - startPoint.X, this.Height - startPoint.Y, this.Width - 1, this.Height - 1);
intPtr = bitmap.GetHicon();
cursor = new Cursor(intPtr);
return cursor;
}</code>
(2) How to get rid of the "4 times as big as the object" cursor? Or how to set the HotSpot in the cursor. That seems impossible... The HotSpot is read only. The only way to set it is at design time (when creating a new cursor in VS2005). But at runtime it's not possible. I found this nice description of the .cur file format. My thought was to just create a bitmap with the same size as the object and add the header information to this bitmap to make it a cursor (or icon that is almost the same). Maybe SetPropertyItem() could be used. Or some other way to add this small info to the bitmap... This would be a nice piece of code to use in many other projects! Or we can use the old win32 call CreateCursor(…) . See this nice article!
(3) Why not skip using a cursor and instead draw the outline direct onto the parent? Well yes, I almost managed that. But first, it's creating more ugly connections from child to parent, and second, it was hard to continually draw the outline if there was a background picture in the parent window – try it and you will see. Remove the OutlineCursor() method and add the code below to your MouseMove event method. You also need a Parent.Invalidate() in MouseUp .
private void userControl_MouseMove(object sender, MouseEventArgs mouseEventArgs) {
Point point = this.PointToScreen(new Point(mouseEventArgs.X, mouseEventArgs.Y));
if(IsCursorOutside(point) == true) {
(sender as Control).MouseMove -= new MouseEventHandler(userControl_MouseMove);
if(cursorGraphics != null) {
cursorGraphics.Dispose();
cursorGraphics = null;
Parent.Invalidate();
}
this.DoDragDrop(this, DragDropEffects.Move);
} else {
if(cursorGraphics == null) cursorGraphics = Parent.CreateGraphics();
cursorGraphics.Clear(Parent.BackColor);
Point cursorPoint = Parent.PointToClient(this.PointToScreen(new Point(mouseEventArgs.X - startPoint.X, mouseEventArgs.Y - startPoint.Y)));
cursorGraphics.DrawRectangle(cursorPen, cursorPoint.X, cursorPoint.Y, this.Width - 1, this.Height - 1);
}
}
Mats
|
|
|
|
|
I can put one more possibility, however it works in completely other way: only with GDI objects, not controls, although maybe it is possible to extend this.
The problem is that drawing resizing/moving rectangles leaves their path. The idea is to save pixels which were under the rectangle (not it's interior - only bounds) and paint them when next reactangle has to be drawn.
I did it, however it needs "more ugly connections from child to parent", how you called it. This is small but most important part of the code
This one saves pixels under bound of rectangle to the array
int[][] GetData(Rectangle rect, Bitmap b)
{
int[][] ret = new int[4][];
int i;
if (!(new Rectangle(new Point(0, 0), new Size(b.Size.Width-1, b.Size.Height-1)).Contains(rect))
|| rect.Height < 0 || rect.Width < 0)
return null;
ret[0] = new int[rect.Width];
ret[1] = new int[rect.Height];
ret[2] = new int[rect.Width];
ret[3] = new int[rect.Height];
for (i = 0; i < rect.Width; i++)
ret[0][i] = b.GetPixel(rect.X + i, rect.Y).ToArgb();
for (i = 0; i < rect.Height; i++)
ret[1][i] = b.GetPixel(rect.X + rect.Width, rect.Y + i).ToArgb();
for (i = 0; i < rect.Width; i++)
ret[2][i] = b.GetPixel(rect.X + rect.Width - i, rect.Y + rect.Height).ToArgb();
for (i = 0; i < rect.Height; i++)
ret[3][i] = b.GetPixel(rect.X, rect.Y + rect.Height - i).ToArgb();
return ret;
}
This one draws saved edges into bitmap:
void DrawRectangle(int[][] data, Point p, Bitmap b)
{
if (data == null) return;
int width = GetWidth(data);
int height = GetHeight(data);
int i;
for (i = 0; i < width; i++)
b.SetPixel(p.X + i, p.Y, Color.FromArgb(data[0][i]));
for (i = 0; i < height; i++)
b.SetPixel(p.X + width, p.Y + i, Color.FromArgb(data[1][i]));
for (i = 0; i < width; i++)
b.SetPixel(p.X + width - i, p.Y + height, Color.FromArgb(data[2][i]));
for (i = 0; i < height; i++)
b.SetPixel(p.X, p.Y + height - i, Color.FromArgb(data[3][i]));
}
I used it when the parent was PictureBox , because calling it's Refresh redraws bitmap stored under Image property.
Maybe it is rather oriental stuff, but...
Ah! One thing about performance:
Good message: No memory jumps, mem usage minimal even if an object is big.
Bad message: High (100%) CPU usage - especially if an object is big. Things up to 100x100 pixels size perform rather good. CPU usage is the only hardware disadvantage - no memory problems.
|
|
|
|
|
Thanks for you help!
If I understand you correct the parent object needs to have a bitmap as background, and you use this bitmap to draw the outline on? If this is the case it feels a bit limited. Correct me if I'm wrong.
Mats
|
|
|
|
|
Yeach, it is extremely limited . Just another proposition. I am now working on sth like "DesignerPanel ", which would be a container control in which all child controls implementing "IDesignObject " could be "designed". Now it works for all user-definied IDesignObjects (with custom paint procedures) and the Button . I have some problems with ListBox and I did'n tried other controls yet. If I get some result, I will inform ya.
Greetings - gajato.
|
|
|
|
|
I have now changed to a better solution (I hope ) using a transparent form instead of the huge cursor. There is still some things to improve... there is some flicker when making the object smaller... And the challenge to package all this (and some more) into a nice reusable component...
Mats
|
|
|
|
|
By the way: This a code which is used by SharpDevelop (open-source IDE) to draw outline rectangle. As we can see, they reference "more directly" to Win GDI:
public static void DrawDragOutline(Region region)
{
if (region == null)
return;
IntPtr hDC = User32.GetDC(IntPtr.Zero);
IntPtr hRegion = region.GetHrgn(Graphics.FromHdc(hDC));
Gdi32.SelectClipRgn(hDC, hRegion);
Win32.RECT rectBox = new Win32.RECT();
Gdi32.GetClipBox(hDC, ref rectBox);
IntPtr brushHandler = GetHalfToneBrush();
IntPtr oldHandle = Gdi32.SelectObject(hDC, brushHandler);
Gdi32.PatBlt(hDC,
rectBox.left,
rectBox.top,
rectBox.right - rectBox.left,
rectBox.bottom - rectBox.top,
(uint)Win32.RasterOperations.PATINVERT);
Gdi32.SelectObject(hDC, oldHandle);
Gdi32.SelectClipRgn(hDC, IntPtr.Zero);
Gdi32.DeleteObject(hRegion);
User32.ReleaseDC(IntPtr.Zero, hDC);
}
They import GDI this way:
internal class Gdi32
{
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern int CombineRgn(IntPtr dest, IntPtr src1, IntPtr src2, int flags);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr CreateRectRgnIndirect(ref Win32.RECT rect);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern bool FillRgn(IntPtr hDC, IntPtr hrgn, IntPtr hBrush);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern int GetClipBox(IntPtr hDC, ref Win32.RECT rectBox);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern int SelectClipRgn(IntPtr hDC, IntPtr hRgn);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr CreateBrushIndirect(ref LOGBRUSH brush);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern bool PatBlt(IntPtr hDC, int x, int y, int width, int height, uint flags);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
}
|
|
|
|
|
Nice, but not excellent.
In my opinion, creation of ultra-huge cursor at runtime just to draw rectangle around an object is not a good idea. Try to maximize the window and make the control big. Program crashes after few moving operations.
Each moving operation increases memory usage for plenty megabytes, if an object is big, then memory steps are even 20 megabytes. Mem is not freed just after object is dropped so mem usage quickly exceeds 100 megabytes. It is not a smart solution.
Maybe for small controls (e.g. toolbar items, as you mentioned), memory is freed quicker than it is allocated, but I find it dirty anyway.
Greetings - gajatko.
|
|
|
|
|
Yes you are right!
This is not good. The cursor part has to be redone. I just tried to add some dispose on the bitmap and cursor objects - but it did not seem to help. As a first improvement it would be nice to change the center position of my cursor - possible? (To avoid make it that big). Also, if this part is done smarter we could also use the outline cursor when resizing the object!
So, please all of you, help me improve this part!
private Graphics cursorGraphics = null;
private Bitmap bitmap = null;
private Cursor cursor = null;
private Pen cursorPen = new Pen(Color.Black);
private Cursor OutlineCursor() {
if(cursor != null) cursor.Dispose();
if(cursorGraphics != null) cursorGraphics.Dispose();
if(bitmap != null) bitmap.Dispose();
bitmap = new Bitmap(this.Width * 2, this.Height * 2);
cursorGraphics = Graphics.FromImage(bitmap);
cursorGraphics.DrawRectangle(cursorPen, this.Width - startPoint.X, this.Height - startPoint.Y, this.Width - 1, this.Height - 1);
IntPtr intPtr = bitmap.GetHicon();
cursor = new Cursor(intPtr);
return cursor;
}
Mats
|
|
|
|
|
Very nice simplicity, cute code writing and complete documentation
These cuase to i say it's realy excellent
I'm working on a map designer project that has two main part, drag and drop from a toolbox and zoom control for my map,in first part i have not any problem, ofcourse reading this source help me to optimize my code, in second one i have some problems,in second part i want to zoom in my map and my added controls to map act like points of map, as needed, change location, scaling, zooming,...
Have you ever work on a project like this, Or have you any idia?
Regards,
Reza Samadabadi
|
|
|
|
|
Thanks!
Do you have a picture as map in the background or does your map just consist of objects that the user drags to the form? I guess that for your user dragged object you will need to recalculate the coordinates to achieve scaling and zoom, then if you have a bitmap as background you will have to recalculate the coordinates according to the bitmap as it resizes along with the form - a bit harder maybe. Hope to find your project here when it ready!
Good luck!
Mats
|
|
|
|
|
You are right Mats
I have a picture as a map in the background and in fact user drag objects on it, because of scaling and zooming of my map, i recalculate location,size,... for every object in my map, but you know that every solution is not good for every problem, i think it might be heavy or difficult for run on week computers or in a map with plenty of objects in it.
I guess it will finish on next week, and i will able to exactly test it on different PCs.
Thanks for your reply.
Reza
|
|
|
|
|
Can you give me some scenarios where this control can be used?
Thanks.
|
|
|
|
|
Well it's up to you!
But I have used this technique in an application used to monitor/control IO:s in a robot system. The application is a toolbox with different graphical objects the user can drag to forms. One is the IO objects, one is a container box used to group other objects, and one is a note used as post-it on the form a s o. All objects can be dragged to other forms a s o. To be able to do some nice layout all this is needed… When done all objects are serialized to file and can easy be restored on next session.
So, in an application that consists of some toolbox that the user can drag to some design surface this can be useful!
Mats
|
|
|
|
|
I can imagine using this for every application where great flexibility is demanded. With the starting point Mats gives to us, one might be able to include a form designer in an app.
Just imagine a CRM-Software, where users can design their own forms - with just the information they need!
Nice one, Mats!
Regards,
Mathias Wuehrmann
|
|
|
|
|