1

In my working C# winforms application, I’m trying to achieve the draggable control behavior where when the user drags a MidiNote control (it’s just called MidiNote rn, gonna be for different controls later) between 2 others, all of the controls to the right of it will “scoot” or slide to the right in order to make room for it (almost like inserting magnets between one another). Then conversely, if you drag a control out to say the very left end, the gap left by the moved control will collapse.
I know this control behavior has been done time and time again for other application (it’s used in Apple’s Final Cut Pro magnetic timeline for instance), but I cannot seem to wrap my head around doing it myself (at least not beyond the point I’m at now)!
Here’s an example of someone achieving this using JQuery, but I don’t think it’s logic transfers too well to c# winform controls. So far, I’m able to with 3 controls drag them between one another, but the moment I do with 4+, the logic breaks (do I need to use recursion somewhere)? Any help is greatly appreciated. I’d be happy it we can together boil this behavior (and similar ones) down to a generic algorithm for all to use. If you need more code or context, lmk.
Thanks!

private void PianoRoll_MouseMove(object? sender, MouseEventArgs e)
{
    const int Y_POS = 200; //arbitrary
    if (_bMouseDownOnNote)
    {
        foreach (MidiNote n in this.Controls.OfType<MidiNote>())
        {
            if (n != _noteToMove)
            {
                if (n.Bounds.Contains(new Point(e.X, Y_POS)))
                {
                    int eX = e.X; int nRight = n.Right; int nWidth = n.Width;
                    if (eX > (nRight - (nWidth / 2)))
                    {
                        Point p = new Point(nRight, Y_POS);
                        _noteToMove.Location = p;
                        //k, we've moved the note, now loop all the notes to the right of this and scoot them accordingly
                        foreach (MidiNote n2 in this.Controls.OfType<MidiNote>())
                        {
                            if (n2 != _noteToMove && n2 != n)
                            {
                                if (n2.Bounds.Contains(new Point(x: nRight, y: Y_POS)))
                                {
                                    int n2Left = n2.Left;
                                    Point p2 = new Point(n2Left + nWidth, Y_POS);
                                    n2.Location = p2;
                                    nRight = n2Left + nWidth;
                                }
                            }
                        }
                    }
                    else if (eX < (nRight - (nWidth / 2)))
                    {
                        int nLeft = n.Left;
                        Point p = new Point(nLeft - nWidth, Y_POS);
                        _noteToMove.Location = p;
                        //haven't handled for multiple notes here yet :/
                    }
                }
            }
        }
    }
}
Finch
  • 65
  • 4
  • Presumably it would be better to not use the Controls property of the container control but use your own Enumerable of MidiNote yourself. If i understand your problem you actually need an ordered set of MidiNote that you want to move parts of. So if you drop one somewhere and you know where you can then move all below or beside of the dropped one. For that "all" to work you need some kind of ordering of your MidiNote and the Controls List just don't have this. – Ralf Apr 17 '23 at 15:42
  • Besides that i you want a nice UI WInforms might be the wrong candidate. If you want here also some kid of animation when moving things like in your linked example that gets hard in Winforms. Getting it flickerfree even harder. Not impossible but hard. – Ralf Apr 17 '23 at 15:45
  • @Ralf Thank you for the response. I understand winforms is probably not the best candidate for this (it's just what I know best ATM). Alternatively, I've thought for a super slick UI, I could setup a physics engine in C# or C++ with Direct2D and make physical properties of these controls literally behave like movable magnets. You have an opinion on either of these options over another? – Finch Apr 17 '23 at 15:53
  • 1
    Sounds weird and complicated if you then want to embed that again Winforms. If you want a bit of animation in a beyond that just a typical desktop app then simply use WPF or MAUI (just like something XAML based) that has that easily build in. – Ralf Apr 17 '23 at 16:03
  • @Ralf I don't have much experience using IEnumerable, but if I were to go that route per your first suggestion, should I maybe have List member in the container control, and then a property called MidiNotes whos get returns IEnumerable? – Finch Apr 17 '23 at 17:06
  • You need to [create a custom ControlDesigner class](https://stackoverflow.com/a/2863807/380384) in order to implement the functionality you need in the designer. Read [more examples](https://www.cyotek.com/blog/aligning-windows-forms-custom-controls-to-text-baselines-using-csharp) on how to do this. – John Alexiou Apr 18 '23 at 00:26

0 Answers0