0

I am getting a run-time error which tells me that it is unable to cast an object of type PictureBox to type MusicNote (MusicNote inherits from PictureBox.)

private void Note_MouseDown(object sender, MouseEventArgs e)
    {
        //try
        //{
            foreach (MusicNote mn in panel2.Controls) //this is where the error occurs
            {
                if (sender == mn)
                {
                    if (e.Button == MouseButtons.Right)
                    {
                        count = 0;
                        timer1.Start();
                        sp.SoundLocation = MusicNote_path + mn.note + ".wav";
                        sp.Play();
                    }
                if (e.Button == MouseButtons.Left)
                {
                    dragging = true;
                    mn.BackColor = Color.HotPink;


                }

Below is a part of the MusicNote class, including the constructor, to show what happens each time a MusicNote is constructed:

class MusicNote : PictureBox
{
    public int notepitch;
    public int noteduration;
    public String noteshape;
    public String note;
    enum Accid { sharp, flat, sole };

    public static String NoteImage_path = Environment.CurrentDirectory + @"\Notes-Images\\";
    public static int xLoc = 30;
    public int yLoc = 0;

    System.Timers.Timer timer1 = new System.Timers.Timer();

    public MusicNote(int iNotepitch, int iDuration, String iBnoteShape, String iNote)
    {
        notepitch = iNotepitch;
        noteduration = iDuration;
        noteshape = iBnoteShape;
        note = iNote;


        ImageLocation =  NoteImage_path + noteshape + ".bmp";
        BackColor = Color.Transparent;
        ClientSize = new Size(35, 35);
        //BringToFront();


        Location = new Point(xLoc, getyLoc(iNote));
        xLoc += 37;
    }

This is how the panel is being populated:

MusicNote mn = new MusicNote(mk.getMusicNote(), duration, bNoteShape, mk.getNote());
mn.MouseDown += new MouseEventHandler(Note_MouseDown);
mn.MouseUp += new MouseEventHandler(Note_MouseUp);
mn.MouseClick += new MouseEventHandler(Note_MouseClick);

panel2.Controls.Add(mn); //adding MusicNote component to MusicStaff (panel2) collection

Edit: You can view the error here.

Any help is appreciated, thanks.

David Tansey
  • 5,813
  • 4
  • 35
  • 51
Luke Xuereb
  • 73
  • 12

5 Answers5

2

To loop over MusicNote instances only, you can use the OfType extension method from LINQ:

foreach (MusicNote mn in panel2.Controls.OfType<MusicNote>()) {
   // do stuff
} 
adjan
  • 13,371
  • 2
  • 31
  • 48
1

When your program gets control in an event handler like Note_MouseDown you receive a reference to the control that 'took' the event in the UI (object sender).

Try casting the sender to MusicNote using the as clause. If the cast is not possible (because sender is something other than MusicNote) the use of the as clause does not throw an exception -- instead it just hands you a NULL reference, which you can test for.

Try something like this:

private void Note_MouseDown(object sender, MouseEventArgs e)
{
    var mn = sender as MusicNote;
    if (mn != null) 
    {
        if (e.Button == MouseButtons.Right)
        {
            count = 0;
            mn.timer1.Start();
            sp.SoundLocation = MusicNote_path + mn.note + ".wav";
            sp.Play();
        }
        if (e.Button == MouseButtons.Left)
        {
            dragging = true;
            mn.BackColor = Color.HotPink;
        }
    }
}

You really don't need the foreach.

David Tansey
  • 5,813
  • 4
  • 35
  • 51
  • I'd probably just use a cast - if the event is only meant to be subscribed to the `MusicNote`, then if the cast fails, that indicates a bug and an exception is entirely reasonable at that point. But I agree that there's no need to use a foreach loop. (The OP is already using `sender` in the comparison, so it's not clear why they're looping at all.) – Jon Skeet Jan 05 '19 at 19:20
0

This error occures every time when the foreach (MusicNote mn in panel2.Controls) loop find anything else then a MusicNote.

You can avoid that by looping all Controls like foreach (Control cntrl in panel2.Controls)

Example Code:

foreach (Control cntrl in panel2.Controls) 
            {
                if(cntrl is MusicNote)
                {
                     //do something with the MusicNote Control
                }
            }
0

There are many ways to fix your issue but let's just do a walkthrough of your code so you can see how clumsy it is. In plain English, you are doing this:

The mouse button is pressed down on a control. Which control? Well the control in sender. You loop through all the controls in panel2 to see if one of them is the sender and then you do some work.

But why would you loop through all the controls in panel2? When you created the MusicNote control, you created this event handler specifically for that control to inform you when the mouse is down on it. And now the control is raising an event and saying "Hey, the mouse button is down and it is down on me!" You see, even though panel2.Controls.OfType<MusicNote>() will fix your issue, but why would you do that? It seems like an XY problem.

The only reason you would do what you are doing is if you created the control, subscribed to the MouseDown event, then programmatically moved the control from one panel to another and you only want to do some work if the control happens to be in panel2 when the mouse is down. I doubt you moved it around; and even if you did, there are better ways to handle that scenario.

Proper Solution

You do not need a loop and all you need is this:

private void Note_MouseDown(object sender, MouseEventArgs e)
{
    // If neither right nor left is down, return immediately because nothing needs 
    // to be done.
    if (!(e.Button == MouseButtons.Right || e.Button == MouseButtons.Left))
    {
        return;
    }

    // This should not fail, if it does, then ask yourself why have you created 
    // this handler for things which are not MusicNote.
    MusicNote mn = (MusicNote)sender;

    // Do some work below
    if (e.Button == MouseButtons.Right)
    {
        count = 0;
        timer1.Start();
        sp.SoundLocation = MusicNote_path + mn.note + ".wav";
        sp.Play();
    }

    if (e.Button == MouseButtons.Left)
    {
        dragging = true;
        mn.BackColor = Color.HotPink;
    }
}
CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
-1

This error occurs when panel2.Controls contains Controls not only of the type MusicNote or PictureBox. From your data it is not clear what is the type of all panel2.Controls, should be solely Collection or List of PictureBoxes. If panel2.Controls contains any Control of a type different from Picturebox or MusicNote (e.g. TextBox, etc.), you get the error. If the types of all panel2.Controls are correct, then most likely the panel2 is not fully loaded when the error occurs. You may try:

foreach (var mn in panel2.Controls)
if (sender == (MusicNote)mn)
Codigo
  • 366
  • 2
  • 9