 |
|
 |
Pretty cool, however if you drag a control outside the viewable area, it auto-scrolls correctly, but then when you scroll down and try to move the control, the X and Y coordinates must be off because it gets all out of wack. For instance; drag a control down off the page, see that the scrollbars automatically show up, scroll down to the control, then try to move it just a little bit. It immediately shoots back up and the scrollbars disappear. You are probably not taking the AutoScrollPosition or Scroll offset into account ??
|
|
|
|
 |
|
 |
Hi MachineGun,
I have just tested my control and can confirm that it does NOT exhibit the symptoms you describe. Unfortunately since I wrote my own control and this one was the inspiration rather than necessarily the basis of the control I am not sure where the differences might lie - so much is different that it is difficult even to guess.
|
|
|
|
 |
|
 |
Thanks for responding. Would it be too much to ask to post just your MouseDown, MouseMove and MouseUp functions just so I can see how you're doing the move math?
|
|
|
|
 |
|
 |
Hi MachineGun,
My control really is very different to the one in the article, having very many features that it doesn't and not having many issues that arise from the original code. For example, as well as the other items listed I allow editing to be turned on and off via a simple property so that the control can be used for both design and presentation with only administrators allowed access to the former. However, the names of the various properties, fields and variables used in the following code should be fairly self explanatory...
Below is the whole of the control event handlers region which hopefully will give you all you need to build your own control:
#region Control Event Handlers
private void ControlMouseEnter(object sender, EventArgs e)
{
var control = (Control) sender;
_defaultCursor = control.Cursor;
if (_canEdit && _canMoveControls)
{
control.Cursor = Cursors.SizeAll;
}
}
private void ControlMouseLeave(object sender, EventArgs e)
{
var control = (Control) sender;
control.Cursor = _defaultCursor;
}
private void ControlMouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_isMouseDown = true;
}
_hitPoint = e.Location;
if (_canEdit)
{
var control = (Control) sender;
_tracker.Control = control;
}
}
private void ControlMouseMove(object sender, MouseEventArgs e)
{
var control = (Control) sender;
if (_canEdit && _canMoveControls && _isMouseDown && e.Button == MouseButtons.Left)
{
_dragging = true;
if (_tracker.Control == control)
{
_tracker.Visible = false;
}
control.Left += e.Location.X - _hitPoint.X;
control.Top += e.Location.Y - _hitPoint.Y;
}
}
private void ControlMouseUp(object sender, MouseEventArgs e)
{
_isMouseDown = false;
if (_canEdit && _canMoveControls && _dragging)
{
if (_snapToGrid)
{
int xPos = ((Control) sender).Location.X;
int yPos = ((Control) sender).Location.Y;
if (xPos%_gridSpacing != 0)
{
xPos = ((int) Math.Round(xPos/(decimal) _gridSpacing))*_gridSpacing;
((Control) sender).Left = xPos;
}
if (yPos%_gridSpacing != 0)
{
yPos = ((int) Math.Round(yPos/(decimal) _gridSpacing))*_gridSpacing;
((Control) sender).Top = yPos;
}
}
OnLayoutChanged(EventArgs.Empty);
}
var control = (Control) sender;
if (_tracker.Control == control)
{
_tracker.Visible = true;
if (control.Top < 0)
{
control.Top = 0;
}
if (control.Left < 0)
{
control.Left = 0;
}
}
_selectedControl = control;
OnSelectionChanged(EventArgs.Empty);
_dragging = false;
}
private void ControlVisibleChanged(object sender, EventArgs e)
{
if (_canEdit && (_canMoveControls || _canResizeControls))
{
var control = (Control) sender;
if (_tracker.Control == control)
{
_tracker.Visible = control.Visible;
}
}
}
private void ControlSizeChanged(object sender, EventArgs e)
{
if (_tracker != null)
{
_tracker.SetBounds();
}
}
private void ControlLocationChanged(object sender, EventArgs e)
{
if (_tracker != null)
{
_tracker.SetBounds();
}
}
private void ControlHandleDestroyed(object sender, EventArgs e)
{
_tracker.Control = null;
}
#endregion
I hope that this is sufficient to help you - I don't think there is anything there that you will find difficult
|
|
|
|
 |
|
 |
Thanks! I appreciate you taking the time. Hey, if nothing else, the snap to grid logic is cool! I will definately check it out. Thanks again!
|
|
|
|
 |
|
 |
I would like to suggest may be a good upgrade of this is Xml integration so that the panel position and size can remember the next time the application open
|
|
|
|
 |
|
 |
Hi Alireza,
Nice code, thank you for sharing it. I can see a number of possible improvements, but the immediately obvious one is that if you click on the canvas itself any previously selected control appears to remain selected i.e. it continues displaying the tracker handles even when it is not in fact selected. This is simple to solve by overriding the OnMouseDown event of the canvas:
protected override void OnMouseDown(MouseEventArgs e)
{
tracker.Visible = false;
base.OnMouseDown(e);
}
I have only had a quick look at the code, and there may be a better way of handling this not least because there is still a problem if you click on a control that is not resizeable... it might be nice to still show a highlight but not implement the handles in that scenario. This could be done by removing the checking code from the Control_MouseDown handler in the Canvas class and just passing the control anyway:
private void Control_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
isMouseDown = true;
hitPoint = e.Location;
Control control = (Control)sender;
tracker.Control = control;
}
}
Then in the tracker class you can simply modify the OnPaint event handler so that it always draws the highlight but only draws the handles if the control can be resized:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.Clear(BackColor);
PropertyInfo info = control.GetType().GetProperty("AutoSize");
if (info != null && ((bool)info.GetValue(control, null)) == false)
{
e.Graphics.FillRectangles(Brushes.White, trackers);
e.Graphics.DrawRectangles(Pens.Black, trackers);
}
}
In note that GetProperty is called more than once, so I would suggest a variable called 'resizable' be set when the control is assigned to the tracker which can then be interrogated so that reflection is used only once. There is also an issue with selecting Text Boxes for resizing, and it would be good to have alignment indicators as we do in VS - I will look at these issues when I get time and post a further suggestion if I come up with sensible solutions.
Chris Bray
|
|
|
|
 |
|
 |
Thank you so much for your consideration. These facts are noticeable and I will look at them in order to improve it.
modified on Saturday, March 27, 2010 1:12 AM
|
|
|
|
 |
|
 |
You are very welcome. Inspired by your idea I have created a new control that operates like the one in Visual Studio designer - I have added the features I suggested but also many more including:
• SelectedControl property
• SelectionChanged event - used in conjunction with SelectedControl allows the properties of the selected control to be automatically displayed and edited in a PropertyGrid
• ShowGrid property that controls whether or not a layout grid is displayed
• SnapToGrid property that controls whether or not a control snaps to the grid when moved
• GridColor property that allows you to set the color of the grid
• GridSpacing property that allows you to define the space between grid points in pixels
• ResizeMode enumeration that determines which handles are displayed depending on control type
• Recognition of multiple control types and queryng the relevant properties so that controls that only have linear resize capability such as single line text boxes and date time pickers have ResizeMode.WidthOnly and just the RightMiddle and LeftMiddle handles are displayed
I have also created a dialog with rulers and property grid to consume the new control.
This is all still a work in progress, but the original idea came from your article which has been acknowledged in the source code.
|
|
|
|
 |
|
 |
These ideas are so interesting, I really appreciate all your operation on this sample. I have got hope of getting a new version of this sample, of course from you
|
|
|
|
 |
|
 |
Hi Alireza,
Still more ideas flooding out!
I have now added an additional dialog to my test sample that allows the tab order of the controls to be set manually at runtime, so that if you re-order the controls you can easily make them accessible in the correct order - I am considering whether to build that into the control itself rather than as part of the test application, and whether to add an automatic reset of tab order based on control positions, with perhaps the option to choose across then down or down then across ordering. I have also added the ability to modify component placement using the arrow keys on the keyboard, and to add and remove controls using the Insert and Delete keys.
I have added an OnInsertRequested event so that a suitable 'Add' dialog can be created and an OnLayoutChanged event so that notification of the fact that the layout is different can be used to allow saving or other processing of the controls.
I also intend to add more features including:
• Multi control selection on Shift+Click and / or dragging a marquee over controls on the canvas
• Alignment commands for aligning selected controls within the canvas and with each other e.g centre in window, space equally, align tops, align centres, etc.
• Guidelines for alignment - not sure yet whether to add them to the canvas like in a DTP application or to display them between controls as in VS - or possibly both?
• Plenty more ideas to follow...
I am very pleased with the results so far, and the entire project was inspired by your control. Thanks again for sharing the idea.
Chris Bray
|
|
|
|
 |
|
 |
Chris, were you able to resolve the scrolling issue? Would you mind sharing your updates?
|
|
|
|
 |
|
 |
Hi MachineGun,
I have not experienced the scrolling issue you describe, so have made no attempt to resolve it even if it exists in my project.
I have the project working as far as I actually need it, and have necessarily moved on to other areas intending to revisit it in due course.
|
|
|
|
 |
|
 |
Is the Canvas control supposed to be in the toolbox? I can't get the Canvas control into my form.
|
|
|
|
 |
|
 |
This is a great tool!
If you want to make it a little easier for someone to understand what is going on, though, the Canvas object declared in the "Windows Form Designer generated code" should be moved out of MainForm.Designer.cs and into MainForm.cs.
It works flawlessly, though.
|
|
|
|
 |
|
 |
The control is too basic. Its not worth a article its a code snippet.
|
|
|
|
 |