3
\$\begingroup\$

Situation So I have made a list of PictureBoxes on my form that looks as follows: enter image description here

All images have a panel behind them that is 4px lager (2xp all around the picture box). The images are named x1, x2, xn.. respectively. The panels behind the images are named y1, y2, yn.. respectively.

I want this to happen smoothly when one of the 5 PictureBoxes is clicked:

  • Before clicked, all pictureboxes are 48x48 and respective panels 52x52
  • When one gets clicked, resize the picturebox to 62x62 and the respective panels to 66x66
  • When one gets clicked, the image must hold the same center as previously
  • When one gets clicked, all other images turn back 'small'
  • when a 'enlarged' image gets clicked, nothing happens

What I made

When form is created, two lists are initialised:

List<PictureBox> views = new List<PictureBox> { x1, x2, x3, x4, x5 }; and List<Panel> backviews = new List<Panel> { y1, y2, y3, y4, y5 };

and PictureBox current which is the name of the current picturebox (x1, x2, etc.)

All PictureBoxes in views have a click event that points to x_Click

private void x_Click(object sender, EventArgs e)
        {
            PictureBox x = sender as PictureBox;

            //Only do something if clicked PictureBox is not large
            if (x.Size != new Size(62, 62))
            {
                // This keeps the PictureBox center constant
                x.Location = new Point(x.Left - 7, x.Top - 7);
                // Resize picturebox
                x.Size = new Size(62, 62);
                // Get the panel corresponding to the PictureBox
                Control y = backviews[int.Parse(x.Name.Replace("x", "")) - 1];
                // This keeps the Panel center constant
                y.Location = new Point(y.Left - 7, y.Top - 7);
                // Resize Panel
                y.Size = new Size(66, 66);

                // Find the current enlarged PictureBox and reverse what is done above
                Control z = views[int.Parse(current.Replace("x", "")) - 1];
                z.Location = new Point(z.Left + 7, z.Top + 7);
                z.Size = new Size(48, 48);
                Control w = backviews[int.Parse(z.Name.Replace("x", "")) - 1];
                w.Location = new Point(w.Left + 7, w.Top + 7);
                w.Size = new Size(52, 52);

                // Set the currently enlarged PictureBox
                current = x.Name;
            }

        }

This works, but not smoothly; I can visually intercept the Panel being enlarged after the PictureBox is being enlarged. I suspect most time is taken by finding the Panels corresponding to the PictureBoxes. Is there any way I can speed this up?

\$\endgroup\$
3
\$\begingroup\$

There are a few things that you should refactor to be able to optimize your code better. Currently everthing sits inside the Click event handler and it's too much to handle there.

You are not able to focus on particular task like that. You should separate the operations and create new methods and properties for them. Here's how you could start. It's not 100% perfect (like the hard coded picture box BackColor) yet but it should give you an idea how to start refactoring your current solution.


Create a new panel control with a picture box inside (you can create a property for it so that you can set the image later). In this control you can implement all the logic that should handle the resize.

Give it a few properties that you can set and calculate the size and location values dynamiclly and internally. Use a flag (like IsEnlarged) to track whether a panel is in its normal or enlarged size rather than hard coded sizes. Here you can handle the Click event and take proper actions to modify the panel and the picture box.

class ResizablePanel : Panel
{
    private bool _isEnlarged;

    public ResizablePanel()
    {
        Controls.Add(new PictureBox
        {
            BackColor = Color.PaleGoldenrod
        });

        Click += PanelOrPicktureBox_Click;
        Controls[0].Click += PanelOrPicktureBox_Click;
    }

    public bool IsEnlarged
    {
        get { return _isEnlarged; }
        set
        {
            if (value == _isEnlarged)
            {
                return;
            }

            _isEnlarged = value;
            Size = _isEnlarged ? EnlargedSize : NormalSize;
            ResizePictureBox();
            TranslatePanel();

            if (IsEnlarged)
            {
                Enlarged?.Invoke(this, EventArgs.Empty);
            }
        }
    }

    public Size NormalSize { get; set; }

    public Size EnlargedSize { get; set; }

    public Size BorderSize { get; set; }

    public void Initialize()
    {
        Size = _isEnlarged ? EnlargedSize : NormalSize;
        ResizePictureBox();
        TranslatePanel();
    }

    private void ResizePictureBox()
    {
        var pictureBox = Controls[0] as PictureBox;

        pictureBox.Location = new Point
        {
            X = BorderSize.Width,
            Y = BorderSize.Height
        };

        // multiply by 2 becasue border size specifies only its width and we
        // need to two of them, one for each side
        pictureBox.Size = new Size
        {
            Width = Size.Width - (BorderSize.Width * 2),
            Height = Size.Height - (BorderSize.Height * 2)
        };
    }

    private void TranslatePanel()
    {
        if (IsEnlarged)
        {
            Location = new Point
            {
                X = Location.X - ((EnlargedSize.Width - NormalSize.Width) / 2),
                Y = Location.Y - ((EnlargedSize.Height - NormalSize.Height) / 2),
            };
        }
        else
        {
            Location = new Point
            {
                X = Location.X + ((EnlargedSize.Width - NormalSize.Width) / 2),
                Y = Location.Y + ((EnlargedSize.Height - NormalSize.Height) / 2),
            };
        }
    }

    private void PanelOrPicktureBox_Click(object sender, EventArgs e)
    {
        IsEnlarged = !IsEnlarged;
    }
}

You also can create your panels dinamiclly and give them some initial values.

private void CreatePictureBoxes()
{
    var pictureBoxCount = 4;

    var panelNormalSize = new Size(52, 52);
    var panelEnlargedSize = new Size(66, 66);
    var borderSize = new Size(5, 5);

    for (var i = 0; i < pictureBoxCount; i++)
    {
        var panel = new ResizablePanel
        {
            NormalSize = panelNormalSize,
            EnlargedSize = panelEnlargedSize,
            BorderSize = borderSize,
            IsEnlarged = false,
            // 3 is just a random border width multiplier to create gaps between the controls
            Location = new Point
            {
                X = panelNormalSize.Width * i + borderSize.Width * 3 * (i + 1),
                Y = borderSize.Height * 3
            },
            BackColor = Color.DarkMagenta,
        };
        panel.Initialize();

        Controls.Add(panel);
    }
}

Initialize the panels:

public Form1()
{
    InitializeComponent();
    CreatePictureBoxes();
}

And this is how it looks like:

PictureBoxes


When one gets clicked, all other images turn back 'small'

Add an event to your new panel and raise it when it gets enlarged. You can then handle this event in the main form and set all other panels to IsEnlarged = false

public event EventHandler Enlarged;

public bool IsEnlarged
{
    get { return _isEnlarged; }
    set
    {
        // prevents multiple changes
        if (value == _isEnlarged)
        {
            return;
        }

        _isEnlarged = value;
        Size = _isEnlarged ? EnlargedSize : NormalSize;
        ResizePictureBox();
        TranslatePanel();

        // raise the event (using C# 6)
        if (IsEnlarged)
        {
            Enlarged?.Invoke(this, EventArgs.Empty);
        }
    }
}

Subscribe to the event during the initialization:

panel.Enlarged += Panel_Enlarged;

Handle the event:

private void Panel_Enlarged(object sender, EventArgs e)
{
    var otherPanels = Controls.OfType<ResizablePanel>().Where(rp => rp != sender && rp.IsEnlarged);
    foreach (var otherPanel in otherPanels)
    {
        otherPanel.IsEnlarged = false;
    }
}

when a 'enlarged' image gets clicked, nothing happens

Just modify the PanelOrPictureBoxClick event handler like this:

IsEnlarged = true;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.