Resolved Identify which control in an array of controls has event.

MPIon

Well-known member
Joined
Jun 13, 2020
Messages
73
Location
England
Programming Experience
10+
I have several Media Player Controls on my form - created in code and need to identify which control has a state change.
The code is :
C#:
        static readonly AxWindowsMediaPlayer[] mediaPlayerBoxes = new AxWindowsMediaPlayer[16];
Then for each control, I add an event handler :-
C#:
mediaPlayerBoxes[i].StatusChange += MediaPlayer_StateChange;
Finally the event handler is :-
C#:
        private    void MediaPlayer_StateChange(object sender, EventArgs e)
        {
            // Get a local reference to the box that was clicked
            AxWindowsMediaPlayer player = sender as AxWindowsMediaPlayer;
            player.Ctlcontrols.pause();
        }
This does work and the video playing is paused, but I need to identify which of the Media Player controls caused the event, i.e. what is the Index into mediaPlayerBoxes.
Can't figure out how to get this.
 
The player has the reference of the control. If you iterate over your mediaPlayerBoxes array, it should also have the same control reference.
 
I didn't get it; you already know which control caused the event

I need to identify which of the Media Player controls caused the event

sender caused it

i.e. what is the Index into mediaPlayerBoxes.

To what end? Say it was mediaPlayerBoxes[4] that caused it, and you have some code that gives you the 4, what will you then do with the 4? If you index mediaPlayerBoxes with it you simply get back to something you already know (sender)

Tell us what else you're planning to do with the index; that's the interesting part that paves the way to working out what code you should actually write
 
If I had to guess, the user is keeping one (or more) parallel data structure(s) that correspond to the indices of the array. Probably holds what the playlist is for that specific player. Our OP probably doesn't know about the Tag property that is on every WinForms control which can be used to hold anything.


The naive approach would be to just store the index into the Tag. The more experienced developer would just put the reference to the parallel data structure element directly in the Tag.
 
I didn't get it; you already know which control caused the event



sender caused it



To what end? Say it was mediaPlayerBoxes[4] that caused it, and you have some code that gives you the 4, what will you then do with the 4? If you index mediaPlayerBoxes with it you simply get back to something you already know (sender)

Tell us what else you're planning to do with the index; that's the interesting part that paves the way to working out what code you should actually write

Actually, I don't know which control caused the event. My media player could be any of the 16 boxes on the screen, depending on which directory I am looking at - there might be 6 pictures and 2 videos in my directory, so 6 of the boxes would show pictures and 2 would show videos (I actually have 16 Picture boxes and 16 Media Player boxes - made visible according to the file type), but I don't know what order they would be in. The media event is triggered by a change state, but it could come from any of the boxes that has a media player in it.
The reason I have to go through this convoluted process of creating 16 event handlers for change of state, is because I want to open the Media Player with the video paused. I tried starting with CtlControls.paused(), but the Media Player still opens with the video playing. The only way I could get the player to be paused was with the event handler - which does work successfully.
I do need to know the index of the box though, so I can suppress the pause function when I genuinely start the video from the screen.
 
If I had to guess, the user is keeping one (or more) parallel data structure(s) that correspond to the indices of the array. Probably holds what the playlist is for that specific player. Our OP probably doesn't know about the Tag property that is on every WinForms control which can be used to hold anything.


The naive approach would be to just store the index into the Tag. The more experienced developer would just put the reference to the parallel data structure element directly in the Tag.

Thanks for the reply, but I am not doing anything as complex as a playlist. I just show the video for a specific wmv file in the Media Player box. See answer to cjard for more explanation of what I am doing.
 
The player has the reference of the control. If you iterate over your mediaPlayerBoxes array, it should also have the same control reference.

Thanks for the reply. I will investigate this, but not really sure where the reference is to be found. When I look at sender, it just says AxWMPlib.AxWindowsMediaPlayer
and player does not have a reference property as far as I can see - must be one of the other properties - handle maybe? I will investigate.
 
Thanks for the reply. I will investigate this, but not really sure where the reference is to be found. When I look at sender, it just says AxWMPlib.AxWindowsMediaPlayer
and player does not have a reference property as far as I can see - must be one of the other properties - handle maybe? I will investigate.

OK, I have managed to isolate the relevant box using the handle, and then try to pause the media player.
C#:
        // Event handler for all Media Player boxes to set to paused when first loaded.
        private    void MediaPlayer_StateChange(object sender, EventArgs e)
        {
            // Get a local reference to the Media Player box.
            AxWindowsMediaPlayer player = sender as AxWindowsMediaPlayer;

            // Find which box caused the event by comparing the handles.
            for (int boxNo = 0; boxNo < 16; boxNo++)
            {
                if (mediaPlayerBoxes[boxNo].Handle == player.Handle)
                {
                    if (mediaPlayerNew[boxNo])
                        mediaPlayerBoxes[boxNo].Ctlcontrols.pause();
                    mediaPlayerNew[boxNo] = false;
                    break;
                }
            }
        }

(mediaPlayerNew is just an array of booleans, all set to true when new items are loaded).

This does work and the line containing the pause command get reached, but unfortunately it does not pause the video - which is the problem I had in the first place.
If I move the pause line to immediately after the AxWindowsMediaPlayer player = sender as AxWindowsMediaPlayer; line, it does pause the video - but that is no good as it means every time I manually play the video it will trigger the change of state and get paused.

I am guessing it is something to do with timing.
 
This:
C#:
if (mediaPlayerBoxes[boxNo].Handle == player.Handle)
could have simply been:
C#:
if (mediaPlayerBoxes[boxNo] == player)

Thereby making this:
C#:
mediaPlayerBoxes[boxNo].Ctlcontrols.pause();
simply
C#:
player.Ctlcontrols.pause();
because mediaPlayerBoxes[boxNo] == player condition being true.

This is what I was talking about the reference to the control in sender being the same as the references you stored in the mediaPlayerBoxes array.
 
And this:
C#:
if (mediaPlayerNew[boxNo])
    mediaPlayerBoxes[boxNo].Ctlcontrols.pause();
mediaPlayerNew[boxNo] = false;
is exactly what I was talking about parallel data structures. You have an array of mediaPlayerBoxes whose indexes correspond with the array mediaPlayerNew.

Maintaining parallel data structures is usually a bad idea. Keep related information together. The only time you keep related information separate is when you are trying to optimize for memory usage where the other related information is not used as often and you are doing multiple queries through the data structure where you want to maximize as much of the data structure fitting with in the very fast CPU onboard memory caches instead of having to hit the slow main memory or super slow disk or database.

And so borrowing the code from your other thread:
C#:
static readonly AxWindowsMediaPlayer[] player = new AxWindowsMediaPlayer[4];
static readonly bool[] isNew = new bool[4];

for (int i = 0; i < 4; i++)
{
    player[i] = new AxWindowsMediaPlayer
    {
        Location = new System.Drawing.Point(250 + 320 * i, 50)
        Size = new System.Drawing.Size(300, 200);
        Visible = true;
        Enabled = true;
    };
    Controls.Add(player[i]);
    player[i].BringToFront();
    
    isNew[i] = true;
}
:
var thisPlayer = (AxWindowsMediaPlayer) sender;
for(int i = 0; i < boxes.Length; i++)
{
    if (player[i] == thisPlayer)
    {
        if (isNew[boxNo])
            boxes[boxNo].Ctlcontrols.pause();
        isNew[boxNo] = false;
    }
}

could be changed to something like:
C#:
for (int i = 0; i < 4; i++)
{
    var player = new AxWindowsMediaPlayer
    {
        Location = new System.Drawing.Point(250 + 320 * i, 50)
        Size = new System.Drawing.Size(300, 200);
        Visible = true;
        Enabled = true;
        Tag = true;    // no more isNew[] parallel data structure
    };
    Controls.Add(player);
    player.BringToFront();
}
:
var thisPlayer = (AxWindowsMediaPlayer) sender;
var isNew = (bool) thisPlayer.Tag;
if (isNew)
{
   thisPlayer.Ctlcontrols.pause();
    thisPlayer.Tag = false;
}
 
Last edited:
I don't know which control caused the event.

sender is the control that causes the event. It's a rule of windows forms - the event handler shall be an invokable member with a first argument that is the control that raised the event

I am saying "Getting the index of the media player in the media players array is pointless because the only thing you can do with it on the media players array is get the media player again, but you already know the media player"

You must be planning on doing something else with the index, not related to the media players array. Perhaps, for example, you're then planning to use it to index the picture boxes array, because you tie media players and pictureboxes together for some purpose and have made the design decision that media player X is associated with picture box X (I.e. it is not a coincidence that you have 16 of each)

What we are saying is there are better, cleaner, easier ways to tie things together than "this media player at index 4 and that picturebox at index 4, serve a related purpose"

At its most simplistic, for example, a windows control has a Tag property that can store anything. The fourth media player's Tag can reference the fourth picturebox so you could say:

C#:
    var mp = sender as MediaPlayer;
    var pb = mp.Tag as PictureBox;

You set the Tag when you're loading the controls; the moment when you know both controls, probably some loop in the constructor of the form that makes a new MP, makes a new PB, adds them to the controls collection, binds their events etc.. that's the moment you do myNewMp.Tag = myNewPicturebox to form the association, so you can later retrieve them



Rather than

C#:
    var x = 0;
    while(!object.ReferenceEquals(sender, mediaplayers[x])) x++);
    var pb = pictureboxes[x];
    var mp = sender as MediaPlayer;

It'll work, but it's not very clean. If there are 7 different things you want to track in relation to a media player, instead of having 7 arrays of different types, make a class with 7 properties and store an instance of it in the Tag. This is how we work in C#. If we want to store information about people we don't do it like this:

C#:
  var names = new string[10];
  var ages = new int[10];
  var birthdays = new DateTime[10];
 
  //John, 23, born 2000
  names[0] = "John";
  ages[0] = 23;
  birthdays[0] = new(2000,1,1);

We do this:

C#:
class Person{ //keep related info together
  public string Name{get;set;}
  public int Age{get;set;}
  public DateTime{get;set;}
}
...

var people = new Person[10];
people[0] = new(){ Name = "John", Age = "23", Birthday = new(2000,1,1) };

(Yes and then we would make Age a readonly property that calculates the years from the birthday.. it's an example)
 
Last edited:
And this:
C#:
if (mediaPlayerNew[boxNo])
    mediaPlayerBoxes[boxNo].Ctlcontrols.pause();
mediaPlayerNew[boxNo] = false;
is exactly what I was talking about parallel data structures. You have an array of mediaPlayerBoxes whose indexes correspond with the array mediaPlayerNew.

Maintaining parallel data structures is usually a bad idea. Keep related information together. The only time you keep related information separate is when you are trying to optimize for memory usage where the other related information is not used as often and you are doing multiple queries through the data structure where you want to maximize as much of the data structure fitting with in the very fast CPU onboard memory caches instead of having to hit the slow main memory or super slow disk or database.

And so borrowing the code from your other thread:
C#:
static readonly AxWindowsMediaPlayer[] player = new AxWindowsMediaPlayer[4];
static readonly bool[] isNew = new bool[4];

for (int i = 0; i < 4; i++)
{
    player[i] = new AxWindowsMediaPlayer
    {
        Location = new System.Drawing.Point(250 + 320 * i, 50)
        Size = new System.Drawing.Size(300, 200);
        Visible = true;
        Enabled = true;
    };
    Controls.Add(player[i]);
    player[i].BringToFront();
   
    isNew[i] = true;
}
:
var thisPlayer = (AxWindowsMediaPlayer) sender;
for(int i = 0; i < boxes.Length; i++)
{
    if (player[i] == thisPlayer)
    {
        if (isNew[boxNo])
            boxes[boxNo].Ctlcontrols.pause();
        isNew[boxNo] = false;
    }
}

could be changed to something like:
C#:
for (int i = 0; i < 4; i++)
{
    var player = new AxWindowsMediaPlayer
    {
        Location = new System.Drawing.Point(250 + 320 * i, 50)
        Size = new System.Drawing.Size(300, 200);
        Visible = true;
        Enabled = true;
        Tag = true;    // no more isNew[] parallel data structure
    };
    Controls.Add(player);
    player.BringToFront();
}
:
var thisPlayer = (AxWindowsMediaPlayer) sender;
var isNew = (bool) thisPlayer.Tag;
if (isNew)
{
   thisPlayer.Ctlcontrols.pause();
    thisPlayer.Tag = false;
}

Thanks for all the replies and help with this project. I have learnt much.
I have sort of got this to work with one or two Media Player boxes on my form, but when there are 5 or 6 it is painfully slow to load and soon crashes with memory access violation errors.

So, I am having a rethink on the design of my project and will probably revert to just having one Media Player on the form, with a play list to show the possible videos available.

Since the original question on the post has been solved, I will mark the thread as closed.
 
Indeed, if one video (only) plays at a time and the rest are just picture boxes showing eg the first frame then it would be better perhaps to just move a single media player around to give the impression of there being multiple
 
Indeed, if one video (only) plays at a time and the rest are just picture boxes showing eg the first frame then it would be better perhaps to just move a single media player around to give the impression of there being multiple

Yes, I originally considered just showing thumbnails for the Video files (and even the images), which when clicked, would open up a full size media player (bit like Windows Photo Viewer, which is not being provided in Windows 11 as I understand). This was my ideal approach, but could not find a quick way of getting the thumbnails for the video files. There did seem to be some propriety software to do this, but don't really want to go down this route.

It seems a shame that since thumbnails already exist for all the images and video files within the thumbnail cache, it is not possible to access them from Visual Studio - or is it? It would make the program much faster, instead of having to re-render and resize all the images from the jpeg.
 
Back
Top Bottom