WPF Development, Practice displaying products and details using MVVM

Windows Presentation Foundation (WPF) is a UI development platform based on the .NET framework that enables the creation of user interfaces (UI) and implements elegant designs through data binding and styling. Today, we will develop an application that displays a simple product list and details using the MVVM (Model-View-ViewModel) design pattern.

1. Understanding MVVM Architecture

The MVVM pattern divides the structure of the application into three main components, increasing maintainability.

  • Model: Responsible for the data structure and business logic of the application. This includes functionalities for fetching data from the database or processing data.
  • View: Contains the UI components displayed to the user, usually written in XAML files.
  • ViewModel: Acts as an intermediary between the Model and the View, providing processed data to enable user interactions in the View.

2. Setting Up the Example Project

Open Visual Studio and create a new WPF application project. Name the project ProductDisplayApp.

2.1 Project Structure

After creating the project, the basic folder structure is as follows:

  • Views: Contains UI-related XAML files.
  • ViewModels: Contains ViewModel classes.
  • Models: Contains data structure and business logic classes.

2.2 Installing NuGet Packages

In this example, we will use the MVVM Light Toolkit, which is a tool that simplifies the implementation of the MVVM architecture. Install MVVM Light Toolkit from the NuGet Package Manager.

3. Creating the Model

Create a Product class that represents product information in the Models folder.

csharp
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
}

4. Writing the ViewModel

Create a ProductViewModel in the ViewModels folder to manage the product list and details of the selected product.

csharp
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.ObjectModel;
using System.Linq;

public class ProductViewModel : ViewModelBase
{
    private ObservableCollection _products;
    private Product _selectedProduct;

    public ObservableCollection Products
    {
        get => _products;
        set => Set(ref _products, value);
    }

    public Product SelectedProduct
    {
        get => _selectedProduct;
        set => Set(ref _selectedProduct, value);
    }

    public ProductViewModel()
    {
        LoadProducts();
    }

    private void LoadProducts()
    {
        Products = new ObservableCollection
        {
            new Product { Id = 1, Name = "Product 1", Description = "Description of Product 1", Price = 10.99M },
            new Product { Id = 2, Name = "Product 2", Description = "Description of Product 2", Price = 15.49M },
            new Product { Id = 3, Name = "Product 3", Description = "Description of Product 3", Price = 7.99M }
        };
    }
}

5. Writing the View (XAML)

Now, let’s write the view to display the product list and details. Open the MainWindow.xaml file and add the following code.

xml

    
        
    
    
        
            
            
        
        
        
            
            
            
            
            
            
            
        
    

6. Running and Testing

When you run the program, the product list will be displayed on the left side and the details of the selected product will appear on the right. Selecting a product from the list shows its name, description, and price.

7. Additional Feature: Integrating with a Database

Now, let’s add a simple feature to integrate the product list with a database. We will use Entity Framework. Run the following command in the package manager console to install it:

bash
Install-Package EntityFramework

Then, add database-related properties above the Product class and create a DbContext class to load the product list from the database.

7.1 Setting Up DbContext

csharp
using System.Data.Entity;

public class ProductContext : DbContext
{
    public DbSet Products { get; set; }
}

7.2 Updating Product Load

Update the LoadProducts method of the ViewModel class with code to load products from the database.

csharp
private void LoadProducts()
{
    using (var context = new ProductContext())
    {
        Products = new ObservableCollection(context.Products.ToList());
    }
}

8. Exception Handling and Enhancing User Experience

Add exception handling for cases where the user has not selected a product. Also, add handling for when the selected product is null.

csharp
public Product SelectedProduct
{
    get => _selectedProduct;
    set
    {
        if (Set(ref _selectedProduct, value))
        {
            // Notify property change for the details
            RaisePropertyChanged(nameof(SelectedProduct));
        }
    }
}

9. Conclusion

In this tutorial, we learned how to structure a WPF application using the MVVM pattern. Through the entire process of displaying a product list in the UI and representing the details of the selected product, we experienced the advantages of MVVM and WPF’s data binding capabilities. This structure can enhance the maintainability and scalability of the application.

10. Next Steps

Moreover, additional functionalities such as connecting to the database, creating and modifying products through user input, etc., can be added to expand the application. We encourage you to continue developing more robust and flexible applications by utilizing the MVVM pattern to separate business logic from the UI.

WPF Development, Animation

Animation is a very important element in WPF (Windows Presentation Foundation). This technology makes the user interface more attractive and interactive, providing an enhanced experience for users. In this article, we will explore the basic concepts of WPF animation, various animation techniques, and how to implement animations through example code.

1. Concept of WPF Animation

In WPF, animation is the process of changing the properties of UI elements over time. Animations can exist in various forms and are mainly used to adjust position, size, rotation, and opacity (transparency). In WPF, animations are mainly implemented using Storyboard. A Storyboard defines the sequence of animations and can include various animation effects.

2. Types of WPF Animation

The types of animations that can be used in WPF are as follows:

  • DoubleAnimation: Animates numeric properties. For example, you can change size, position, rotation, etc.
  • ColorAnimation: Animates colors. It can create an effect where the color of UI elements changes.
  • PointAnimation: Animates the point position (location in 2D space).
  • ObjectAnimationUsingKeyFrames: Animates properties using keyframes. It can easily create complex animations by shifting between several different values.
  • Storyboard: A container that allows you to combine multiple animations to operate together.

3. Basic Animation Example

Here, we will create a simple WPF animation example. This example shows an animation where the button’s size increases and then returns to its original size when clicked.

3.1 MainWindow.xaml File

            
                <Window x:Class="WpfAnimationExample.MainWindow"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        Title="WPF Animation Example" Height="200" Width="300">
                    < Grid>
                        < Button Name="AnimatedButton" Content="Click Me"
                                   Width="100" Height="50"
                                   Click="AnimatedButton_Click">
                        < Button.Triggers>
                            < EventTrigger RoutedEvent="Button.Click">
                                < BeginStoryboard>
                                    < Storyboard>
                                        < DoubleAnimation Storyboard.TargetProperty="Width"
                                                         From="100" To="150" Duration="0:0:0.2"
                                                         AutoReverse="True" />
                                        < DoubleAnimation Storyboard.TargetProperty="Height"
                                                         From="50" To="75" Duration="0:0:0.2"
                                                         AutoReverse="True" />
                                    < /Storyboard>
                                < /BeginStoryboard>
                            < /EventTrigger>
                        < /Button.Triggers>
                        < /Button>
                    < /Grid>
                < /Window>
            
        

3.2 MainWindow.xaml.cs File

            
                using System.Windows;
                
                namespace WpfAnimationExample
                {
                    public partial class MainWindow : Window
                    {
                        public MainWindow()
                        {
                            InitializeComponent();
                        }
                    }
                }
            
        

This example performs an animation where the button grows to 1.5 times its size and then returns to its original size when clicked. By using the AutoReverse property, you can set the animation to automatically reverse after it finishes.

4. In-Depth Understanding of Animation

In WPF, animations can be made more complex using KeyFrames. For example, you can implement animations with varying speeds using DoubleAnimationUsingKeyFrames.

4.1 Example Using KeyFrame

            
                <Button Name="AnimatedButton" Content="Animate Me!"
                         Width="200" Height="100">
                    < Button.Triggers>
                        < EventTrigger RoutedEvent="Button.Click">
                            < BeginStoryboard>
                                < Storyboard>
                                    < DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Height">
                                        < LinearDoubleKeyFrame Value="150" KeyTime="0:0:1"/>
                                        < SineDoubleKeyFrame Value="100" KeyTime="0:0:2"/>
                                        < LinearDoubleKeyFrame Value="50" KeyTime="0:0:3"/>
                                    < /DoubleAnimationUsingKeyFrames>
                                < /Storyboard>
                            < /BeginStoryboard>
                        < /EventTrigger>
                    < /Button.Triggers>
                < /Button>
            
        

In the above example, when the button is clicked, an animation is defined that changes the button’s height from 150 to 100, and finally to 50. Each KeyFrame defines how the property changes at a specific time during the animation.

5. Properties of Animation

WPF animations can be adjusted in more detail through various properties. Below are the main animation properties:

  • Duration: The time over which the animation occurs. It is defined in TimeSpan format.
  • RepeatBehavior: Defines how the animation repeats its behavior. For example, setting Forever will repeat it indefinitely.
  • FillBehavior: Determines what happens to the target property after the animation ends. The basic options are HoldEnd and Stop.
  • Storyboard.TargetProperty: Specifies the property to which the animation will apply. This property can be specified through PropertyPath.

6. Advanced Animation Techniques

WPF also provides the ability to implement conditional animations using triggers and styles. For instance, specific animations can be applied only while the mouse is over a UI element.

6.1 Mouse Over Animation

            
                <Button Content="Hover over me!" Width="200" Height="100">
                    <Button.Style>
                        <Style TargetType="Button">
                            <Setter Property="Background" Value="LightGray"/>
                            <Style.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Trigger.EnterActions>
                                        <BeginStoryboard>
                                            <Storyboard>
                                                <ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)"
                                                                To="Red" Duration="0:0:0.5" />
                                            </Storyboard>
                                        </BeginStoryboard>
                                    </Trigger.EnterActions>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Button.Style>
                </Button>
            
        

The above example applies an animation that changes the background color to red when the mouse hovers over the button. This provides a better interaction for the user.

7. Animation Performance

When using animations, performance must also be considered. WPF animations support GPU acceleration, but executing too many animations simultaneously can degrade rendering performance. Here are some tips for optimizing animation performance:

  • Minimize the number of animations: Ensure that too many animations do not run at once.
  • Static UI elements: Make unnecessary animations on UI elements static to reduce rendering overhead.
  • Use GPU acceleration: Set RenderOptions.BitmapScalingMode to leverage GPU acceleration.

8. Conclusion

WPF animations are a powerful tool to make user interfaces more attractive. From basic animations to advanced animation techniques, we have explored how to implement animations in WPF through various examples. Since animations are an important element that can enhance user experience and promote interaction, please utilize them appropriately to develop attractive applications.

WPF Development, Spaghetti Code

WPF (Windows Presentation Foundation) is a UI framework developed by Microsoft, and it is a powerful tool for desktop application development. However, WPF development is prone to spaghetti code. Spaghetti code refers to a complex code structure that is difficult to read and maintain, which can hinder development productivity and negatively affect code quality.

What is spaghetti code?

Spaghetti code refers to a program structure that is abnormally tangled. It usually means that the dependencies between various functions or methods are high, making it difficult to follow the flow of the code. This can lead to difficulties during debugging or when adding features, and ultimately diminish the team’s work efficiency. This issue is particularly pronounced in event-driven programming like WPF.

Causes of spaghetti code

  • Poor architectural choices: If you do not adhere to MVC (Model-View-Controller) or MVVM (Model-View-ViewModel) patterns, the components can get tangled with each other.
  • Inefficient state management: If the method of managing the UI state is inefficient, the code can become complex and dependencies arise.
  • Use of global state: Using global variables accessible throughout the application can lead to unexpected bugs.
  • Excessive event handling: Overusing events can increase the complexity of the code.

How to prevent spaghetti code in WPF

To prevent spaghetti code, it is necessary to adhere to several principles. Proper application of these principles can enhance code readability and improve maintainability.

1. Utilize the MVVM pattern

The MVVM pattern is a particularly effective architectural design pattern in WPF. MVVM separates the application into three components: Model, View, and ViewModel, clarifying each role. This separation allows for easier maintenance by decoupling UI from business logic.


        // Model
        public class UserModel
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }

        // ViewModel
        public class UserViewModel : INotifyPropertyChanged
        {
            private UserModel user;
            public UserModel User
            {
                get { return user; }
                set { user = value; OnPropertyChanged(nameof(User)); }
            }

            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged(string propertyName) =>
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    

2. Use Commands

In WPF, you can define commands through the ICommand interface and relay events to the UI elements. This enhances code readability and aids in the separation of UI and logic.


        public class RelayCommand : ICommand
        {
            private readonly Action execute;
            private readonly Predicate canExecute;

            public RelayCommand(Action execute, Predicate canExecute = null)
            {
                this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
                this.canExecute = canExecute;
            }

            public event EventHandler CanExecuteChanged
            {
                add { CommandManager.RequerySuggested += value; }
                remove { CommandManager.RequerySuggested -= value; }
            }

            public bool CanExecute(object parameter) => canExecute == null || canExecute(parameter);
            public void Execute(object parameter) => execute(parameter);
        }
    

3. Data Binding

Data binding is one of the most powerful features of WPF. You should leverage binding to ensure smooth data flow between View and ViewModel. This reduces dependencies between UI code and business logic, maintaining code consistency.


        // XAML
        
    

4. Organized project structure

Clearly defining the project structure and segregating components by functionality can enhance maintainability. For instance, using folders like Models, ViewModels, and Views organizes files according to their roles. Such an approach makes code navigation and maintenance easier.

5. Code reviews and refactoring

Regular code reviews and refactoring are another important way to prevent spaghetti code. Through code reviews, team members can exchange feedback and promptly address discovered issues. Refactoring helps tidy up code, reduce duplication, and improve readability.

Example of spaghetti code

The code below is a simple example that illustrates how a spaghetti code structure can manifest in WPF. Here, a simple function to input and save user information is implemented, but the functionalities are too intertwined, making maintenance difficult.


        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }

            private void SaveUser()
            {
                var name = txtName.Text;
                var age = int.Parse(txtAge.Text);
                // In-memory database save logic here...
                MessageBox.Show("User saved.");
            }

            private void btnSave_Click(object sender, RoutedEventArgs e)
            {
                SaveUser();
            }
        }
    

The above code mixes the user information input UI and business logic within a single class. This reduces the independence of each functionality and can complicate changes or feature additions.

Conclusion

Spaghetti code is something to avoid in WPF development. Proper architectural patterns, use of commands, data binding, an organized project structure, and code reviews and refactoring greatly help prevent spaghetti code and enhance code quality. Adhering to and implementing these principles can significantly improve the quality of WPF applications. It is always important to keep the code clean.

WPF Development, Style

Windows Presentation Foundation (WPF) is a powerful framework for building rich, interactive user interfaces. One of the many features of WPF is the ability to easily change the appearance and feel of the UI by leveraging various styles and themes. This article provides detailed explanations on how to apply styles in WPF development, along with several examples for practical application.

1. Concept of WPF Styles

Styles are an essential element of WPF, providing a way to consistently set the properties of various visual elements that make up the user interface. By using styles, you can define the appearance of UI elements and reuse these definitions to reduce code duplication. For example, you can apply the same style to all elements such as buttons, text boxes, and check boxes, creating a unified visual effect.

2. Basic Structure of Styles

In WPF, styles are primarily defined using the Style element. Each style has the following basic structure:

<Style x:Key="MyButtonStyle" TargetType="Button">
    <Setter Property="Background" Value="LightBlue"/>
    <Setter Property="Foreground" Value="White"/>
    <Setter Property="FontSize" Value="16"/>
    <Setter Property="Padding" Value="10"/>
</Style>

This style is named “MyButtonStyle” and targets the Button. You can customize the appearance of the button by setting various properties.

3. Applying Styles

To apply a defined style to a UI element, simply assign the style to the element’s Style property. For example:

<Button Style="{StaticResource MyButtonStyle}" Content="Click me"/>

In the example above, the “MyButtonStyle” style is applied to the Button. Styles can be reused easily like this.

4. Utilizing Multiple Styles and Triggers

You can use the Trigger element to define properties in styles that change based on conditions. This allows you to add various visual effects based on the state of UI elements.

<Style x:Key="MyButtonStyleWithTrigger" TargetType="Button">
    <Setter Property="Background" Value="LightBlue"/>
    <Setter Property="Foreground" Value="White"/>
    <Setter Property="FontSize" Value="16"/>

    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="DarkBlue"/>
        </Trigger>
    </Style.Triggers>
</Style>

In the style example above, the button’s background color changes when the mouse is over it. By applying state-based styles like this, user interactions become smoother.

5. Utilizing Resource Dictionaries

Style definitions can be stored in a resource dictionary, allowing for systematic management of styles in large projects. By using resource dictionaries, styles can be shared across multiple XAML files, facilitating easier maintenance.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="MyButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="LightGreen"/>
        <Setter Property="Foreground" Value="Black"/>
    </Style>
</ResourceDictionary>

6. Defining and Applying Themes and Styles in XAML

Defining and applying themes in XAML is one of the ways to maximize user experience. By grouping multiple styles into themes, you can enhance consistency across the entire application. Below is an example of setting up a default theme:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Themes/LightTheme.xaml"/>
            <ResourceDictionary Source="Themes/DarkTheme.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

This way, multiple themes can be switched dynamically. If theme files are managed as resource dictionaries, they can be easily switched as needed in the application.

7. Conclusion

Styles and themes in WPF are powerful tools for UI development. Utilizing them allows for consistency in user interfaces, reduction of code duplication, and easier maintenance. Apply the knowledge gained above to add great styles and themes to your WPF applications.

In particular, leveraging various triggers and resource dictionaries can help manage complex UIs. A thoughtful design considering user interactions will significantly enhance user experience.

WPF Development, Converter

Today, we will learn how to develop a converter using WPF (Windows Presentation Foundation).
WPF is a framework provided by Microsoft for developing GUI applications and offers powerful features for implementing digital user interfaces.
In this tutorial, we will start from the basic concepts of WPF and explain in detail the process of implementing the functionality of the converter.

1. What is WPF?

WPF is part of the .NET framework and is used to implement advanced user interfaces.
One of the main features of WPF is the ability to define the UI using XAML (Extensible Application Markup Language).
Using XAML allows you to describe UI components intuitively and can be used in conjunction with .NET languages like C# to implement logic.
This makes it advantageous for developing complex UIs with WPF.

2. Overview of the Converter Application

The converter we will develop in this tutorial is a small application that performs conversions between two formats.
For example, it will provide temperature conversion (Celsius to Fahrenheit and Fahrenheit to Celsius) and length conversion (meters to feet and feet to meters).
Users can enter values in the input field and click the convert button to see the results.

3. Setting Up the Development Environment

To develop a WPF application, Visual Studio is required.
The Visual Studio Community version is free to use and can be easily installed to start working on WPF applications.
After installation, create a new project and select WPF App (.NET Core or .NET Framework).

4. Project Structure

The basic structure of a WPF project is as follows:

  • MainWindow.xaml: A XAML file that defines the UI elements.
  • MainWindow.xaml.cs: A C# code file that implements the UI logic.
  • App.xaml: Defines the application’s entry point and resources.

5. Designing the UI with XAML


        <Window x:Class="ConverterApp.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                Title="Converter" Height="350" Width="525">
            <Grid>
                <Label Content="Temperature Converter" FontSize="24" HorizontalAlignment="Center" Margin="10"/>
                <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
                    <TextBox x:Name="InputValue" Width="200" Margin="5" />
                    <ComboBox x:Name="InputUnit" Width="200" Margin="5">
                        <ComboBoxItem Content="Celsius" />
                        <ComboBoxItem Content="Fahrenheit" />
                    </ComboBox>
                    <Button Content="Convert" Width="200" Margin="5" Click="ConvertButton_Click"/>
                    <TextBlock x:Name="ResultText" FontSize="16" Margin="5"/>
                </StackPanel>
            </Grid>
        </Window>
    

The above XAML code creates a basic UI. It takes temperature input from the user and provides a combo box to select either Celsius or Fahrenheit.
When the convert button is clicked, the conversion results are displayed based on the input value.

6. Implementing C# Code

Below is the C# code that implements the conversion logic. Write this in the MainWindow.xaml.cs file.


        using System;
        using System.Windows;

        namespace ConverterApp
        {
            public partial class MainWindow : Window
            {
                public MainWindow()
                {
                    InitializeComponent();
                }

                private void ConvertButton_Click(object sender, RoutedEventArgs e)
                {
                    double inputValue;
                    bool isValid = double.TryParse(InputValue.Text, out inputValue);
                    
                    if (!isValid)
                    {
                        ResultText.Text = "Please enter a valid number.";
                        return;
                    }

                    if (InputUnit.SelectedIndex == 0) // Celsius
                    {
                        double result = (inputValue * 9 / 5) + 32;
                        ResultText.Text = $"{inputValue} °C is {result} °F.";
                    }
                    else // Fahrenheit
                    {
                        double result = (inputValue - 32) * 5 / 9;
                        ResultText.Text = $"{inputValue} °F is {result} °C.";
                    }
                }
            }
        }
    

In the above C# code, we define the ConvertButton_Click method, which is called when the convert button is clicked.
It reads the value entered by the user and performs the appropriate conversion based on the selected unit.
If the input value is not valid, a warning message is displayed to inform the user.

7. Adding Length Converter

Next, let’s add the length conversion feature. Modify the UI to add a length conversion button and implement the conversion logic.


        <Label Content="Length Converter" FontSize="24" HorizontalAlignment="Center" Margin="10"/>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBox x:Name="LengthInputValue" Width="200" Margin="5" />
            <ComboBox x:Name="LengthInputUnit" Width="200" Margin="5">
                <ComboBoxItem Content="Meters" />
                <ComboBoxItem Content="Feet" />
            </ComboBox>
            <Button Content="Convert" Width="200" Margin="5" Click="LengthConvertButton_Click"/>
            <TextBlock x:Name="LengthResultText" FontSize="16" Margin="5"/>
        </StackPanel>
    

After adding the length conversion UI in a similar fashion, add the length conversion logic to the MainWindow.xaml.cs.


        private void LengthConvertButton_Click(object sender, RoutedEventArgs e)
        {
            double inputLengthValue;
            bool isLengthValid = double.TryParse(LengthInputValue.Text, out inputLengthValue);
            
            if (!isLengthValid)
            {
                LengthResultText.Text = "Please enter a valid number.";
                return;
            }

            if (LengthInputUnit.SelectedIndex == 0) // Meters
            {
                double lengthResult = inputLengthValue * 3.28084;
                LengthResultText.Text = $"{inputLengthValue} meters is {lengthResult} feet.";
            }
            else // Feet
            {
                double lengthResult = inputLengthValue / 3.28084;
                LengthResultText.Text = $"{inputLengthValue} feet is {lengthResult} meters.";
            }
        }
    

8. Running and Testing the Application

Once all features are implemented, run the application to test if the converter works correctly.
Enter various values and units to verify that the conversions are done properly.
You can improve the UI or add additional features as needed.

9. Conclusion

In this tutorial, we have looked at the process of developing a basic converter application using WPF.
We were able to create an application that interacts with users utilizing the advantages of XAML for UI composition and C# for logic implementation.
I hope to deepen this technology through various WPF projects in the future.

10. References

Getting Started with WPF
Step-by-Step Guide to Developing WPF Applications