Управление 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, равно как и элементы управления (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 или switch в событии обновления, чтобы изменить изображение, ваш код все равно будет требовать постоянных модификаций по мере добавления новых изображений. - 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