[C#] Movable Controls

Witam

Robię sobie coś na zasadzie Movable and Resizable Controls. Mam przycisk którym mogę ruszać jednak chciałem zrobić tak:

MouseDown:

MouseClicked = true;

RememberEventHandler = (sender as Button).Click;

(sender as Button).Click.Clear()

Mouse Up:

MouseClicked = false;

(sender as Button).Clik +=RememberEventHandler;

if(!LocationChanged)

(sender as Button).PerformClick();

Chodzi o to że jeśli ruszę przyciskiem to nie zostanie wywołane zdarzenie Click. Jednak jeśli ktoś tylko na niego kliknie to zostanie wywołane zdarzenie Click. Aktualnie mogę to zrobić wpisując ręcznie w kod wszystko ale chciałbym mieć uniwersalne 3 krótkie funkcje pod kilka przycisków.

Jeśli chodzi o przemieszczanie kontrolek, to możesz spróbować takiego kodu:

private void button_MouseUp(object sender, MouseEventArgs e)

{

    Button button = (Button)sender;


    int x = button.Location.X + e.X;

    int y = button.Location.Y + e.Y;


    bool moved = !((x >= button.Location.X && x <= button.Location.X + button.Width)

        && (y >= button.Location.Y && y <= button.Location.Y + button.Height));

    bool inParentControl = (x >= 0 && x <= button.Parent.Width)

        && (y >= 0 && y <= button.Parent.Height);


    if (moved && inParentControl)

        button.Location = new Point(x, y);

}


private void button_Click(object sender, EventArgs e)

{

    Button button = (Button)sender;

    MessageBox.Show(button.Name);

}

Pod każdy button na formie podepnij te dwie procedury i zobacz co się stanie.

PS Temat jest troszkę źle nazwany. Już lepiej by było [C#] Movable Controls. Aktualnie temat nie ma nic wspólnego z tym co faktycznie próbujesz osiągnąć.

Tyle że z tym kodem co Ty masz nie widać przemieszczającego się buttona jeśli dobrze interpretuje kod. Klikam, przeciągam i zwalniam klawisz myszy i dopiero pojawia się w nowym miejscu. Ja chcę uzyskać efekt widocznego buttona podczas przeciągania. Dodatkowo Twój kod z tego co widzę również nie jest odporny na kliknięcie. Po przeciągnięciu buttona zostanie on kliknięty jeśli próbujemy go przeciągnąć o małą odległość(czyt. kursor nadal będzie w obszarze buttona) i nie zostanie przesunięty. I tak w ogóle dodałbym do Twojego kodu jeszcze połowę rozmiaru buttona bo ja raczej jak przeciągam button to miejsce w którym go upuszczam jest środkiem buttona.

if (moved && inParentControl)

                button.Location = new Point(x-button.Size.Width/2, y-button.Size.Height/2);

Ja mam swój kod i działa ale dla 3 przycisków muszę dawać switch czy if aby odjąć EventHandler od odpowiedniego przycisku a następnie go dodać. Ja chcę uniwersalną funkcję gdzie zapamiętam co było przypisane do sendera pod zdarzenie Click, wyzeruje to zdarzenie a następnie przywrócę funkcję obsługującą to zdarzenie. :edit: Chyba, że ktoś zna inny sposób zablokowania kliknięcia. Jak wyciągnąć z buttona adres funkcji odpowiadającej za kliknięcie… :edit: Zrobiłem to trochę w inny sposób mianowicie:

private void MouseDownMoveResizeEvent(object sender, MouseEventArgs e)

{

    mouse_clicked = true;

    BeforeLocation = (sender as Control).Location;

}


private void MouseUpMoveResizeEvent(object sender, MouseEventArgs e)

{

    mouse_clicked = false;

}


private void MouseMoveMoveEvent(object sender, MouseEventArgs e)

{

    if (mouse_clicked)

    {

        Control c = (Control)sender;

        c.Location = AfterLocation = new Point(c.Location.X + e.X - c.Size.Width/2, c.Location.Y + e.Y - c.Size.Height/2);

        if (BeforeLocation != AfterLocation)

        {

            moved = true;

        }

    }

}

A następnie w buttonach na samym początku mam:

if (moved)

{

    moved = false;

    return;

}

Ten kod daje mi efekt taki że przesuwam kontrolkę i widzę ją podczas przesuwania a button nie klika się. Początkowo umieściłem kod ze sprawdzaniem lokacji w zdarzeniu MouseUp ale zdarzenie Click występuje wcześniej niż mouseUp dlatego dałem w MouseMove bo mouseMove występuje najwcześniej.

Dokładnie tak się dzieje. Nie wspomniałeś o tym w pierwszym poście :stuck_out_tongue:

Ciekawe … korzystasz z .NET Framework 4? U mnie wywołanie zdarzenia Click występuje tylko i wyłącznie wtedy, gdy button nie zostanie przesunięty, tzn. gdy nie zmieni się wartość Location.

Jasne, możesz to zmieniać dowolnie. U mnie w kodzie miejsce puszczenia klawisza myszy jest jednocześnie miejscem lewego górnego rogu button-a. Jednak wydaje mi się, że najlepsze rozwiązanie będzie wtedy, gdy miejsce kliknięcia będzie adekwatne do miejsca upuszczenia. Nie będzie wówczas takich widocznych przeskoków kontrolki. Jakbyś chciał mieć to tak zrobione, to daj znać, wówczas wprowadzę poprawki w tym swoim kodzie.

PS Jeszcze jedno … może warto w aplikacji wprowadzić takie dwa tryby? Jeden tryb to będzie wtedy, gdy kontrolki będą interaktywne, czyli np. będą obsługiwać zdarzenia Click, a drugi to będzie tryb, w którym użytkownik będzie mógł modyfikować położenie kontrolek?

Kod jest w .Net 2.0 bo tak w pracy się bawiłem ale korzystam z VS 2010. A z tym przeskokiem to sobie poradzę. Edytowałem swój post wcześniejszy. Jak na piechotę to osiągnąłem.

Ps. Wszystko ok, ja po prostu dzisiaj tak na to wpadłem i się bawię. A co do trybu edycji że tak nazwę go to będę musiał utworzyć jakąś funkcję i w niej ręcznie dodawać zdarzenia bezpośrednio b.click+=new eventHandler(xxx); a drugą która będzie usuwać? Nie ma czegoś takiego button.reagujNaEventy = false;?

Jeszcze musisz uwzględnić sytuację, że ktoś wyjedzie myszką poza obszar kontrolki rodzica podczas zmiany rozmiaru lub przesuwania. Inaczej będą się działy cuda :stuck_out_tongue:

Jeśli chodzi o tryb edycji, to dokładnie o to mi chodziło. Nic mi nie wiadomo o tym, aby można było “dezaktywować” wybrany event. W każdym bądź razie … takie dwa tryby są używane np. w firefox-ie. Oczywiście to, czy powinieneś użyć takie dwa tryby zależy od specyfiki aplikacji, ale akurat w przeglądarce sprawdza się to świetnie.

Jeśli nie można deaktywować to po prostu trzeba zrobić jedną dużą funkcję gdzie dodaje wszystkie eventy a drugą odejmuję. A co do wyjechania poza obszar to fajnie, schowałem button w minusowym obszarze i go ni ma :stuck_out_tongue_winking_eye: Takie do robienia na złość dobre. Było i nie ma. Ale to ja będę chciał zrobić coś na wzór że kontrolka się zatrzyma przy krawędzi jeśli wyjedziemy kursorem poza obszar rodzica.

Właśnie coś takiego miałem na myśli.

Nie wiem co rozumiesz przez minusowy obszar. W każdym bądź razie współrzędna X i Y button-a (właściwość Location) musi się mieścić w <0; Size> kontrolki rodzica. Wówczas będzie OK.

Dobry pomysł.

Nie minusowy rozmiar tylko minusowe współrzędne. Przesunąłem button bardzo do góry tak że zniknął z obszaru formy.

No ale współrzędne nie mogą być minusowe :stuck_out_tongue: Wartości w Location masz minusowe?

EDIT: Faktycznie mogą być ujemne (właśnie sam sprawdziłem) :slight_smile:

Po przesunięciu buttona poza formę do góry w Location mam = {X = 84 Y = -210}

Tak, masz rację. Właśnie sprawdziłem (edytowałem powyższy post). Jak się wyjedzie w lewo lub do góry poza obszar kontrolki rodzica to odpowiednie współrzędne przyjmują wartości ujemne. Najlepiej jednak będzie sprawdzać nie to czy któraś współrzędna jest ujemna (bo to jest prawdziwe tylko dla dwóch przypadków), a w sposób jaki pisałem wyżej, czyli, że Location.X i Location.Y button-a musi się znaleźć w <0; Size.Width> oraz <0; Size.Height> kontrolki rodzica danego buttona.

if (x >= 0 && (x + control.Size.Width) <= control.Parent.Size.Width && y >= 0 && (y + control.Size.Height) <= control.Parent.Size.Height) {}

Ja wyliczam teraz nowy X i nowy Y. Następnie sprawdzam czy ten X lub Y nie jest poza obszarem formy. Przy Y muszę wziąć pod uwagę CaptionHeight i Border a przy X tylko Border i wszystko działa. Jeśli poniżej zera zwyczajnie przypisuje zero a jeśli wychodzi z drugiej strony to przypisuje wyliczoną lokację minus ramki. Później po pracy wrzucę mój gotowy kod do przesuwania kontrolki (w moim przypadku buttona) dla potomnych. matzu najwyżej dodasz coś od siebie jakbyś miał pomysł lepszego rozwiązania.

W porządku. Ja wcześniej nie pomyślałem o tym, że trzeba też uwzględnić Margin kontrolki i Padding kontenera. U siebie też to dodaj (chyba, że już to masz). Zobacz też jak zachowa się kontrolka, gdy umieścisz ją w np. panelu (czy tam jakimkolwiek kontenerze innym niż sama Form-a). Możesz też np. dodać możliwość przesuwania kontrolek przy użyciu klawiszy (lewo, góra, dół, prawo, lewo-góra, dół-prawo itp.).

I oczywiście najważniejsze, zapamiętanie wszystkich zmian :wink: ja niestety nie umiem korzystać z tych plików settings wbudowanych ponieważ zawsze zapisywał mi plik config gdzieś tam w użytkownikach a ja chciałbym żeby zapisywało w pliku obok aplikacji. Ale to jest na inny temat.

Edit:

Właśnie, miałem wrzucić kod. Z załączniku cały projekt a tutaj daje poszczególne funkcje.

Hmmm napisałem ze w załączniku a tu nie widzę opcji dodawania załączników.

Forma się skaluje, efekt jest jaki jest i można przesuwać obiekty.

Form1.cs

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;


using System.Text;

using System.Windows.Forms;


namespace ScalableForm

{

    public partial class Form1 : Form

    {

        Size Actual_size = new Size();

        Size Oryginal_Size = new Size();

        Point BeforeLocation = new Point();

        Point AfterLocation = new Point();

        bool mouse_clicked = false;

        bool moved = false;

        public Form1()

        {

            InitializeComponent();

        }


        private void Form1_Load(object sender, EventArgs e)

        {

            Oryginal_Size = this.Size;

            Actual_size = this.Size;

        }

        void resizeControls(Control.ControlCollection control)

        {

            double aspectY = (double)(this.Height) / Actual_size.Height;

            double aspectX = (double)(this.Width) / Actual_size.Width;

            foreach (Control c in control)

            {

                Font f = c.Font;

                c.Location = new Point((int)(aspectX * c.Location.X), (int)(aspectY * c.Location.Y));

                c.Size = new Size((int)(aspectX * c.Size.Width), (int)(aspectY * c.Size.Height));


                if (c.HasChildren)

                {

                    resizeControls(c.Controls);

                }

                c.Font = new Font(f.FontFamily, (float)(aspectY * f.Size), f.Style, f.Unit, f.GdiCharSet, f.GdiVerticalFont);

            }

        }

        private void Form1_Resize(object sender, EventArgs e)

        {

            resizeControls(this.Controls);

            Actual_size = this.Size;

        }


        private void button1_Click(object sender, EventArgs e)

        {

            if (moved)

            {

                moved = false;

                return;

            }

            MessageBox.Show("");

        }

        private void button_MouseUp(object sender, MouseEventArgs e)

        {

            Button button = (Button)sender;


            int x = button.Location.X + e.X;

            int y = button.Location.Y + e.Y;


            bool moved = !((x >= button.Location.X && x <= button.Location.X + button.Width)

                && (y >= button.Location.Y && y <= button.Location.Y + button.Height));

            bool inParentControl = (x >= 0 && x <= button.Parent.Width)

                && (y >= 0 && y <= button.Parent.Height);


            button.Enabled = false;

            if (moved && inParentControl)

                button.Location = new Point(x - button.Size.Width / 2, y - button.Size.Height / 2);

            button.Enabled = true;


        }


        private void button_Click(object sender, EventArgs e)

        {

            Button button = (Button)sender;

            MessageBox.Show(button.Name);

        }


        private void MouseDownMoveResizeEvent(object sender, MouseEventArgs e)

        {

            mouse_clicked = true;

            BeforeLocation = (sender as Control).Location;

        }


        private void MouseUpMoveResizeEvent(object sender, MouseEventArgs e)

        {

            mouse_clicked = false;

        }


        private void MouseMoveResizeEvent(object sender, MouseEventArgs e)

        {

            if (mouse_clicked)

            {

                Panel tmp_panel = new Panel();

                switch ((sender as PictureBox).Name)

                {

                    case "pictureBox1":

                        //tmp_panel = panel1;

                        break;

                    case "pictureBox2":

                        //tmp_panel = panel2;

                        break;

                }

                tmp_panel.Height = (sender as PictureBox).Top + e.Y;

                tmp_panel.Width = (sender as PictureBox).Left + e.X;

            }

        }

        private void MouseMoveMoveEvent(object sender, MouseEventArgs e)

        {

            if (mouse_clicked)

            {

                Control c = (Control)sender;

                Control p = (Control)c.Parent;

                int x = c.Location.X + e.X - c.Size.Width / 2;

                int y = c.Location.Y + e.Y - c.Size.Height / 2;


                int x_overide = p.ClientSize.Width - c.Size.Width;

                int y_overide = p.ClientSize.Height - c.Size.Height;


                if (x < 0)

                    x = 0;

                else if (x > x_overide)

                    x = x_overide;


                if (y < 0)

                    y = 0;

                else if (y > y_overide)

                    y = y_overide;


                c.Location = AfterLocation = new Point(x, y);

                if (BeforeLocation != AfterLocation)

                {

                    moved = true;

                }

            }

        }

    }

}

Form1.Designer.cs

namespace ScalableForm

{

    partial class Form1

    {

        /// 

        /// Required designer variable.

        /// 

        private System.ComponentModel.IContainer components = null;


        /// 

        /// Clean up any resources being used.

        /// 

        /// true if managed resources should be disposed; otherwise, false.

        protected override void Dispose(bool disposing)

        {

            if (disposing && (components != null))

            {

                components.Dispose();

            }

            base.Dispose(disposing);

        }


        #region Windows Form Designer generated code


        /// 

        /// Required method for Designer support - do not modify

        /// the contents of this method with the code editor.

        /// 

        private void InitializeComponent()

        {

            this.button1 = new System.Windows.Forms.Button();

            this.button2 = new System.Windows.Forms.Button();

            this.label1 = new System.Windows.Forms.Label();

            this.button3 = new System.Windows.Forms.Button();

            this.panel1 = new System.Windows.Forms.Panel();

            this.maskedTextBox1 = new System.Windows.Forms.MaskedTextBox();

            this.radioButton1 = new System.Windows.Forms.RadioButton();

            this.checkBox1 = new System.Windows.Forms.CheckBox();

            this.progressBar1 = new System.Windows.Forms.ProgressBar();

            this.comboBox1 = new System.Windows.Forms.ComboBox();

            this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();

            this.splitContainer1 = new System.Windows.Forms.SplitContainer();

            this.panel1.SuspendLayout();

            ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();

            this.splitContainer1.Panel1.SuspendLayout();

            this.splitContainer1.SuspendLayout();

            this.SuspendLayout();

            // 

            // button1

            // 

            this.button1.Location = new System.Drawing.Point(12, 12);

            this.button1.Name = "button1";

            this.button1.Size = new System.Drawing.Size(75, 23);

            this.button1.TabIndex = 0;

            this.button1.Text = "button1";

            this.button1.UseVisualStyleBackColor = true;

            this.button1.Click += new System.EventHandler(this.button1_Click);

            this.button1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.MouseDownMoveResizeEvent);

            this.button1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.MouseMoveMoveEvent);

            this.button1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.MouseUpMoveResizeEvent);

            // 

            // button2

            // 

            this.button2.Location = new System.Drawing.Point(12, 41);

            this.button2.Name = "button2";

            this.button2.Size = new System.Drawing.Size(75, 23);

            this.button2.TabIndex = 1;

            this.button2.Text = "button2";

            this.button2.UseVisualStyleBackColor = true;

            // 

            // label1

            // 

            this.label1.Location = new System.Drawing.Point(12, 87);

            this.label1.Name = "label1";

            this.label1.Size = new System.Drawing.Size(100, 23);

            this.label1.TabIndex = 2;

            this.label1.Text = "label1";

            // 

            // button3

            // 

            this.button3.Location = new System.Drawing.Point(78, 39);

            this.button3.Name = "button3";

            this.button3.Size = new System.Drawing.Size(75, 23);

            this.button3.TabIndex = 0;

            this.button3.Text = "button3";

            this.button3.UseVisualStyleBackColor = true;

            this.button3.Click += new System.EventHandler(this.button_Click);

            this.button3.MouseDown += new System.Windows.Forms.MouseEventHandler(this.MouseDownMoveResizeEvent);

            this.button3.MouseMove += new System.Windows.Forms.MouseEventHandler(this.MouseMoveMoveEvent);

            this.button3.MouseUp += new System.Windows.Forms.MouseEventHandler(this.MouseUpMoveResizeEvent);

            // 

            // panel1

            // 

            this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;

            this.panel1.Controls.Add(this.button3);

            this.panel1.Controls.Add(this.maskedTextBox1);

            this.panel1.Controls.Add(this.radioButton1);

            this.panel1.Location = new System.Drawing.Point(15, 146);

            this.panel1.Name = "panel1";

            this.panel1.Size = new System.Drawing.Size(200, 100);

            this.panel1.TabIndex = 3;

            this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.MouseDownMoveResizeEvent);

            this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.MouseMoveMoveEvent);

            this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.MouseUpMoveResizeEvent);

            // 

            // maskedTextBox1

            // 

            this.maskedTextBox1.Location = new System.Drawing.Point(15, 13);

            this.maskedTextBox1.Name = "maskedTextBox1";

            this.maskedTextBox1.Size = new System.Drawing.Size(100, 20);

            this.maskedTextBox1.TabIndex = 6;

            // 

            // radioButton1

            // 

            this.radioButton1.AutoSize = true;

            this.radioButton1.Location = new System.Drawing.Point(12, 68);

            this.radioButton1.Name = "radioButton1";

            this.radioButton1.Size = new System.Drawing.Size(85, 17);

            this.radioButton1.TabIndex = 5;

            this.radioButton1.TabStop = true;

            this.radioButton1.Text = "radioButton1";

            this.radioButton1.UseVisualStyleBackColor = true;

            // 

            // checkBox1

            // 

            this.checkBox1.AutoSize = true;

            this.checkBox1.Location = new System.Drawing.Point(150, 12);

            this.checkBox1.Name = "checkBox1";

            this.checkBox1.Size = new System.Drawing.Size(80, 17);

            this.checkBox1.TabIndex = 4;

            this.checkBox1.Text = "checkBox1";

            this.checkBox1.UseVisualStyleBackColor = true;

            // 

            // progressBar1

            // 

            this.progressBar1.Location = new System.Drawing.Point(93, 64);

            this.progressBar1.Name = "progressBar1";

            this.progressBar1.Size = new System.Drawing.Size(100, 23);

            this.progressBar1.TabIndex = 7;

            this.progressBar1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.MouseDownMoveResizeEvent);

            this.progressBar1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.MouseMoveMoveEvent);

            this.progressBar1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.MouseUpMoveResizeEvent);

            // 

            // comboBox1

            // 

            this.comboBox1.FormattingEnabled = true;

            this.comboBox1.Location = new System.Drawing.Point(72, 93);

            this.comboBox1.Name = "comboBox1";

            this.comboBox1.Size = new System.Drawing.Size(121, 21);

            this.comboBox1.TabIndex = 8;

            // 

            // numericUpDown1

            // 

            this.numericUpDown1.Location = new System.Drawing.Point(3, 38);

            this.numericUpDown1.Name = "numericUpDown1";

            this.numericUpDown1.Size = new System.Drawing.Size(120, 20);

            this.numericUpDown1.TabIndex = 9;

            // 

            // splitContainer1

            // 

            this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;

            this.splitContainer1.Location = new System.Drawing.Point(15, 252);

            this.splitContainer1.Name = "splitContainer1";

            // 

            // splitContainer1.Panel1

            // 

            this.splitContainer1.Panel1.Controls.Add(this.numericUpDown1);

            this.splitContainer1.Size = new System.Drawing.Size(255, 165);

            this.splitContainer1.SplitterDistance = 127;

            this.splitContainer1.TabIndex = 10;

            // 

            // Form1

            // 

            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

            this.ClientSize = new System.Drawing.Size(284, 444);

            this.Controls.Add(this.splitContainer1);

            this.Controls.Add(this.comboBox1);

            this.Controls.Add(this.progressBar1);

            this.Controls.Add(this.checkBox1);

            this.Controls.Add(this.panel1);

            this.Controls.Add(this.label1);

            this.Controls.Add(this.button2);

            this.Controls.Add(this.button1);

            this.Name = "Form1";

            this.Text = "Form1";

            this.Load += new System.EventHandler(this.Form1_Load);

            this.Resize += new System.EventHandler(this.Form1_Resize);

            this.panel1.ResumeLayout(false);

            this.panel1.PerformLayout();

            ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();

            this.splitContainer1.Panel1.ResumeLayout(false);

            this.splitContainer1.ResumeLayout(false);

            this.ResumeLayout(false);

            this.PerformLayout();


        }


        #endregion


        private System.Windows.Forms.Button button1;

        private System.Windows.Forms.Button button2;

        private System.Windows.Forms.Label label1;

        private System.Windows.Forms.Button button3;

        private System.Windows.Forms.Panel panel1;

        private System.Windows.Forms.MaskedTextBox maskedTextBox1;

        private System.Windows.Forms.RadioButton radioButton1;

        private System.Windows.Forms.CheckBox checkBox1;

        private System.Windows.Forms.ProgressBar progressBar1;

        private System.Windows.Forms.ComboBox comboBox1;

        private System.Windows.Forms.NumericUpDown numericUpDown1;

        private System.Windows.Forms.SplitContainer splitContainer1;


    }

}