Контрол на Winforms

Имам бутон, който използвам през цялото време като малък бутон за избор до комбинирано поле. Когато щракна върху бутона, отварям по-голям пълен списък. Тази страна на нещата работи добре и нямам проблем с това..

Проблемът ми е, когато някой ми каза, можеш ли да промениш тази грозна икона, която си избрал, с моята хубава икона.

Обърках се, имам стотици от тези бутони на много формуляри. Затова си помислих, че ще създам персонализирана контрола, наречена PickButton (която е стандартен бутон и куп набор от свойства по подразбиране) и вместо това ще ги пусна във формуляра навсякъде. В кода на персонализираната контрола PickButton зададох някои свойства и изображението на хубавата икона на клиентите.

Така че пускам PickButton от моята кутия с инструменти във формуляра, засега нещата изглеждат доста добре и се чувствам малко умен. Сега си мисля, че ще се върна към моята хубава икона, а не към скапаната, която клиентът избра, и ще променя кода в персонализираната контрола PickButton. Но не мога да се отърва от тази икона на клиенти, защото кодът, когато се изпълнява PickButton, се случва преди кода във файла на дизайнера, който има иконата на клиентите.

Така че целта ми беше да имам контрола PickButton и да мога да променям иконата и други свойства на едно място и всички свойства да бъдат зададени, когато екземпляр на контролата бъде създаден и показан във формуляра.

Не бях ли толкова умен и тръгнах да постигна задачата по грешен начин???

Това е моят персонализиран контролен клас PickButton

public class PickButton : Button
{

    public PickButton()
    {
        InitialiseButton();
    }

    internal void InitialiseButton()
    {
        this.ImageAlign = ContentAlignment.MiddleCenter;
        this.Image = WindowsFormsApplication1.Properties.Resources.Cancel.ToBitmap();
        this.Size = new Size( 28, 28 );
        this.Dock = DockStyle.Fill;
        this.Margin = new Padding( 0, 2, 2, 0 );
        this.Text = string.Empty;
    }
}

Сега пускам един във формуляра си и кодът в дизайнера е както следва

 // 
        // pickButton1
        // 
        this.pickButton1.Dock = System.Windows.Forms.DockStyle.Fill;
        this.pickButton1.Image = ((System.Drawing.Image)(resources.GetObject("pickButton1.Image")));
        this.pickButton1.Location = new System.Drawing.Point(0, 0);
        this.pickButton1.Margin = new System.Windows.Forms.Padding(0, 2, 2, 0);
        this.pickButton1.Name = "pickButton1";
        this.pickButton1.Size = new System.Drawing.Size(284, 262);
        this.pickButton1.TabIndex = 0;
        this.pickButton1.Text = "pickButton1";
        this.pickButton1.UseVisualStyleBackColor = true;

Сега искам да променя изображението, така че променям кода на PickButton, за да използвам различна икона

this.Image = WindowsFormsApplication1.Properties.Resources.Browse.ToBitmap();

Стартирайте приложението и първата икона все още е тази, която се показва поради този ред код във файла на дизайнера

        this.pickButton1.Image = ((System.Drawing.Image)(resources.GetObject("pickButton1.Image")));

person trailerman    schedule 26.07.2013    source източник
comment
Не съм напълно сигурен дали разбирам обяснението ви (кодът за публикуване вероятно ще бъде по-ясен), но защо не можете просто да зададете желаната от вас икона в конструктора PickButton?   -  person Cody Gray    schedule 26.07.2013
comment
Без да мога да видя кода, би трябвало да предположа, че сте били твърде умен и сте изложили иконата като свойство. Така че не реши проблема, защото сега имате стотици свойства за настройване. Вместо това го задайте в конструктора, така че всички бутони да използват една и съща икона.   -  person Hans Passant    schedule 26.07.2013
comment
@CodyGray Редактирах оригиналния въпрос и добавих малко код   -  person trailerman    schedule 27.07.2013
comment
@HansPassant Редактирах оригиналния въпрос и добавих малко код   -  person trailerman    schedule 27.07.2013
comment
Значи разбрахте, че е по-добре да присвоите свойството Image в конструктора. Твърде късно, файлът Designer.cs на формуляра все още има присвоено свойство Image. Няма да го премахнете само защото сте променили конструктора. Премахнете бутона и го добавете отново, за да коригирате. Или възстановете от контрол на източника.   -  person Hans Passant    schedule 27.07.2013
comment
Ако искате да оставите присвояването на иконата на клиента във файла .Designer.cs, опитайте да замените метода OnLoad (или нещо подобно, паметта ми е малко размита) и преназначете иконата към вашата там.   -  person Sameer Singh    schedule 27.07.2013
comment
@SameerSingh Класът Button няма събитие OnLoad или Load, нито Controls (System.Windows.Forms.Control). UserControls обаче имат събитие за зареждане.   -  person Scope Creep    schedule 24.10.2013
comment
@HansPassant Настройването на изображението в конструктора няма да направи нещо за решаване на този проблем. Дизайнерът ще създаде екземпляра (който извиква конструктора), който ще зададе изображението, ако направи това, което предлагате. След това няколко реда по-късно кодът на дизайнера, който той публикува, ще се изпълни, което ще презапише изображението, на което сте го задали в конструктора.   -  person Scope Creep    schedule 24.10.2013


Отговори (2)


Концепцията за задаване на всички свойства на едно място беше добра идея, просто не беше приложена съвсем правилно. Бих направил този клас наследник от UserControl вместо от Button. Като го направите UserControl, можете да използвате дизайнера, за да зададете всички свойства, които искате, като изображението по подразбиране за бутона. Задайте това в дизайнера, след което просто плъзнете и пуснете вашия UserControl от кутията с инструменти върху вашите формуляри. Ако използвате само вашия контрол "PickButton" с комбинирани полета, бих поставил комбинирания списък и на UserControl. Ако някога искате да промените изображението на бутона си в бъдеще (или друго свойство по този въпрос), ще можете да го промените в ctlPickButton и това ще разпространи промените във всички екземпляри, използвани във вашия проект(и).

ctlPickButton:

public partial class ctlPickButton : UserControl
{
    public event EventHandler pickButtonClicked;

    public ctlPickButton()
    {
        InitializeComponent();
    }

    //Allows buttons image to be set in code if necessary
    public Image Image
    {
        get
        {
            return button1.Image;
        }
        set
        {
            if (Image != null)
            {
                button1.Image = value;
            }
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (pickButtonClicked != null)
        {
            pickButtonClicked(sender, e);
        }
    }
}

Демо форма:

    public Form1()
    {
        InitializeComponent();

        ctlPickButton1.pickButtonClicked += new EventHandler(ctlPickButton1_pickButtonClicked);
        ctlPickButton2.pickButtonClicked += new EventHandler(ctlPickButton2_pickButtonClicked);
    }

    void ctlPickButton2_pickButtonClicked(object sender, EventArgs e)
    {
        if (comboBox2.SelectedItem != null)
        {
            MessageBox.Show(comboBox2.SelectedItem.ToString());
        }
    }

    void ctlPickButton1_pickButtonClicked(object sender, EventArgs e)
    {
        if (comboBox1.SelectedItem != null)
        {
            MessageBox.Show(comboBox1.SelectedItem.ToString());
        }
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        comboBox1.Items.Add("French");
        comboBox1.Items.Add("Spanish");
        comboBox1.Items.Add("English");
        comboBox1.Items.Add("German");

        comboBox2.Items.Add("Pizza");
        comboBox2.Items.Add("Hamburger");
        comboBox2.Items.Add("Potato");
        comboBox2.Items.Add("Chicken");

        //Shows how the default image set in the designer can be overwritten for a 
        //specific instance using the "Image" property
        ctlPickButton2.Image = Testbed.Properties.Resources.searchIcon2;
    }
}

Изображение на ctlPickButton в дизайнера

въведете описание на изображението тук

person Scope Creep    schedule 24.10.2013
comment
@SameerSingh Разгледах го и не мисля, че е добър подход. Първата причина е, че вашият бутон не може да се настрои правилно. Вашето решение ще изисква извикване на customButton.Refresh(); за всяко отделно копие на бутона всеки път, когато се използва. Второ, вашето решение не позволява изображението да варира между отделните екземпляри, тъй като сте кодирали изображението в метода Refresh. Трето, настройката на свойствата не е това, за което служи Refresh() и тъй като не извиква base.Refresh(), това няма да накара бутона да се обезсили и да бъде преначертан. - person Scope Creep; 25.10.2013
comment
Това беше просто демонстрационно приложение, което илюстрира какво се опитвате да постигнете. Не искате да извиквате Refresh () за всеки случай? Или го извикайте в конструктора на CustomButton, или променете изображението директно в неговия конструктор, или го променете в дизайнера (въпреки че мислех, че целта е да замените изображението на клиента по подразбиране). Ако искате изображението да варира, можете да зададете свойството на всеки екземпляр поотделно и да не извиквате Refresh () или да използвате друго свойство и да накарате Refresh () да използва това. - person Sameer Singh; 26.10.2013
comment
И накрая, използвах Refresh (), защото исках да бъде извикан само веднъж (други методи/събития се задействаха непрекъснато, което не беше това, което исках) - що се отнася до невалидността, просто добавете base.Refresh () към метода. Това е приложение за бърз модел на прототип, което е предназначено да бъде отправна точка и е много отворено за подобрение и конструктивна критика. - person Sameer Singh; 26.10.2013
comment
@SameerSingh В крайна сметка вашата идея няма да отведе никого, включително и вас, по добър път. Изображението на бутоните е свойство на бутона и като такова, според концепциите за ООП програмиране, трябва да може да зададе собствено изображение, без да ИЗИСКВА външен код. Освен това, както вече казах, вашият механизъм за настройка на изображението води до това, че всяко копие на класа е твърдо кодирано към едно изображение без механизъм за промяната му. Дори ако сте направили if/else или превключите във вашето събитие за опресняване, за да промените изображението, вашият код пак ще изисква постоянни модификации, тъй като се добавят повече изображения. - person Scope Creep; 28.10.2013
comment
@SameerSingh Не се опитвам да бъда глупак, просто искам да реализираш идеята си, макар и креативна, не е добра. Добре написаните обекти капсулират своите данни и функционалност и могат да се поддържат сами. Вашият не го прави. Използването на събитие за нещо различно от предназначението му не е добро. Един напълно конфигурируем бутон може да бъде чудесно начало за многократно използваема контролна библиотека (dll). Но никога няма да стане толкова многократно използван, като използва предложеното от вас решение. Отново, не изтръгвам идеята ви, за да бъдете глупак, честно казано просто се опитвам да ви покажа защо вашият начин на мислене ще ви ухапе в дългосрочен план. - person Scope Creep; 28.10.2013

Мисля, че намерих просто, чисто решение:

В класа CustomButton (който наследява от System.Windows.Forms.Button), заменете метода Refresh() и задайте изображението на бутона на това, което искате да видите:

public class CustomButton : Button
{
  public override void Refresh()
  {
    Image = MyResources.HappyFace;
  }
}

Във формуляра, който ще съдържа екземпляр на вашия CustomButton, просто извикайте customButton.Refresh() в конструктора след InitializeComponent():

public partial class MainForm : Form
{
  public MainForm()
  {
    InitializeComponent();

    customButton.Refresh();
  }
}

Поставих демо приложение в Github.

person Sameer Singh    schedule 25.10.2013