Windows Develop Bookmark and Share   
 index > Windows Forms Designer > Excel like selection?
 

Excel like selection?

I have a collection of controls, displayed right next to each other like a grid. The user is supposed to select multiple controls, just like in Excel.
Though, I have no idea how to do it.
I tried using the MouseDown event, but that captures the mouse and it while holding down the mouse button, it won't fire for other controls.
I tried checking the mouse coordinates, but that only results in weird flickering and no multiple selection.

I really need help, I have this problem for over half a year already.
Thanks.
Megidolaon  Monday, July 13, 2009 1:00 PM

Hi Megidolaon,

Based on my understanding, you want to select multiple controls which likes selecting multiple cells in the excel. For example, when you pressed the Ctrl key and click several cells, all the cells would be selected. Another case is selecting a region. When you click and hold on the mouse, then drag the mouse, it would draw a rectangle and all the cells in the rectangle would be selected. If I misunderstood you, please feel free to tell me.

These are the main ideas of my solution:

1. Handle the MouseDown event of each control to handle the control select. If the Control key is pressed, we only need to select the new control. Otherwise, we need to unselect all the controls and then select the new control.

2. Handle the MouseDown event of the form or our user control to unselect all the controls.

3. Handle the KeyDown and KeyUp events of each control and the form to update the Control key state. We need to know if the Control key is pressed when we handle the control selecting.

4. Create a transparent form to draw the select rectangle.

5. Create a event to trace the select rectangle changing and handle the control selecting. If a control is in the select rectangle, select it; otherwise, unselect it.

This is my code snippet:

Form:
public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

private void Form1_Load(object sender, EventArgs e)

{

//Initial the control collection

for (int i = 0; i < 10; i++)

{

Button bt = new Button();

bt.Name = i.ToString();

bt.Text = i.ToString();

bt.Location = new Point(100, i * (bt.Height + 2) + 20);

_controls.Add(bt);

this.Controls.Add(bt);

}

//Set the event handlers of the controls.

foreach (Control ctrl in _controls)

{

ctrl.MouseDown += new MouseEventHandler(ctrl_MouseDown);

ctrl.KeyDown += new KeyEventHandler(ctrl_KeyDown);

ctrl.KeyUp += new KeyEventHandler(ctrl_KeyUp);

}

//Set the event handlers of the form.

this.KeyDown += new KeyEventHandler(ctrl_KeyDown);

this.KeyUp += new KeyEventHandler(ctrl_KeyUp);

this.MouseDown += new MouseEventHandler(Form1_MouseDown);

//Set the event handlers of the RectangleDrawer.

RectangleDrawer.Move += new EventHandler<MoveArgs>(RectangleDrawer_Move);

}

//Handle the select rectangle changing event.

private void RectangleDrawer_Move(object sender, MoveArgs e)

{

//Check each control, if it is in the rectangle, select; otherwise, unselect it.

foreach (Control ctrl in _controls)

{

if (e.SelectRect.IntersectsWith(ctrl.RectangleToScreen(ctrl.DisplayRectangle)))

this.SelectControl(ctrl);

else

this.UnselectControl(ctrl);

}

}

//Update the state to indicate the Control key is not pressed.

private void ctrl_KeyUp(object sender, KeyEventArgs e)

{

if (e.Control)

_isControlPressed = false;

}

//Update the state to indicate the Control key is pressed.

private void ctrl_KeyDown(object sender, KeyEventArgs e)

{

if (e.Control)

_isControlPressed = true;

}

//Only select the control which the mouse clicks on.

private void ctrl_MouseDown(object sender, MouseEventArgs e)

{

if(_isControlPressed == false)

this.UnselectAll();

this.SelectControl((sender as Control));

}

private void Form1_MouseDown(object sender, MouseEventArgs e)

{

//Start the select rectange drawing.

RectangleDrawer.Draw(this);

}

private void SelectControl(Control ctrl)

{

ctrl.BackColor = SELECT_BACK_COLOR;

}

private void UnselectControl(Control ctrl)

{

ctrl.BackColor = DEFAULT_BACK_COLOR;

}

private void UnselectAll()

{

foreach (Control ctrl in _controls)

{

ctrl.BackColor = DEFAULT_BACK_COLOR;

}

}

private static readonly Color SELECT_BACK_COLOR = SystemColors.ActiveCaption;

private static readonly Color DEFAULT_BACK_COLOR = SystemColors.Control;

private bool _isControlPressed = false;

private List<Control> _controls = new List<Control>();

}

Select rectangle drawing class:

public static class RectangleDrawer

{

private static Form mMask;

private static Point mPos;

public static Rectangle Draw(Form parent)

{

// Record the start point

mPos = parent.PointToClient(Control.MousePosition);

// Create a transparent form on top of <frm>

mMask = new Form();

mMask.FormBorderStyle = FormBorderStyle.None;

mMask.BackColor = Color.Magenta;

mMask.TransparencyKey = mMask.BackColor;

mMask.ShowInTaskbar = false;

mMask.StartPosition = FormStartPosition.Manual;

mMask.Size = parent.ClientSize;

mMask.Location = parent.PointToScreen(Point.Empty);

mMask.MouseMove += MouseMove;

mMask.MouseUp += MouseUp;

mMask.Paint += PaintRectangle;

mMask.Load += DoCapture;

// Display the overlay

mMask.ShowDialog(parent);

// Clean-up and calculate return value

mMask.Dispose();

mMask = null;

Point pos = parent.PointToClient(Control.MousePosition);

int x = Math.Min(mPos.X, pos.X);

int y = Math.Min(mPos.Y, pos.Y);

int w = Math.Abs(mPos.X - pos.X);

int h = Math.Abs(mPos.Y - pos.Y);

return new Rectangle(x, y, w, h);

}

//Rectangle changing event.

public static event EventHandler<MoveArgs> Move;

private static void DoCapture(object sender, EventArgs e)

{

// Grab the mouse

mMask.Capture = true;

}

private static void MouseMove(object sender, MouseEventArgs e)

{

// Repaint the rectangle

mMask.Invalidate();

if (Move != null && mMask != null && mMask.Visible)

{

//Fire the rectangle changing event.

Rectangle rect = Rectangle.Empty;

Point start = Point.Empty;

if (e.X > start.X && e.Y > start.Y)

rect = Rectangle.FromLTRB(mPos.X,mPos.Y,e.X,e.Y);

else if (e.X < start.X && e.Y < start.Y)

rect = Rectangle.FromLTRB(e.X, e.Y, mPos.X, mPos.Y);

if (rect != Rectangle.Empty) rect = mMask.RectangleToScreen(rect);

Move(null, new MoveArgs { SelectRect = rect });

}

}

private static void MouseUp(object sender, MouseEventArgs e)

{

// Done, close mask

mMask.Close();

}

private static void PaintRectangle(object sender, PaintEventArgs e)

{

// Draw the current rectangle

Point pos = mMask.PointToClient(Control.MousePosition);

using (Pen pen = new Pen(Brushes.Black))

{

pen.DashStyle = DashStyle.Dot;

e.Graphics.DrawLine(pen, mPos.X, mPos.Y, pos.X, mPos.Y);

e.Graphics.DrawLine(pen, pos.X, mPos.Y, pos.X, pos.Y);

e.Graphics.DrawLine(pen, pos.X, pos.Y, mPos.X, pos.Y);

e.Graphics.DrawLine(pen, mPos.X, pos.Y, mPos.X, mPos.Y);

}

}

}

//Select rectangle move arguments.

public class MoveArgs : EventArgs

{

public Rectangle SelectRect { set; get; }

}

You can get the old link about RectangleDrawer class from
http://social.msdn.microsoft.com/forums/en-US/winforms/thread/3087655c-bd50-4408-9c55-dd179e442675/.

Let me know if this helps.
Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Wednesday, July 15, 2009 10:12 AM
Thanks.

I think that helped me a bit, but it still doesn't quite work.
I first tried copying the code directly into a new project's form, but it wouldn't compile.
Then I added it to my project (capsulating RectangleDrawer into its own cs file). Though, it doesn't work properly.

The main problem is that you the RectangleDrawer Draw method gets the parent form and returns a rectangle with the selection, but that isn't actually used anywhere.
But using a new form for selection never occurred to me, it seems to be a great way of showing the selection.

Still, my main problem remains, I don't know how I can select controls (in my case PictureBoxes) and then use the selected controls after selection is done.

I'll try creating a new form for selection, then change size according to mouse movement and add all PictureBoxes the form intersects with to the collection of selected controls.
Megidolaon  Thursday, July 16, 2009 9:51 PM

Hi Megidolaon,

Could you please provide the errors why the code cannot be compiled? From my experience, the root cause of the error is that some references is not added properly. Please check the using statements, they ought to be like this:
using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.Runtime.InteropServices;

using System.Drawing.Drawing2D;

The code of RectangleDrawer is written by nobugz. I tested twice and it works fine. The Rectangle returned in Draw method is the selected rectangle, but I did not use it. I use the Move event to notify the main form which area is being selected. The purpose is to make the selecting behavior like selecting several icons on the desktop on XP: the icons would be selected while we are dragging the mouse. Otherwise, the icons can only be selected after we release the mouse.

In your reply, you said I don't know how I can select controls. If you mean selecting an area by dragging the mouse, my code snippet already shows how to do that. Besides the RectangleDrawer class, we only need to handle the MouseDown event of the form and call the Draw method of the RectangleDrawer class and handle the Move event of RectangleDrawer to cope with the selecting. My code snippet in last reply shows the detail.

You also asked How to use the selected controls after selection is done . We can store the selected controls in a list when we handle the selecting and use the selected controls wherever we want. In another way, we can store the select state in the Tag property of each control. When we handle the selecting, we can update the state, such as set the Tag property to true if selected or false if unselected. Then we can traverse the controls to get the selected controls and do something we want.

Let me know if this helps.
Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Friday, July 17, 2009 2:24 AM

Hi Megidolaon,

Could you please let me know if my reply helps you? If you still have problems, please feel free to tell me.

Regards,
Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Monday, July 20, 2009 10:05 AM

Hi,

We are changing the issue type to “General Discussion�because you have not followed up with the necessary information. If you have more time to look at the issue and provide more information, please feel free to change the issue type back to “Question�by opening the Options list at the top of the post window, and changing the type. If the issue is resolved, we will appreciate it if you can share the solution so that the answer can be found and used by other community members having similar questions.

Aland Li


Please mark the replies as answers if they help and unmark if they don't. This can be beneficial to other community members reading the thread.
Aland Li  Friday, July 31, 2009 12:41 PM

You can use google to search for other answers

Custom Search

More Threads

• Is this a good design?
• manual position of Form2 from Form1
• textbox accepts 0.99 to 99.99
• Ticker Control
• Auto generated number
• DataColumn property in Component
• Designer can't load form - "could not load type ...from mscorlib ....due to value type mismatch"
• VS2008 move controls outside visible area
• C++ CodeDOM parser error: Internal Error
• Previously visible user control becomes invisible in design view of winform(vb.net) in VS 2008.