Search code examples
c#windowswinformsmouseeventmouse

How to interact with mouse in Windows Form


I have a dictionary of buttons and also an null variable button. When you click on an button from the dictionary, we get the values ​​​​that should be sent to an empty variable.

public static Button selectedfigure=null;
public Dictionary<(int x, int y), Button> _Panels = new Dictionary<(int x, int y), Button>();

public void Fillboard()
{
    for (int x = 0; x < 8; x++)
    {
        for (int y = 0; y < 8; y++)
        {
            _Panels[(x, y)] = new Button()
            {
                Height = 64,
                Width = 64,
                Location = new Point(x * 64, y * 64),
                BackColor = CalculateBackColor(x,y),
                BackgroundImage = CalculateBackimage(x,y)               
            };
            Controls.Add(_Panels[(x, y)]);
        }
    }
}

Solution

  • Your question is about how to interact with Mouse events on your "board". The answer is to subscribe to Mouse events like Click for each control you add. Your code is using a normal Button but will be much simpler if you make a custom class that inherits Button or preferably PictureBox and this way each instance keeps track of its own X-Y, color, and image.

    Also, instead of using a Dictionary lookup and setting Location properties yourself, you might try an 8x8 TableLayoutPanel for the board, then iterate all its 8 columns and rows with methods that take column and row as arguments.


    The mouse events can be subscribed to in the addSquare method.

    private void addSquare(int column, int row)
    {
        var color = ((column + row) % 2) == 0 ? Color.White : Color.Black;
        var square = new Square
        {
            BackColor = color,
            Column = column,
            Row = row,
            Size = new Size(80, 80),
            Margin = new Padding(0),
            Padding = new Padding(10),
            Anchor = (AnchorStyles)0xf,
            SizeMode = PictureBoxSizeMode.StretchImage,
        };
        tableLayoutPanel.Controls.Add(square, column, row);
        // Hook the mouse events here
        square.Click += onSquareClicked;
        square.MouseHover += onSquareMouseHover;
    }
    

    Before and after iterating the TableLayoutPanel with addSquare

    squares


    Changing the value of the Square.Piece property will choose an image from a resource file.

    private void initImage(int column, int row)
    {
        var square = (Square)tableLayoutPanel.GetControlFromPosition(column, row);
        if (square.BackColor == Color.Black)
        {
            switch (row)
            {
                case 0:
                case 1:
                case 2:
                    square.Piece = Piece.Black;
                    break;
                case 5:
                case 6:
                case 7:
                    square.Piece = Piece.Red;
                    break;
                default:
                    square.Piece = Piece.None;
                    break;
            }
        }
    }
    

    Before and after iterating the TableLayoutPanel with initImage

    pieces


    Mouse event handlers are simple now:

    private void onSquareMouseHover(object sender, EventArgs e)
    {
        var square = (Square)sender;
        _tt.SetToolTip(square, square.ToString());
    }
    private void onSquareClicked(object sender, EventArgs e)
    {
        var square = (Square)sender;
        MessageBox.Show($"Clicked: {square}");
    }
    ToolTip _tt = new ToolTip();
    

    mouse events


    The Square class is uncomplicated, just a few lines of code.

    class Square : PictureBox // Gives more visual control than Button
    {
        public int Column { get; internal set; }
        public int Row { get; internal set; }
        Piece _piece = Piece.None;
        public Piece Piece
        {
            get => _piece;
            set
            {
                if(!Equals(_piece, value))
                {
                    _piece = value;
                    switch (_piece)
                    {
                        case Piece.None:
                            Image = null;
                            break;
                        case Piece.Black:
                            Image = Resources.black;
                            break;
                        case Piece.Red:
                            Image = Resources.red;
                            break;
                    }
                }
            }
        }
        public override string ToString() =>
            Piece == Piece.None ? 
                $"Empty {BackColor.Name} square [column:{Column} row:{Row}]" :
                $"{Piece} piece [column:{Column} row:{Row}]";
    }
    enum Piece { None, Black, Red };
    

    Edited (in response to Jeremy's excellent suggestion)

    Where and how to call the methods to initialize the board:

    public MainForm()
    {
        InitializeComponent();
        // Board is a TableLayoutPanel 8 x 8
        tableLayoutPanel.AutoSize = true;
        tableLayoutPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
        // Add squares
        IterateBoard(addSquare);
        // Add pieces
        IterateBoard(initImage);
    }
    
    void IterateBoard(Action<int, int> action)
    {
        for (int column = 0; column < 8; column++)
        {
            for (int row = 0; row < 8; row++)
            {
                action(column, row);
            }
        }
    }