Windows form button with image

Элементы управления

Последнее обновление: 31.10.2015

Элементы управления представляют собой визуальные классы, которые получают введенные пользователем данные и могут инициировать различные события.
Все элементы управления наследуются от класса Control и поэтому имеют ряд общих свойств:

  • Anchor: Определяет, как элемент будет растягиваться

  • BackColor: Определяет фоновый цвет элемента

  • BackgroundImage: Определяет фоновое изображение элемента

  • ContextMenu: Контекстное меню, которое открывается при нажатии на элемент правой кнопкой мыши.
    Задается с помощью элемента ContextMenu

  • Cursor: Представляет, как будет отображаться курсор мыши при наведении на элемент

  • Dock: Задает расположение элемента на форме

  • Enabled: Определяет, будет ли доступен элемент для использования. Если это свойство имеет значение False,
    то элемент блокируется.

  • Font: Устанавливает шрифт текста для элемента

  • ForeColor: Определяет цвет шрифта

  • Location: Определяет координаты верхнего левого угла элемента управления

  • Name: Имя элемента управления

  • Size: Определяет размер элемента

  • Width: ширина элемента

  • Height: высота элемента

  • TabIndex: Определяет порядок обхода элемента по нажатию на клавишу Tab

  • Tag: Позволяет сохранять значение, ассоциированное с этим элементом управления

Кнопка

Наиболее часто используемым элементом управления является кнопка. Обрабатывая событие нажатия кнопки, мы может производить те или иные действия.

При нажатии на кнопку на форме в редакторе Visual Studio мы по умолчанию попадаем в код обработчика события Click,
который будет выполняться при нажатии:

private void button1_Click(object sender, EventArgs e)
{
    MessageBox.Show("Hello World");
}

Оформление кнопки

Чтобы управлять внешним отображением кнопки, можно использовать свойство FlatStyle. Оно может принимать следующие значения:

  • Flat — Кнопка имеет плоский вид

  • Popup — Кнопка приобретает объемный вид при наведении на нее указателя, в иных случаях она имеет плоский вид

  • Standard — Кнопка имеет объемный вид (используется по умолчанию)

  • System — Вид кнопки зависит от операционной системы

Изображение на кнопке

Как и для многих элементов управления, для кнопки можно задавать изображение с помощью свойства BackgroundImage.
Однако мы можем также управлять размещением текста и изображения на кнопки.
Для этого надо использовать свойство TextImageRelation. Оно приобретает следующие значения:

  • Overlay: текст накладывается на изображение

  • ImageAboveText: изображение располагается над текстом

  • TextAboveImage: текст располагается над изображением

  • ImageBeforeText: изображение располагается перед текстом

  • TextBeforeImage: текст располагается перед изображением

Например, установим для кнопки изображение. Для этого выберем кнопку и в окне Свойств нажмем на поле Image (не путать с BackgroundImage). Нам откроется диалоговое окно
установи изображения:

Установка изображения для кнопки

В этом окне выберем опцию Local Resource и нажмем на кнопку Import, после чего нам откроется диалоговое окно для выбора файла изображения.

После выбора изображения мы можем установить свойство ImageAlign, которое управляет позиционированием изображения на кнопке:

Нам доступны 9 вариантов, с помощью которых мы можем прикрепить изображение к определенной стороне кнопки. Оставим здесь значение по умолчанию — MiddleCenter,
то есть позиционирование по центру.

Затем перейдем к свойству TextImageRelation и установим для него значение ImageBeforeText. В итоге мы получим кнопку,
где сразу после изображения идет надпись на кнопке:

Клавиши быстрого доступа

При работе с формами при использовании клавиатуры очень удобно пользоваться клавишами быстрого доступа. При нажатии на клавиатуре комбинации
клавиш At+некоторый символ, будет вызываться определенная кнопка. Например, зададим для некоторой кнопки свойство Text равное &Аватар.
Первый знак — амперсанд — определяет ту букву, которая будет подчеркнута. В данном случае надпись будет выглядеть как
Аватар. И теперь чтобы вызвать событие Click, нам достаточно нажать на комбинацию клавиш Alt+А.

Кнопки по умолчанию

Форма, на которой размещаются все элементы управления, имеет свойства, позволяющие назначать кнопку по умолчанию и кнопку отмены.

Так, свойство формы AcceptButton позволяет назначать кнопку по умолчанию, которая будет срабатывать по нажатию на клавишу Enter.

Аналогично работает свойство формы CancelButton, которое назначает кнопку отмены. Назначив такую кнопку, мы можем вызвать ее нажатие,
нажав на клавишу Esc.

Adding images to buttons in C# can greatly enhance the visual appeal and user experience of your applications. In this tutorial, we will walk through how to create a button with an image in C#.

Using Windows Forms

If you are working with Windows Forms, you can easily add an image to a button using the Image property. Here’s an example:

Button imageButton = new Button();
imageButton.Image = Image.FromFile("path/to/your/image.png");
imageButton.ImageAlign = ContentAlignment.MiddleLeft; // Set the alignment of the image

In the code snippet above, we create a new Button instance and set its Image property to an image loaded from a file. You can adjust the image alignment using the ImageAlign property.

Using WPF

For WPF applications, you can use XAML to define buttons with images. Here’s an example:

<Button>
    <StackPanel Orientation="Horizontal">
        <Image Source="path/to/your/image.png" Width="16" Height="16"/>
        <TextBlock Text="Button with Image"/>
    </StackPanel>
</Button>

In this XAML snippet, we create a button that contains an image and text. The Image element is used to display the image, and you can adjust the Width and Height properties as needed.

Conclusion

Adding images to buttons in C# is a simple way to improve the visual appeal of your applications. Whether you are working with Windows Forms or WPF, incorporating images into buttons can make your UI more engaging and intuitive for users. Experiment with different image sizes and alignments to achieve the desired look for your buttons.

Introduction

Learn how to create a series of buttons at runtime which is useful for displaying information were the data changes, for instance, product categories, images or asking questions. There are three projects, two projects read data from a SQL Server database, and one reads json data. In each of the projects there is a common pattern used which means that code will be easy to follow from project to project.

An important thing here in all code samples is to keep code in each form to a minimal which means, as done when writing the provided code a developer can easily copy-and-paste code to another project and make changes rather than copy code from a form where there may be ties to the form.

Source code

Working with databases

When working with SQL Server data, SQL statements are stored in read only strings for the reader to easily see the statements. Some might consider using stored procedures for security.

Connection strings are stored in appsettings.json

Custom Button

While most developers needing to store project specific information for a button use the tag property of, in this case a button, here a custom is used.

Button properties

  • Identifier for storing a primary key
  • Stash for storing anything (but not used, here to give possiblities)
  • Project specific property e.g. Image, a class instance
public class DataButton : Button
{
    [Category("Behavior"), Description("Identifier")]
    public int Identifier { get; set; }
    [Category("Behavior"), Description("Stash")]
    public string Stash { get; set; }

    public Image Picture { get; set; }
}

public class DataButton : Button
{
    [Category("Behavior"), Description("Identifier")]
    public int Identifier { get; set; }
    [Category("Behavior"), Description("Stash")]
    public string Stash { get; set; }

    public new Container Container { get; set; }
}

public class DataButton : Button
{
    [Category("Behavior"), Description("Identifier")]
    public int Identifier { get; set; }
    [Category("Behavior"), Description("Stash")]
    public string Stash { get; set; }
}

Enter fullscreen mode

Exit fullscreen mode

Common classes

Consistency is important when writing similar task. For each project there are two classes, ButtonSetup used to feed information to ButtonOperations which is responsible for button creation.

➰ In each project, positioning an button sizes vary.

ButtonSetup

public class ButtonSetup
{
    public Control Control { get; set; }
    public int Top { get; set; }
    public int BaseHeightPadding { get; set; }
    public int Left { get; set; }
    public int Width { get; set; }
    public EventHandler ButtonClick { get; set; }
}

Enter fullscreen mode

Exit fullscreen mode

Which is used to feed information to ButtonOperations constructor. Note, Log.Information which uses SeriLog NuGet packages to write details to a file which is useful if there is an issue with displaying buttons, remove this for production code.

public static void Initialize(ButtonSetup sender)
{

    ParentControl = sender.Control;
    Top = sender.Top;
    HeightPadding = sender.BaseHeightPadding;
    Left = sender.Left;
    Width = sender.Width;
    EventHandler = sender.ButtonClick;
    ButtonsList = new List<DataButton>();

    var methodName = $"{nameof(ButtonOperations)}.{nameof(Initialize)}";
    Log.Information("{Caller} Top: {Top} Left: {Left}", methodName, sender.Top, sender.Left);

}

Enter fullscreen mode

Exit fullscreen mode

Displaying images

In this example a database table has images a developer wants to display and the count of images can change.

Form with buttons to display images

Data Operations

The following statements are stored in a class to read data.

public static string SelectImageByIdentifier => 
    """
    SELECT 
        id, 
        Photo,
        Description
    FROM 
        dbo.Pictures1 
    WHERE 
        dbo.Pictures1.id = @id;
    """;

public static string ReadAllImages => 
    """
    SELECT 
        Id,
        Photo,
        [Description] 
    FROM 
        dbo.Pictures1
    """;

Enter fullscreen mode

Exit fullscreen mode

Model for storing data with the Image property converting a byte array to an image with an extension method.

public class PhotoContainer
{
    public int Id { get; set; }
    public Image Image => Photo.BytesToImage();
    public Image Picture { get; set; }
    public byte[] Photo { get; set; }
    public string Description { get; set; }
    public override string ToString() => Description;
}

Enter fullscreen mode

Exit fullscreen mode

Class to read data from SQL-Server using Dapper NuGet package.

internal class PhotoOperations
{

    /// <summary>
    /// Read image from database table by primary key
    /// </summary>
    /// <param name="identifier">Primary key</param>
    public static (bool success, PhotoContainer) ReadImage(int identifier)
    {
        using var cn = new SqlConnection(ConnectionString());
        var container =  cn.QueryFirstOrDefault<PhotoContainer>(
            SqlStatements.SelectImageByIdentifier, new {id = identifier});
        if (container is not null)
        {
            container.Picture = container.Photo.BytesToImage();

            return (true, container);
        }

        return (false, null);
    }

    /// <summary>
    /// Read all records for images
    /// </summary>
    public static List<PhotoContainer> Read()
    {
        using var cn = new SqlConnection(ConnectionString());
        return  cn.Query<PhotoContainer>(SqlStatements.ReadAllImages).AsList();
    }
}

Enter fullscreen mode

Exit fullscreen mode

Button creation class

  • Parent property is the control to add buttons too.
  • Identifier, primary key used to reference SQL-Server data.
  • Name, always name controls as a reference to a button may be needed.
  • EventHandler which provide access to a button Click event.
  • Log.Information is for development, remove for production.
public static class ButtonOperations
{
    public static List<DataButton> ButtonsList { get; set; }
    public static int Top { get; set; }
    public static int Left { get; set; }
    public static int Width { get; set; }
    public static int HeightPadding { get; set; }
    public static string BaseName { get; set; } = "Button";
    public static EventHandler EventHandler { get; set; }
    public static Control ParentControl { get; set; }
    private static int _index = 1;

    public static void Initialize(ButtonSetup sender)
    {
        ParentControl = sender.Control;
        Top = sender.Top;
        HeightPadding = sender.BaseHeightPadding;
        Left = sender.Left;
        Width = sender.Width;
        EventHandler = sender.ButtonClick;
        ButtonsList = [];

        var methodName = $"{nameof(ButtonOperations)}.{nameof(Initialize)}";

        // allows developer to see what was created for debug purposes
        Log.Information("{Caller} Top: {Top} Left: {Left}", methodName, sender.Top, sender.Left);

    }
    /// <summary>
    /// Create new <see cref="DataButton"/> and add to <see cref="ButtonsList"/>"/> and set Click event
    /// </summary>
    /// <param name="description">Description of image</param>
    /// <param name="identifier">Primary key of row for image</param>
    private static void CreateButton(string description, int identifier)
    {

        var ( _, photoContainer) = PhotoOperations.ReadImage(identifier);
        var button = new DataButton()
        {
            Name = $"{BaseName}{_index}",
            Text = description,
            Width = Width,
            Height = 29,
            Location = new Point(Left, Top),
            Parent = ParentControl,
            Identifier = identifier,
            Visible = true, 
            Picture = photoContainer.Picture
        };

        button.Click += EventHandler;

        var methodName = $"{nameof(ButtonOperations)}.{nameof(CreateButton)}";

        // allows developer to see what was created for debug purposes
        Log.Information("{Caller} Name: {Name} CategoryId: {CategoryId} Location {Left},{Right}", 
            methodName, button.Name, identifier, Left, Top);

        ButtonsList.Add(button);

        ParentControl.Controls.Add(button);
        Top += HeightPadding;
        _index += 1;

    }

    public static void BuildButtons()
    {
        foreach (var container in PhotoOperations.Read())
        {
            CreateButton(container.Description, container.Id);
        }
    }
}

Enter fullscreen mode

Exit fullscreen mode

Form code

A FlowLayoutPanel will house buttons with a PictureBox below the FlowLayoutPanel (which is docked to form top).

Buttons are created followed by finding a specific button (DataButton) with specific text. This is followed by setting the Button’s image to the PictureBox and then making the button the active control.

ButtonClick event is triggered when clicking any button (DataButton) and assigning it’s image to the PictureBox.

Read from two table

This code sample reads from a modifies version of Microsoft’s NorthWind database. Each category is displayed in buttons (DataButton), on click display products for the select category in a ListBox. Under the ListBox shows the product name and primary key. Double Click a product to get the primary key and product name. This allows a developer to write another statement to find out for instances orders that the product is in.

Shows the form

Models

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public override string ToString() => Name;
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public override string ToString() => Name;
}

Enter fullscreen mode

Exit fullscreen mode

Custom button

Note when compared to the last example has the same properties minus the Image property.

public class DataButton : Button
{
    [Category("Behavior"), Description("Identifier")]
    public int Identifier { get; set; }
    [Category("Behavior"), Description("Stash")]
    public string Stash { get; set; }
}

Enter fullscreen mode

Exit fullscreen mode

Data Operations

Here Dapper is used again while rather than using another class for SQL Statements the statements are in each method which some developer might like instead of separating statements.

The pattern used here is similar to the last example minis images.

  • ReadCategories method reads all categories from the database.
  • ReadProducts method reads all products for a specific category.
public class DataOperations
{
    /// <summary>
    /// Read all categories
    /// </summary>
    /// <returns>list of categories</returns>
    public static List<Category> ReadCategories()
    {
        var list = new List<Category>();

        try
        {
            using SqlConnection cn = new() { ConnectionString = ConnectionString() };
            var selectStatement = 
                """
                SELECT CategoryID as Id, CategoryName as Name
                FROM dbo.Categories
                """;
            list = cn.Query<Category>(selectStatement).ToList();
        }
        catch (Exception exception)
        {
            Log.Error(exception.Message);
        }

        return list;

    }
    /// <summary>
    /// Read products by category identifier
    /// </summary>
    /// <param name="identifier">category identifier</param>
    /// <returns>list of products for category</returns>
    public static List<Product> ReadProducts(int identifier)
    {
        using SqlConnection cn = new() { ConnectionString = ConnectionString() };

        var selectStatement = 
            """
            SELECT ProductID as Id, ProductName as Name
            FROM dbo.Products WHERE CategoryID = @Id
            ORDER BY ProductName
            """;

        return cn.Query<Product>(selectStatement, new { Id = identifier }).ToList();

    }
}

Enter fullscreen mode

Exit fullscreen mode

Button creation

Follows the same code as with the image sample.

  • Parent property is the control to add buttons too.
  • Identifier, primary key used to reference SQL-Server data.
  • Name, always name controls as a reference to a button may be needed.
  • EventHandler which provide access to a button Click event.
  • Log.Information is for development, remove for production.
public static class ButtonOperations
{
    public static List<DataButton> ButtonsList { get; set; }
    public static int Top { get; set; }
    public static int Left { get; set; }
    public static int Width { get; set; }
    public static int HeightPadding { get; set; }
    public static string BaseName { get; set; } = "Button";
    public static EventHandler EventHandler { get; set; }
    public static Control ParentControl { get; set; }
    private static int _index = 1;


    public static void Initialize(ButtonSetup sender)
    {

        ParentControl = sender.Control;
        Top = sender.Top;
        HeightPadding = sender.BaseHeightPadding;
        Left = sender.Left;
        Width = sender.Width;
        EventHandler = sender.ButtonClick;
        ButtonsList = new List<DataButton>();

        var methodName = $"{nameof(ButtonOperations)}.{nameof(Initialize)}";
        Log.Information("{Caller} Top: {Top} Left: {Left}", methodName, sender.Top, sender.Left);

    }

    private static void CreateCategoryButton(string text, int categoryIdentifier)
    {

        var button = new DataButton()
        {
            Name = $"{BaseName}{_index}",
            Text = text,
            Width = Width,
            Height = 29,
            Location = new Point(Left, Top),
            Parent = ParentControl,
            Identifier = categoryIdentifier,
            Visible = true,
            ImageAlign = ContentAlignment.MiddleLeft,
            TextAlign = ContentAlignment.MiddleRight
        };

        button.Click += EventHandler;

        var methodName = $"{nameof(ButtonOperations)}.{nameof(CreateCategoryButton)}";
        Log.Information("{Caller} Name: {Name} CategoryId: {CategoryId} Location {Left},{Right}", 
            methodName, button.Name, categoryIdentifier, Left, Top);

        ButtonsList.Add(button);

        ParentControl.Controls.Add(button);
        Top += HeightPadding;
        _index += 1;

    }

    public static void BuildButtons()
    {
        foreach (var category in DataOperations.ReadCategories())
        {
            CreateCategoryButton(category.Name, category.Id);
        }
    }
}

Enter fullscreen mode

Exit fullscreen mode

Form code

Here buttons are placed directly on the left side of the form unlike the last example which placed button in a FlowLayoutPanel. There is not much different from the last examle.

In the form constructor.

  • Setup for creating buttons.
  • Create the buttons and display the category buttons.

In the Click event for the buttons, the active button is assigned an image to show the active category from project resources.

➰ Note how clean and easy the code is to read!!!

public partial class Form1 : Form
{
    private BindingList<Product> productsBindingList;
    private readonly BindingSource productBindingSource = new ();
    public Form1()
    {
        InitializeComponent();

        ButtonOperations.BaseName = "CategoryButton";


        var buttonSetup = new ButtonSetup
        {
            Control = this,
            Top = 20,
            BaseHeightPadding = 42,
            Left = 10,
            Width = 150,
            ButtonClick = CategoryButtonClick
        };

        ButtonOperations.Initialize(buttonSetup);
        ProductsListBox.DoubleClick += ProductsListBoxOnDoubleClick;
        ButtonOperations.BuildButtons();
    }

    private void ProductsListBox_SelectedIndexChanged(object sender, EventArgs e)
    {
        DisplayCurrentProduct();
    }

    private void DisplayCurrentProduct()
    {
        if (productBindingSource.Current is null)
        {
            return;
        }

        var product = productsBindingList[productBindingSource.Position];
        CurrentProductTextBox.Text = $"{product.Id}, {product.Name}";
    }

    private void ProductsListBoxOnDoubleClick(object sender, EventArgs e)
    {
        if (productBindingSource.Current is null)
        {
            return;
        }

        var product = productsBindingList[productBindingSource.Position];

        MessageBox.Show($"{product.Id}, {product.Name}");
    }

    private void CategoryButtonClick(object sender, EventArgs e)
    {

        ButtonOperations.ButtonsList.ForEach(b => b.Image = null);

        var button = (DataButton)sender;

        button.Image = Resources.rightArrow24;

        ProductsListBox.SelectedIndexChanged -= ProductsListBox_SelectedIndexChanged;
        productsBindingList = new BindingList<Product>(DataOperations.ReadProducts(button.Identifier));
        productBindingSource.DataSource = productsBindingList;
        ProductsListBox.DataSource = productBindingSource;
        ProductsListBox.SelectedIndexChanged += ProductsListBox_SelectedIndexChanged;

        DisplayCurrentProduct();

    }
}

Enter fullscreen mode

Exit fullscreen mode

Questions and answers

Although this code sample follows the same code patterns as the above samples there are more moving parts.

Data is read from a json file so that the reader can switch it up from using a database. There are over 500 questions and answers. For this example, 15 random questions are displayed.

Form screenshot

Model

  • The properties, A,B,C,D are choices
  • QuestionIdentifier property is the button text
  • Answer is one of the A,B,C,D properties
{
  "Id": 1,
  "Question": "A flashing red traffic light signifies that a driver should do what?",
  "A": "stop",
  "B": "speed up",
  "C": "proceed with caution",
  "D": "honk the horn",
  "Answer": "A"
}

Enter fullscreen mode

Exit fullscreen mode

public class Container
{
    public int Id { get; set; }
    public int QuestionIdentifier { get; set; }
    public string Question { get; set; }
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
    public string D { get; set; }
    public string Answer { get; set; }
    public override string ToString() => Question;
}

Enter fullscreen mode

Exit fullscreen mode

Button operations

Again, use the same logic as the former examples. Note the use of Random.Shared.GetItems which provides random questions from the json file so each time the project runs different questions are shown.

public static class ButtonOperations
{
    public static List<DataButton> ButtonsList { get; set; }
    public static List<Container> Containers { get; set; }
    public static int Top { get; set; }
    public static int Left { get; set; }
    public static int Width { get; set; }
    public static int HeightPadding { get; set; }
    public static string BaseName { get; set; } = "Button";
    public static EventHandler EventHandler { get; set; }
    public static Control ParentControl { get; set; }
    private static int _index = 1;
    private static int _questionCount;


    public static void Initialize(ButtonSetup sender)
    {

        ParentControl = sender.Control;
        Top = sender.Top;
        HeightPadding = sender.BaseHeightPadding;
        Left = sender.Left;
        Width = sender.Width;
        EventHandler = sender.ButtonClick;
        ButtonsList = [];
        _questionCount = sender.QuestionCount;

        var methodName = $"{nameof(ButtonOperations)}.{nameof(Initialize)}";

        // allows developer to see what was created for debug purposes
        Log.Information("{Caller} Top: {Top} Left: {Left}", methodName, sender.Top, sender.Left);

    }

    private static void CreateButton(Container container)
    {
        var button = new DataButton()
        {
            Name = $"{BaseName}{_index}",
            Text = container.QuestionIdentifier.ToString(),
            Width = Width,
            Height = 29,
            Location = new Point(Left, Top),
            Parent = ParentControl,
            Identifier = container.Id,
            Container = container,
            Visible = true
        };

        button.Click += EventHandler;

        var methodName = $"{nameof(ButtonOperations)}.{nameof(CreateButton)}";

        // allows developer to see what was created for debug purposes
        Log.Information("{Caller} Name: {Name} Id: {CategoryId} Location {Left},{Right}", 
            methodName, button.Name, container.Id, Left, Top);

        ButtonsList.Add(button);

        ParentControl.Controls.Add(button);
        Top += HeightPadding;
        _index += 1;

    }

    public static void BuildButtons()
    {
        Containers = Random.Shared
            .GetItems<Container>(CollectionsMarshal.AsSpan(JsonOperations.GetQuestions()), _questionCount)
            .ToList();

        for (int index = 0; index < Containers.Count; index++)
        {
            Containers[index].QuestionIdentifier = index + 1;
        }

        foreach (var container in Containers)
        {
            CreateButton(container);
        }
    }
}

Enter fullscreen mode

Exit fullscreen mode

Read Json file class

public class JsonOperations
{
    public static string FileName => "questions.json";

    public static List<Container> GetQuestions() 
        => JsonSerializer.Deserialize<List<Container>>(File.ReadAllText(FileName));
}

Enter fullscreen mode

Exit fullscreen mode

DataButton

In this case the question/answers are included in the property Container.

public class DataButton : Button
{
    [Category("Behavior"), Description("Identifier")]
    public int Identifier { get; set; }
    [Category("Behavior"), Description("Stash")]
    public string Stash { get; set; }

    public new Container Container { get; set; }
}

Enter fullscreen mode

Exit fullscreen mode

Form Code

  • As with the former code samples, buttons are created using the same logic. Controls are added to a FlowLayoutPanel.
  • Four RadioButtons are data bound to A,B,C,D choices for the current question.
  • A label is data bound to the current question.
  • Container _currentContainer is used to keep track of the current question.

ButtonClick event

This event sets up the correct answer by setting the RadioButton Tag property to not null.

AnswerButton_Click event

Checks to see if the correct RadioButton was clicked.

public partial class Form1 : Form
{
    private BindingList<Container> _containers = new();
    private BindingSource _bindingSource = new();
    private Container _currentContainer;

    public Form1()
    {
        InitializeComponent();

        ButtonOperations.BaseName = "CategoryButton";

        var buttonSetup = new ButtonSetup
        {
            Control = this,
            Top = 20,
            BaseHeightPadding = 42,
            Left = 10,
            Width = 150,
            ButtonClick = ButtonClick,
            QuestionCount = 15
        };

        ButtonOperations.Initialize(buttonSetup);
        ButtonOperations.BuildButtons();
        var buttons = ButtonOperations.ButtonsList;

        foreach (var button in ButtonOperations.ButtonsList)
        {
            flowLayoutPanel1.Controls.Add(button);
        }

        _containers = new BindingList<Container>(ButtonOperations.Containers);
        _bindingSource.DataSource = _containers;

        QuestionLabel.DataBindings.Add("Text", _bindingSource, "Question");
        radioButtonA.DataBindings.Add("Text", _bindingSource, "A");
        radioButtonB.DataBindings.Add("Text", _bindingSource, "B");
        radioButtonC.DataBindings.Add("Text", _bindingSource, "C");
        radioButtonD.DataBindings.Add("Text", _bindingSource, "D");

        _currentContainer = _containers[0];

        var answer = _currentContainer.Answer;
        var rb = QuestionGroupBox.Controls.OfType<RadioButton>()
            .FirstOrDefault(x => x.Name.EndsWith(answer));

        rb!.Tag = "Correct";
    }

    private void ButtonClick(object? sender, EventArgs e)
    {
        // clear all tags used in the button click event to determine correct answer
        foreach (var radioButton in QuestionGroupBox.Controls.OfType<RadioButton>())
        {
            radioButton.Tag = null;
        }

        var button = (DataButton)sender!;
        _currentContainer = button.Container;

        // set the correct answer for the radio button
        var rb = QuestionGroupBox.Controls.OfType<RadioButton>()
            .FirstOrDefault(x => x.Name.EndsWith(_currentContainer.Answer));
        rb!.Tag = "Correct";

        // set the position of the binding source to the current container/question
        _bindingSource.Position = _containers.IndexOf(_currentContainer);

        foreach (var radioButton in QuestionGroupBox.RadioButtonList())
        {
            radioButton.Checked = false;
        }

    }

    private void AnswerButton_Click(object sender, EventArgs e)
    {
        var answer = QuestionGroupBox.RadioButtonChecked();
        if (answer is null) return;
        if (answer.Tag is not null)
        {
            MessageBox.Show("Correct");  
        }
        else
        {
            MessageBox.Show("Incorrect");
        }
    }
}

Enter fullscreen mode

Exit fullscreen mode

Summary

Several code samples have been provided to show how to create dynamic buttons at runtime for Window Forms uses similar/consistent code patterns which should educate a developer to then follow what has been provided for their task.

If something does not make sense, set breakpoints and traverse the code and inspect object and variables for a better understanding of the code.

C#, Java, Python, C++ Programaming Examples

Hello, developers! 👋
Today, we’re diving into a very practical and commonly needed feature in Windows Forms (WinForms) applications — dynamically switching a button’s image based on user interaction, without relying on image file names at runtime.

In this tutorial, you’ll learn how to smoothly toggle a button’s image between two states (such as “Accept” and “Reject”) using global Bitmap objects.
This approach enhances user experience while keeping your code clean and efficient.

Why Switch Button Images Dynamically?

Dynamic image switching provides visual feedback to users, making your application more intuitive and interactive.
Instead of just changing text, combining it with images helps users immediately recognize the current state or action.

Typical scenarios where this is useful:

  • Approving or rejecting items
  • Play/Pause buttons
  • On/Off toggles
  • Expanding/Collapsing menus

Step-by-Step Code Example

Here’s the full example in C#:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

using System;

using System.Drawing;

using System.Windows.Forms;

namespace WindowsFormsApp4

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

        // Global Bitmap objects for button states

        Bitmap bmpAccept = Properties.Resources.accept;

        Bitmap bmpReject = Properties.Resources.reject;

        private void Form1_Load(object sender, EventArgs e)

        {

            button1.Image = bmpAccept;

            button1.Text = «Accept All»;

        }

        private void button1_Click(object sender, EventArgs e)

        {

            if (button1.Image == bmpAccept)

            {

                button1.Image = bmpReject;

                button1.Text = «Reject All»;

            }

            else

            {

                button1.Image = bmpAccept;

                button1.Text = «Accept All»;

            }

        }

    }

}

How It Works: Code Breakdown

1. Global Bitmap Objects

We define two global Bitmap variables:

  • bmpAccept for the “Accept” action
  • bmpReject for the “Reject” action

Using global objects allows quick and efficient image switching without reloading resources each time.


2. Initial Button Setup

In the Form1_Load event:

  • The button’s image is set to bmpAccept
  • The button’s text is set to "Accept All"

This ensures that your application starts in a consistent, default state.


3. Handling the Click Event

Inside button1_Click:

  • We check if the current image is bmpAccept.
  • If it is, we switch it to bmpReject and change the button text to "Reject All".
  • Otherwise, we revert back to bmpAccept and "Accept All".

Result: Smooth toggling between two button states based on user interaction!


Extra Tips for Better UX

  • Image Size: Ensure that the images you use are properly resized to fit the button without distortion.
  • Tooltips: You can add tooltips for additional hints when users hover over the button.
  • Accessibility: Always combine icons with clear text for better accessibility and user understanding.

Conclusion

By using global Bitmap objects and simple conditional logic, you can easily switch button images in your C# WinForms applications without worrying about file names or runtime resource issues.

This technique not only keeps your code clean but also delivers a smoother and more engaging user experience.
Perfect for toggle buttons, approval systems, and interactive interfaces!

If you found this tutorial helpful, don’t forget to:

  • 👍 Like the video
  • 🔔 Subscribe to the channel
  • 📝 Comment your thoughts or ask questions!

Stay tuned for more practical coding tutorials and C# tips! 🚀

You may also like

Все знают cc как добавить кнопки в C# форму, особенно в Visual Studio это очень легко. Но как добавить в форму кнопка изображения. Сегодня будем создать именно такую приложению.

Создаем новый Windows.Forms приложения. Добавим там две PictureBox и даем их размер 40×40.

Откроем свойство приложении и в Resources добавим картинки, которые находятся где то в компьютере.

Картинки программным образом можно получать из Resources таким образом
        Image Check = Resources.check;
        Image Cross = Resources.cross;

Если теперь в конструкторе добавить эти строки
            pictureBox1.Image = Check;
            pictureBox2.Image = Cross;
то при запуске приложении, изображении будут появляться на форму

Когда курсор мыши находится на изображении, мы хотим чтобы вид курсора изменился на рука. Для этого в свойстах двух PictureBox для Cursor выбираем Hand.

Теперь создаем событии для клика, где например должен выводится какое то сообщения
        private void pictureBox1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(«Check Button is Clicked!»);
        }


        private void pictureBox2_Click(object sender, EventArgs e)
        {
            MessageBox.Show(«Cross Button is Clicked!»);
        }

Давайте теперь сделаем немного анимацию. Мы например хотим, чтобы когда курсор мыши находится на PictureBox размер изображении изменился. Для этого для каждого изображении создаем события MouseHover и MouseLeave. То что будет с MouseLeave думаю всем понятно, изображения будет принимать свой прежний размер.
        private void pictureBox1_MouseLeave(object sender, EventArgs e)
        {
            pictureBox1.Image = Check;
        }


        private void pictureBox2_MouseLeave(object sender, EventArgs e)
        {
            pictureBox2.Image = Cross;
        }

А вот с MouseHover мы должны изменить размер изображении допустим на 20%. Вот две события для изменения размеров изображений
        private void pictureBox1_MouseHover(object sender, EventArgs e)
        {
            int Check_Width = Check.Width + ((Check.Width * 20) / 100);
            int Check_Height = Check.Height + ((Check.Height * 20) / 100);


            Bitmap Check_1 = new Bitmap(Check_Width, Check_Height);
            Graphics g = Graphics.FromImage(Check_1);
            g.DrawImage(Check, new Rectangle(Point.Empty, Check_1.Size));
            pictureBox1.Image = Check_1;
        }


        private void pictureBox2_MouseHover(object sender, EventArgs e)
        {
            int Cross_Width = Cross.Width + ((Cross.Width * 20) / 100);
            int Cross_Height = Cross.Height + ((Cross.Height * 20) / 100);


            Bitmap Cross_1 = new Bitmap(Cross_Width, Cross_Height);
            Graphics g = Graphics.FromImage(Cross_1);
            g.DrawImage(Cross, new Rectangle(Point.Empty, Cross_1.Size));
            pictureBox2.Image = Cross_1;
        }

Вот польный код программы
using Image_as_Button.Properties;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace Image_as_Button
{
    public partial class Form1 : Form
    {
        Image Check = Resources.check;
        Image Cross = Resources.cross;


        public Form1()
        {
            InitializeComponent();
            pictureBox1.Image = Check;
            pictureBox2.Image = Cross;
        }


        private void pictureBox1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(«Check Button is Clicked!»);
        }


        private void pictureBox2_Click(object sender, EventArgs e)
        {
            MessageBox.Show(«Cross Button is Clicked!»);
        }


        private void pictureBox1_MouseHover(object sender, EventArgs e)
        {
            int Check_Width = Check.Width + ((Check.Width * 20) / 100);
            int Check_Height = Check.Height + ((Check.Height * 20) / 100);


            Bitmap Check_1 = new Bitmap(Check_Width, Check_Height);
            Graphics g = Graphics.FromImage(Check_1);
            g.DrawImage(Check, new Rectangle(Point.Empty, Check_1.Size));
            pictureBox1.Image = Check_1;
        }


        private void pictureBox2_MouseHover(object sender, EventArgs e)
        {
            int Cross_Width = Cross.Width + ((Cross.Width * 20) / 100);
            int Cross_Height = Cross.Height + ((Cross.Height * 20) / 100);


            Bitmap Cross_1 = new Bitmap(Cross_Width, Cross_Height);
            Graphics g = Graphics.FromImage(Cross_1);
            g.DrawImage(Cross, new Rectangle(Point.Empty, Cross_1.Size));
            pictureBox2.Image = Cross_1;
        }


        private void pictureBox1_MouseLeave(object sender, EventArgs e)
        {
            pictureBox1.Image = Check;
        }


        private void pictureBox2_MouseLeave(object sender, EventArgs e)
        {
            pictureBox2.Image = Cross;
        }
    }
}

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • После обновления windows 10 пропал рабочий стол
  • Где посмотреть видеокарту на windows 10 на ноутбуке
  • Драйвер для nexus 7 для windows 10
  • Восстановление запуска windows 10 при повреждении загрузчика
  • Активация windows через kms auto