0
votes

I am saving the extents of a control on a WinForm in a dictionary such as:

Dictionary<Tuple<int, int>, Control> dictionary = 
    new Dictionary<Tuple<int, int>, Control>();

And I am drawing controls on the WinForm programmatically. Whenever I draw each control I save the bounds of that control in this dictionary such as:

dictionary.Add(Tuple.Create(myControl.X, myControl.Y), myControl);
dictionary.Add(Tuple.Create(myControl.X + myControl.Length, myControl.Y), myControl);
dictionary.Add(Tuple.Create(myControl.X + myControl.Length, myControl.Y + myControl.Width), myControl);
dictionary.Add(Tuple.Create(myControl.X, myControl.Y + myControl.Width), myControl);

Now, what I want to achieve is that whenever there is a mouse click on the WinForm for MouseEventArgs e, I want to check whether Point(e.X, e.Y) lies within the bounds of a control or not??

I am aware that I can iterate through the Key value pairs of the dictionary and calculate whether the Point(e.X,e.Y) lies within the bound or not. But I want to avoid iterating through the Keys of the dictionary and get the solution.

Any idea how I can achieve it without iterating through the dictionary and calculate for each point?

2
First, why do you need to store the positions in a tuple? The control will tell you exactly where it is. - DavidG
Thanks for the reply, David. I know that the control tells we about its whereabouts but if I have to check whether the mouse clicked point lies within a control or not, I have to iterate through all the control and check individually till I find which control falls on the click event point. - user8719628
Now, if I use iteration thru control, the function is very slow for too many controls on the form. Instead I thought storing the co-ordinates in a dictionary would enable me access whether the clicked point lies within the bounds of a control or not easily. But again I still find myself in the same issue of iterating thru the dictionary, which I want to avoid. - user8719628
2 seconds for 300 controls doesn't seem right at all, your logic must be doing something extra. - DavidG
As an aside, why are you not just capturing the mouse click even on the actual control in the first place? That way you don't need to search anything. - DavidG

2 Answers

0
votes

Iteration should be super fast, especially if you are using the standard controls properties to determine it's position and size. For example this code will generate 5000 controls, gives them all random size and position, then loop through each one and tell you if a point sits within it's boundaries. This entire thing runs in less than 0.4 seconds on my machine:

var stopwatch = new Stopwatch();
stopwatch.Start();

Random rnd = new Random();

//Generate 500 controls with random positions
var controls = Enumerable.Range(0, 5000)
    .Select(index => new Control 
    {
        Top = rnd.Next(0, 1000), 
        Left = rnd.Next(0, 1000), 
        Width = rnd.Next(0, 1000), 
        Height = rnd.Next(0, 1000) 
    })
    .ToList();

var controlsUnderMouse = controls
    .Where(c => c.Bounds.Contains(150, 150))
    .ToList();

Console.WriteLine($"Found {controlsUnderMouse.Count()} controls in {stopwatch.Elapsed}");

Which will output something like this:

Found 106 controls in 00:00:00.3939616

If you are only looking for a single control, you could change the Where to First and that will return even quicker.

0
votes

Try this. If you want, you can replace the int id with a Control.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reactive.Linq;
using System.Windows.Forms;

namespace Anything
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var random = new Random();
            var bounds = Screen.PrimaryScreen.Bounds;
            var extents = new Dictionary<int, Rectangle>();

            for (var i = 0; i < 300; i++)
            {
                var x = random.Next(bounds.Width);
                var y = random.Next(bounds.Height);
                var w = random.Next(bounds.Width - x);
                var h = random.Next(bounds.Height - y);

                extents[i] = new Rectangle(x, y, w, h);
            }

            var domains = from time in Observable.Interval(TimeSpan.FromMilliseconds(100))
                          from extent in extents
                          where extent.Value.Contains(Control.MousePosition)
                          select extent.Key;

            var subscription = domains.Subscribe(id => Console.Write($"{id},"));

            Console.ReadKey();

            subscription.Dispose();
        }
    }
}