<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: VectoArt</title>
    <description>The latest articles on DEV Community by VectoArt (@vectoart_a497c516fac199bd).</description>
    <link>https://dev.to/vectoart_a497c516fac199bd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3545453%2Ff05acaf8-2636-4591-a476-5858a0156c88.jpg</url>
      <title>DEV Community: VectoArt</title>
      <link>https://dev.to/vectoart_a497c516fac199bd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vectoart_a497c516fac199bd"/>
    <language>en</language>
    <item>
      <title>Building a Real-Time Day Planner in WPF: From Static Design to Dynamic Data</title>
      <dc:creator>VectoArt</dc:creator>
      <pubDate>Mon, 13 Oct 2025 17:54:40 +0000</pubDate>
      <link>https://dev.to/vectoart_a497c516fac199bd/building-a-real-time-day-planner-in-wpf-from-static-design-to-dynamic-data-55ge</link>
      <guid>https://dev.to/vectoart_a497c516fac199bd/building-a-real-time-day-planner-in-wpf-from-static-design-to-dynamic-data-55ge</guid>
      <description>&lt;p&gt;See the app in action: &lt;a href="https://youtu.be/P0J6JZ73m0Q" rel="noopener noreferrer"&gt;https://youtu.be/P0J6JZ73m0Q&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Desktop applications don’t have to be sluggish or look dated. Using WPF (Windows Presentation Foundation) and C#, we can build highly responsive, modern interfaces that update their data instantly.&lt;/p&gt;

&lt;p&gt;This article walks you through the core engineering principles behind creating a beautiful, real-time Day Planner application. We’ll focus on three critical concepts that make this app work seamlessly: MVVM Fundamentals (specifically INotifyPropertyChanged), LINQ for Dynamic Data Counting, and CollectionViewSource for Instant Filtering.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The XAML Blueprint: A Responsive, Custom Layout
&lt;/h2&gt;

&lt;p&gt;A great user experience starts with a great layout. In WPF, this means mastering the Grid.&lt;/p&gt;

&lt;p&gt;Our application uses a standard two-column layout defined in MainWindow.xaml:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Column 0 (Sidebar): We assign a fixed width of 280 pixels. This ensures our navigation panel is always stable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Column 1 (Main Content): We use Width="*". The asterisk tells WPF, "Take up all available space." This is the foundation of our responsive design; as the window adjusts, the main content area resizes automatically.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Custom Styling with ControlTemplate&lt;/strong&gt;&lt;br&gt;
WPF controls are highly customizable. To achieve the clean, rounded look for our sidebar buttons and input controls, we often can’t rely on standard properties like CornerRadius (which isn't available on a base Button). Instead, we create custom Style definitions that use a ControlTemplate.&lt;/p&gt;

&lt;p&gt;This allows us to replace the default look of a control with our own structure — usually a Border—and apply properties like CornerRadius and &lt;br&gt;
custom hover Trigger effects to that internal Border.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. The Core: The Task Model and MVVM Fundamentals
&lt;/h2&gt;

&lt;p&gt;The heart of any real-time application is its data model’s ability to communicate changes to the UI.&lt;/p&gt;

&lt;p&gt;In our C# code (MainWindow.xaml.cs), we define the Task model. For this model to work dynamically, it must implement the INotifyPropertyChanged interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Magic of INotifyPropertyChanged&lt;/strong&gt;&lt;br&gt;
When a user interacts with a task — for example, by checking the IsCompleted checkbox—we need the associated UI elements (like a strikethrough text decoration or a sidebar count) to update immediately.&lt;/p&gt;

&lt;p&gt;This is achieved by invoking the OnPropertyChanged() method within the setter of any critical property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Task : INotifyPropertyChanged
{
    // ...
    public bool IsCompleted
    {
        get =&amp;gt; _isCompleted;
        set
        {
            if (_isCompleted != value)
            {
                _isCompleted = value;
                OnPropertyChanged(); // &amp;lt;-- This signals the UI to update!
            }
        }
    }
    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The ObservableCollection&lt;/strong&gt;&lt;br&gt;
Our list of tasks is held in an ObservableCollection. Unlike a standard List, the ObservableCollection notifies the UI when an item is added or removed.&lt;/p&gt;

&lt;p&gt;By combining INotifyPropertyChanged (for internal task changes) and ObservableCollection (for list changes), we ensure the entire application always reflects the current state of our data.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Real-Time Magic: Dynamic Counting with LINQ
&lt;/h2&gt;

&lt;p&gt;The sidebar counters for “My day” or “Work” are powered by the RecalculateCounts method, which runs every time a task is added, removed, or changed.&lt;/p&gt;

&lt;p&gt;To filter and count the tasks efficiently, we use LINQ (Language Integrated Query). This allows us to write powerful, SQL-like queries directly in C#.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Counting Logic&lt;/strong&gt;&lt;br&gt;
The logic is remarkably clean. We first find all incomplete tasks, then apply specific date or category filters to that subset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void RecalculateCounts()
{
    var today = DateTime.Today;
    var nextWeek = today.AddDays(7);

    // 1. Get only incomplete tasks
    var incompleteTasks = Tasks.Where(t =&amp;gt; !t.IsCompleted).ToList();

    // 2. Count for 'My day'
    MyDayCount = incompleteTasks.Count(t =&amp;gt; t.DueDate.Date == today);

    // 3. Count for 'Next 7 days'
    NextSevenDaysCount = incompleteTasks.Count(t =&amp;gt; t.DueDate.Date &amp;gt; today &amp;amp;&amp;amp; t.DueDate.Date &amp;lt;= nextWeek);

    // 4. Update the bound strings for the XAML buttons
    MyDayButtonContent = $"My day ({MyDayCount})";
    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the user sees an immediate numerical change in the sidebar as soon as they add a new task or modify a due date.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Advanced Interactivity: Converters and Filtering
&lt;/h2&gt;

&lt;p&gt;Two final components bring polish to the application: custom value converters and dynamic list filtering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual Feedback with IValueConverter&lt;/strong&gt;&lt;br&gt;
To create the strikethrough effect when a task is checked, we cannot simply bind the task’s IsCompleted boolean directly to the TextBlock.TextDecorations property. We need an intermediary to translate a bool into the WPF type TextDecorations.Strikethrough.&lt;/p&gt;

&lt;p&gt;This is the job of the CompletedToStrikeConverter class, which implements IValueConverter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Input: true (The task is completed).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Converter Output: TextDecorations.Strikethrough (The WPF style).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern keeps our C# logic clean and isolates presentation rules in a reusable class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic List Filtering with CollectionViewSource&lt;/strong&gt;&lt;br&gt;
The final piece of real-time functionality is making the task list change instantly when a sidebar button is clicked. We accomplish this using a CollectionViewSource.&lt;/p&gt;

&lt;p&gt;Instead of binding our main ItemsControl directly to the Tasks collection, we bind it to a View of that collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Inside the button click handler:
TasksViewSource.View.Filter = TasksViewSource_Filter;
TasksViewSource.View.Refresh();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By combining powerful WPF features, we transformed a static to-do list concept into a fluid, real-time desktop application. We leveraged MVVM patterns to keep our data and UI synchronized, used LINQ for efficient data processing, and employed CollectionViewSource for elegant filtering.&lt;/p&gt;

&lt;p&gt;The result is a responsive, modern Day Planner ready to run on any Windows desktop!&lt;/p&gt;

&lt;p&gt;If you’d like to dive deeper, the natural next steps are adding features like persistence (saving data to a file or database) and implementing inline task editing and deletion. Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complete Source Code Reference&lt;/strong&gt;&lt;br&gt;
For a complete and runnable project, here are the contents of the required files.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;MainWindow.xaml.cs (C# Logic and ViewModel)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace DayPlanner
{
    public class Task : INotifyPropertyChanged
    {
        private string _description;
        private string _listName;
        private string _category;
        private DateTime _dueDate;
        private bool _isCompleted;

        public string Description
        {
            get =&amp;gt; _description;
            set { _description = value; OnPropertyChanged(nameof(Description)); }
        }
        public string ListName
        {
            get =&amp;gt; _listName;
            set { _listName = value; OnPropertyChanged(nameof(ListName)); }
        }
        public string Category
        {
            get =&amp;gt; _category;
            set { _category = value; OnPropertyChanged(nameof(Category)); }
        }
        // NEW: Due Date property
        public DateTime DueDate
        {
            get =&amp;gt; _dueDate;
            set { _dueDate = value; OnPropertyChanged(nameof(DueDate)); }
        }

        public bool IsCompleted
        {
            get =&amp;gt; _isCompleted;
            set
            {
                // Set the backing field
                _isCompleted = value;
                // Notify UI that IsCompleted property changed
                OnPropertyChanged(nameof(IsCompleted));
                // Re-notify to update the TextDecorations (StrikeThrough) on the TextBlock
                OnPropertyChanged(nameof(Description));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }



    public class CompletedToStrikeConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool isCompleted &amp;amp;&amp;amp; isCompleted)
            {
                // Returns a StrikeThrough decoration for completed tasks
                return TextDecorations.Strikethrough;
            }
            return null; // No decoration
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // Two-way conversion not necessary for this property
            return Binding.DoNothing;
        }
    }

    public class CompletedToFillConverter : IValueConverter
    {
        private static readonly SolidColorBrush CompletedBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#B3D3B9")); // Light green
        private static readonly SolidColorBrush IncompleteBrush = new SolidColorBrush(Colors.Transparent);

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is bool isCompleted &amp;amp;&amp;amp; isCompleted)
            {
                return CompletedBrush;
            }
            return IncompleteBrush;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // Two-way conversion not necessary for this property
            return Binding.DoNothing;
        }
    }
    /// &amp;lt;summary&amp;gt;
    /// Interaction logic for MainWindow.xaml
    /// &amp;lt;/summary&amp;gt;
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string _currentDateTimeString;
        public string CurrentDateTimeString
        {
            get { return _currentDateTimeString; }
            set
            {
                _currentDateTimeString = value;
                OnPropertyChanged(nameof(CurrentDateTimeString));
            }
        }

        private string _myDayButtonContent;
        public string MyDayButtonContent
        {
            get =&amp;gt; _myDayButtonContent;
            set { _myDayButtonContent = value; OnPropertyChanged(); }
        }

        private string _nextSevenDaysButtonContent;
        public string NextSevenDaysButtonContent
        {
            get =&amp;gt; _nextSevenDaysButtonContent;
            set { _nextSevenDaysButtonContent = value; OnPropertyChanged(); }
        }

        private string _allMyTasksButtonContent;
        public string AllMyTasksButtonContent
        {
            get =&amp;gt; _allMyTasksButtonContent;
            set { _allMyTasksButtonContent = value; OnPropertyChanged(); }
        }

        private string _personalButtonContent;
        public string PersonalButtonContent
        {
            get =&amp;gt; _personalButtonContent;
            set { _personalButtonContent = value; OnPropertyChanged(); }
        }

        private string _workButtonContent;
        public string WorkButtonContent
        {
            get =&amp;gt; _workButtonContent;
            set { _workButtonContent = value; OnPropertyChanged(); }
        }

        private string _groceryListButtonContent;
        public string GroceryListButtonContent
        {
            get =&amp;gt; _groceryListButtonContent;
            set { _groceryListButtonContent = value; OnPropertyChanged(); }
        }
        private string _newTaskCategory;
        public string NewTaskCategory
        {
            get =&amp;gt; _newTaskCategory;
            set
            {
                if (_newTaskCategory != value)
                {
                    _newTaskCategory = value;
                    OnPropertyChanged();
                }
            }
        }
        private DateTime _newTaskDueDate;
        public DateTime NewTaskDueDate
        {
            get =&amp;gt; _newTaskDueDate;
            set
            {
                if (_newTaskDueDate != value)
                {
                    _newTaskDueDate = value;
                    OnPropertyChanged();
                }
            }
        }
        public ObservableCollection&amp;lt;Task&amp;gt; Tasks { get; set; }
        public ObservableCollection&amp;lt;string&amp;gt; AvailableCategories { get; set; }


        private int _myDayCount;
        public int MyDayCount
        {
            get =&amp;gt; _myDayCount;
            set
            {
                if (_myDayCount != value)
                {
                    _myDayCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _nextSevenDaysCount;
        public int NextSevenDaysCount
        {
            get =&amp;gt; _nextSevenDaysCount;
            set
            {
                if (_nextSevenDaysCount != value)
                {
                    _nextSevenDaysCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _allMyTasksCount;
        public int AllMyTasksCount
        {
            get =&amp;gt; _allMyTasksCount;
            set
            {
                if (_allMyTasksCount != value)
                {
                    _allMyTasksCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _personalCount;
        public int PersonalCount
        {
            get =&amp;gt; _personalCount;
            set
            {
                if (_personalCount != value)
                {
                    _personalCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _workCount;
        public int WorkCount
        {
            get =&amp;gt; _workCount;
            set
            {
                if (_workCount != value)
                {
                    _workCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _groceryListCount;
        public int GroceryListCount
        {
            get =&amp;gt; _groceryListCount;
            set
            {
                if (_groceryListCount != value)
                {
                    _groceryListCount = value;
                    OnPropertyChanged();
                }
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            InitializeCategoryAndDateSelection();

            InitializeTasks();

            CurrentDateTimeString = DateTime.Now.ToString("dddd, MMMM dd, yyyy");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void InitializeCategoryAndDateSelection()
        {
            AvailableCategories = new ObservableCollection&amp;lt;string&amp;gt;
            {
                "Personal", "Work", "Grocery List", "Study", "Finance" // Example categories
            };
            // Set initial values that will be bound to the new task input controls
            NewTaskCategory = AvailableCategories.First();
            NewTaskDueDate = DateTime.Today.AddDays(1);
        }

        private void InitializeTasks()
        {
            // Use ObservableCollection for dynamic updates
            Tasks = new ObservableCollection&amp;lt;Task&amp;gt;();
        }

        private void AddTaskButton_Click(object sender, RoutedEventArgs e)
        {
            // 1. Get the description from the TextBox
            string taskDescription = NewTaskTextBox.Text.Trim();
            string placeholderText = "Add a new task...";

            // 2. Validate input and ensure it's not the placeholder text
            if (!string.IsNullOrEmpty(taskDescription) &amp;amp;&amp;amp; taskDescription != placeholderText)
            {
                // 3. Create a new Task object
                // New tasks default to "My lists &amp;gt; Personal" and incomplete
                Task newTask = new Task
                {
                    Description = taskDescription,
                    ListName = "My lists",
                    Category = NewTaskCategory, // Use the selected category (bound from ComboBox)
                    DueDate = NewTaskDueDate,   // Use the selected date (bound from DatePicker)
                    IsCompleted = false
                };

                // 4. Add the new task to the ObservableCollection. 
                // The UI (ItemsControl) will automatically update in real-time.
                Tasks.Add(newTask);

                // 5. Clear the TextBox and reset the placeholder text
                NewTaskTextBox.Text = placeholderText;
                NewTaskTextBox.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AAAAAA"));

                // Reset category and date selections back to defaults
                NewTaskCategory = AvailableCategories[0];
                NewTaskDueDate = DateTime.Today;

                // 6. Recalculate counts to reflect the new task
                RecalculateCounts();
            }

        }
        private void NewTaskTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            if (NewTaskTextBox.Text == "Add a new task...")
            {
                NewTaskTextBox.Text = string.Empty;
                // Change color from placeholder grey to normal text color
                NewTaskTextBox.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333"));
            }
        }

        private void NewTaskTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(NewTaskTextBox.Text.Trim()))
            {
                NewTaskTextBox.Text = "Add a new task...";
                NewTaskTextBox.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AAAAAA"));
            }
        }

        private void Tasks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Subscribe to PropertyChanged for newly added tasks
            if (e.NewItems != null)
            {
                foreach (Task task in e.NewItems)
                {
                    task.PropertyChanged += Task_PropertyChanged;
                }
            }

            // Unsubscribe from PropertyChanged for removed tasks
            if (e.OldItems != null)
            {
                foreach (Task task in e.OldItems)
                {
                    task.PropertyChanged -= Task_PropertyChanged;
                }
            }
            RecalculateCounts();
        }

        private void Task_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // Only recalculate if properties that affect filtering change
            if (e.PropertyName == nameof(Task.IsCompleted) || e.PropertyName == nameof(Task.DueDate) || e.PropertyName == nameof(Task.Category))
            {
                RecalculateCounts();
            }
        }

        private void RecalculateCounts()
        {
            if (Tasks == null) return;
            var today = DateTime.Today;
            var nextWeek = today.AddDays(7);

            // Counts should generally only include incomplete tasks
            var incompleteTasks = Tasks.Where(t =&amp;gt; !t.IsCompleted).ToList();

            MyDayCount = incompleteTasks.Count(t =&amp;gt; t.DueDate.Date == today);

            // Next 7 days tasks: Due in the next 7 days, excluding today
            NextSevenDaysCount = incompleteTasks.Count(t =&amp;gt; t.DueDate.Date &amp;gt; today &amp;amp;&amp;amp; t.DueDate.Date &amp;lt;= nextWeek);

            AllMyTasksCount = incompleteTasks.Count(); // Count of all incomplete tasks

            // List Counts
            PersonalCount = incompleteTasks.Count(t =&amp;gt; t.Category == "Personal");
            WorkCount = incompleteTasks.Count(t =&amp;gt; t.Category == "Work");
            GroceryListCount = incompleteTasks.Count(t =&amp;gt; t.Category == "Grocery List");

            // Update the formatted string properties for button content
            MyDayButtonContent = $"My day ({MyDayCount})";
            NextSevenDaysButtonContent = $"Next 7 days ({NextSevenDaysCount})";
            AllMyTasksButtonContent = $"All my tasks ({AllMyTasksCount})";
            PersonalButtonContent = $"Personal ({PersonalCount})";
            WorkButtonContent = $"Work ({WorkCount})";
            GroceryListButtonContent = $"Grocery List ({GroceryListCount})";
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;MainWindow.xaml (WPF Layout and Styling)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Window x:Class="DayPlanner.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DayPlanner"
        xmlns:hc="https://handyorg.github.io/handycontrol"
        mc:Ignorable="d"
Title="Day Planner" 
Height="700" 
Width="1000"
FontFamily="Segoe UI"
AllowsTransparency="True"
WindowStyle="None"
Background="Transparent" WindowStartupLocation="CenterScreen"&amp;gt;


    &amp;lt;Window.Resources&amp;gt;
        &amp;lt;!-- Color Definitions --&amp;gt;
        &amp;lt;Color x:Key="PrimaryBackground"&amp;gt;#F8F8F8&amp;lt;/Color&amp;gt;
        &amp;lt;Color x:Key="SidebarBackground"&amp;gt;#FFFFFF&amp;lt;/Color&amp;gt;
        &amp;lt;Color x:Key="CardBackground"&amp;gt;#FFFFFF&amp;lt;/Color&amp;gt;
        &amp;lt;Color x:Key="AccentColor"&amp;gt;#000&amp;lt;/Color&amp;gt;
        &amp;lt;Color x:Key="TextColor"&amp;gt;#000&amp;lt;/Color&amp;gt;
        &amp;lt;Color x:Key="WhiteColor"&amp;gt;#FFFFFF&amp;lt;/Color&amp;gt;
        &amp;lt;Color x:Key="LightAccentColor"&amp;gt;#000&amp;lt;/Color&amp;gt;
        &amp;lt;Color x:Key="LightBorderColor"&amp;gt;#E8E8E8&amp;lt;/Color&amp;gt;

        &amp;lt;SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="{StaticResource PrimaryBackground}"/&amp;gt;
        &amp;lt;SolidColorBrush x:Key="SidebarBackgroundBrush" Color="{StaticResource SidebarBackground}"/&amp;gt;
        &amp;lt;SolidColorBrush x:Key="CardBackgroundBrush" Color="{StaticResource CardBackground}"/&amp;gt;
        &amp;lt;SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}"/&amp;gt;
        &amp;lt;SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/&amp;gt;
        &amp;lt;SolidColorBrush x:Key="CompletedTaskBrush" Color="ForestGreen"/&amp;gt;
        &amp;lt;SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource WhiteColor}"/&amp;gt;
        &amp;lt;SolidColorBrush x:Key="LightAccentBrush" Color="{StaticResource LightAccentColor}"/&amp;gt;
        &amp;lt;SolidColorBrush x:Key="LightBorderBrush" Color="{StaticResource LightBorderColor}"/&amp;gt;

        &amp;lt;!-- Value Converters --&amp;gt;
        &amp;lt;local:CompletedToStrikeConverter x:Key="CompletedToStrikeConverter"/&amp;gt;
        &amp;lt;local:CompletedToFillConverter x:Key="CompletedToFillConverter"/&amp;gt;

        &amp;lt;!-- Style for Sidebar Navigation Buttons --&amp;gt;
        &amp;lt;Style x:Key="SidebarButtonStyle" TargetType="Button"&amp;gt;
            &amp;lt;Setter Property="Background" Value="Transparent"/&amp;gt;
            &amp;lt;Setter Property="BorderThickness" Value="0"/&amp;gt;
            &amp;lt;Setter Property="Foreground" Value="{StaticResource WhiteBrush}"/&amp;gt;
            &amp;lt;Setter Property="FontSize" Value="14"/&amp;gt;
            &amp;lt;Setter Property="Padding" Value="12,8"/&amp;gt;
            &amp;lt;Setter Property="Margin" Value="0,2,0,2"/&amp;gt;
            &amp;lt;Setter Property="HorizontalContentAlignment" Value="Left"/&amp;gt;
            &amp;lt;Setter Property="Cursor" Value="Hand"/&amp;gt;
            &amp;lt;Setter Property="Template"&amp;gt;
                &amp;lt;Setter.Value&amp;gt;
                    &amp;lt;ControlTemplate TargetType="Button"&amp;gt;
                        &amp;lt;Border Padding="{TemplateBinding Padding}" 
                            Background="{TemplateBinding Background}" 
                            CornerRadius="8"&amp;gt;
                            &amp;lt;ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                          VerticalAlignment="{TemplateBinding VerticalAlignment}"/&amp;gt;
                        &amp;lt;/Border&amp;gt;
                        &amp;lt;ControlTemplate.Triggers&amp;gt;
                            &amp;lt;Trigger Property="IsMouseOver" Value="True"&amp;gt;
                                &amp;lt;Setter Property="Background" Value="{StaticResource LightAccentBrush}"/&amp;gt;
                            &amp;lt;/Trigger&amp;gt;
                        &amp;lt;/ControlTemplate.Triggers&amp;gt;
                    &amp;lt;/ControlTemplate&amp;gt;
                &amp;lt;/Setter.Value&amp;gt;
            &amp;lt;/Setter&amp;gt;
        &amp;lt;/Style&amp;gt;

        &amp;lt;!-- Style for the Task Completion CheckBox (styled as a circle) --&amp;gt;
        &amp;lt;Style x:Key="CircleCheckBoxStyle" TargetType="CheckBox"&amp;gt;
            &amp;lt;Setter Property="Template"&amp;gt;
                &amp;lt;Setter.Value&amp;gt;
                    &amp;lt;ControlTemplate TargetType="CheckBox"&amp;gt;
                        &amp;lt;Grid Background="Transparent"&amp;gt;
                            &amp;lt;Ellipse x:Name="CheckMark" 
                                 Width="20" 
                                 Height="20" 
                                 StrokeThickness="2" 
                                 Stroke="#DDDDDD" 
                                 Fill="Transparent" 
                                 VerticalAlignment="Center" 
                                 HorizontalAlignment="Center"/&amp;gt;
                        &amp;lt;/Grid&amp;gt;
                        &amp;lt;ControlTemplate.Triggers&amp;gt;
                            &amp;lt;!-- Visuals for Checked State --&amp;gt;
                            &amp;lt;Trigger Property="IsChecked" Value="True"&amp;gt;
                                &amp;lt;!-- When checked, fill the ellipse with the light green color --&amp;gt;
                                &amp;lt;Setter TargetName="CheckMark" Property="Fill" Value="{StaticResource CompletedTaskBrush}"/&amp;gt;
                                &amp;lt;Setter TargetName="CheckMark" Property="Stroke" Value="{StaticResource CompletedTaskBrush}"/&amp;gt;
                            &amp;lt;/Trigger&amp;gt;
                            &amp;lt;!-- Visuals for Mouse Over State --&amp;gt;
                            &amp;lt;Trigger Property="IsMouseOver" Value="True"&amp;gt;
                                &amp;lt;Setter TargetName="CheckMark" Property="Stroke" Value="{StaticResource AccentBrush}"/&amp;gt;
                            &amp;lt;/Trigger&amp;gt;
                        &amp;lt;/ControlTemplate.Triggers&amp;gt;
                    &amp;lt;/ControlTemplate&amp;gt;
                &amp;lt;/Setter.Value&amp;gt;
            &amp;lt;/Setter&amp;gt;
        &amp;lt;/Style&amp;gt;

        &amp;lt;!-- NEW: Style for the circular Add Task Button --&amp;gt;
        &amp;lt;Style x:Key="AddButtonStyle" TargetType="Button"&amp;gt;
            &amp;lt;Setter Property="Background" Value="{StaticResource AccentBrush}"/&amp;gt;
            &amp;lt;Setter Property="Foreground" Value="{StaticResource WhiteBrush}"/&amp;gt;
            &amp;lt;Setter Property="FontWeight" Value="Bold"/&amp;gt;
            &amp;lt;Setter Property="FontSize" Value="18"/&amp;gt;
            &amp;lt;Setter Property="Width" Value="30"/&amp;gt;
            &amp;lt;Setter Property="Height" Value="30"/&amp;gt;
            &amp;lt;Setter Property="Padding" Value="0"/&amp;gt;
            &amp;lt;Setter Property="BorderThickness" Value="0"/&amp;gt;
            &amp;lt;Setter Property="Cursor" Value="Hand"/&amp;gt;
            &amp;lt;Setter Property="Template"&amp;gt;
                &amp;lt;Setter.Value&amp;gt;
                    &amp;lt;ControlTemplate TargetType="Button"&amp;gt;
                        &amp;lt;Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="15"&amp;gt;
                            &amp;lt;ContentPresenter HorizontalAlignment="Center" 
                                          VerticalAlignment="Center"/&amp;gt;
                        &amp;lt;/Border&amp;gt;
                        &amp;lt;ControlTemplate.Triggers&amp;gt;
                            &amp;lt;Trigger Property="IsMouseOver" Value="True"&amp;gt;
                                &amp;lt;Setter Property="Background" Value="{StaticResource LightAccentBrush}"/&amp;gt;
                            &amp;lt;/Trigger&amp;gt;
                        &amp;lt;/ControlTemplate.Triggers&amp;gt;
                    &amp;lt;/ControlTemplate&amp;gt;
                &amp;lt;/Setter.Value&amp;gt;
            &amp;lt;/Setter&amp;gt;
        &amp;lt;/Style&amp;gt;


        &amp;lt;!-- DataTemplate for a Task Item --&amp;gt;
        &amp;lt;DataTemplate x:Key="TaskTemplate"&amp;gt;
            &amp;lt;Border Background="{StaticResource CardBackgroundBrush}" 
                CornerRadius="12" 
                Margin="10"
                Padding="15"
                Width="Auto"
                HorizontalAlignment="Stretch"
                BorderThickness="1"
                BorderBrush="#E8E8E8"&amp;gt;
                &amp;lt;Border.Effect&amp;gt;
                    &amp;lt;!-- Soft shadow to mimic elevation --&amp;gt;
                    &amp;lt;DropShadowEffect ShadowDepth="0" 
                                  Color="#DDDDDD" 
                                  Opacity="0.5" 
                                  BlurRadius="10"/&amp;gt;
                &amp;lt;/Border.Effect&amp;gt;
                &amp;lt;Grid&amp;gt;
                    &amp;lt;Grid.ColumnDefinitions&amp;gt;
                        &amp;lt;ColumnDefinition Width="Auto"/&amp;gt;
                        &amp;lt;ColumnDefinition Width="*"/&amp;gt;
                    &amp;lt;/Grid.ColumnDefinitions&amp;gt;

                    &amp;lt;!-- INTERACTIVE Checkbox (Bound to IsCompleted) --&amp;gt;
                    &amp;lt;CheckBox Grid.Column="0" 
                          Style="{StaticResource CircleCheckBoxStyle}"
                          IsChecked="{Binding IsCompleted, Mode=TwoWay}"
                          VerticalAlignment="Center" 
                          Margin="0,0,15,0"/&amp;gt;

                    &amp;lt;StackPanel Grid.Column="1"&amp;gt;
                        &amp;lt;!-- Task Metadata (e.g., "My lists &amp;gt; Work") --&amp;gt;
                        &amp;lt;StackPanel Orientation="Horizontal" Opacity="0.7" Margin="0,0,0,2"&amp;gt;
                            &amp;lt;TextBlock Text="{Binding ListName}" FontSize="11" Foreground="{StaticResource TextBrush}"/&amp;gt;
                            &amp;lt;TextBlock Text=" &amp;gt; " FontSize="11" Foreground="{StaticResource TextBrush}"/&amp;gt;
                            &amp;lt;TextBlock Text="{Binding Category}" FontSize="11" FontWeight="SemiBold" Foreground="{StaticResource TextBrush}"/&amp;gt;
                        &amp;lt;/StackPanel&amp;gt;

                        &amp;lt;!-- Task Description (Bound to IsCompleted for Text Decoration) --&amp;gt;
                        &amp;lt;TextBlock Text="{Binding Description}" 
                               FontSize="15" 
                               TextWrapping="Wrap"
                               TextDecorations="{Binding IsCompleted, Converter={StaticResource CompletedToStrikeConverter}}"
                               Foreground="{StaticResource TextBrush}"/&amp;gt;
                    &amp;lt;/StackPanel&amp;gt;
                    &amp;lt;!-- NEW: Due Date Display --&amp;gt;
                    &amp;lt;TextBlock Grid.Column="2"
                           Text="{Binding DueDate, StringFormat='MMM dd'}" 
                           FontSize="12"
                           Opacity="0.6"
                           Margin="0,0,10,0"
                           VerticalAlignment="Top"
                           HorizontalAlignment="Right"/&amp;gt;
                &amp;lt;/Grid&amp;gt;
            &amp;lt;/Border&amp;gt;
        &amp;lt;/DataTemplate&amp;gt;
    &amp;lt;/Window.Resources&amp;gt;
    &amp;lt;Border CornerRadius="25" Background="{StaticResource PrimaryBackgroundBrush}"&amp;gt;
        &amp;lt;Grid&amp;gt;
            &amp;lt;Grid.ColumnDefinitions&amp;gt;
                &amp;lt;!-- Sidebar Column (fixed width) --&amp;gt;
                &amp;lt;ColumnDefinition Width="280"/&amp;gt;
                &amp;lt;!-- Main Content Column --&amp;gt;
                &amp;lt;ColumnDefinition Width="*"/&amp;gt;
            &amp;lt;/Grid.ColumnDefinitions&amp;gt;

            &amp;lt;Border Grid.Column="0" 
        Background="{StaticResource AccentBrush}"
        CornerRadius="25,0,0,25"
        Padding="20,30,20,20"&amp;gt;
                &amp;lt;DockPanel&amp;gt;
                    &amp;lt;Border DockPanel.Dock="Top" CornerRadius="10" Background="White" &amp;gt;
                        &amp;lt;StackPanel Margin="5,5,5,5"&amp;gt;
                            &amp;lt;!-- User Profile Area --&amp;gt;
                            &amp;lt;StackPanel Orientation="Horizontal" Margin="0,0,0,0"&amp;gt;
                                &amp;lt;Ellipse Width="40" Height="40" Margin="0,0,10,0"&amp;gt;
                                    &amp;lt;Ellipse.Fill&amp;gt;
                                        &amp;lt;ImageBrush ImageSource="/Picsart_23-08-23_01-02-11-506.jpg"/&amp;gt;
                                    &amp;lt;/Ellipse.Fill&amp;gt;
                                &amp;lt;/Ellipse&amp;gt;
                                &amp;lt;StackPanel VerticalAlignment="Center"&amp;gt;
                                    &amp;lt;!-- Changed name to VectoArt --&amp;gt;
                                    &amp;lt;TextBlock Text="VectoArt" FontWeight="Bold" FontSize="16" /&amp;gt;
                                    &amp;lt;TextBlock Text="Subscribe" FontSize="12" Opacity="0.6" /&amp;gt;
                                &amp;lt;/StackPanel&amp;gt;
                            &amp;lt;/StackPanel&amp;gt;
                        &amp;lt;/StackPanel&amp;gt;
                    &amp;lt;/Border&amp;gt;
                    &amp;lt;StackPanel DockPanel.Dock="Top" Margin="0,20,0,30"&amp;gt;
                        &amp;lt;TextBlock Text="My day" FontSize="18" FontWeight="SemiBold" Margin="0,5,0,5" Foreground="{StaticResource WhiteBrush}"/&amp;gt;
                        &amp;lt;Button Content="{Binding MyDayButtonContent}" Style="{StaticResource SidebarButtonStyle}"/&amp;gt;
                        &amp;lt;Button Content="{Binding NextSevenDaysButtonContent}" Style="{StaticResource SidebarButtonStyle}"/&amp;gt;
                        &amp;lt;Button Content="{Binding AllMyTasksButtonContent}" Style="{StaticResource SidebarButtonStyle}"/&amp;gt;
                    &amp;lt;/StackPanel&amp;gt;
                    &amp;lt;StackPanel DockPanel.Dock="Top" Margin="0,0,0,30"&amp;gt;
                        &amp;lt;TextBlock Text="My lists" FontSize="18" FontWeight="SemiBold" Margin="0,5,0,5" Foreground="{StaticResource WhiteBrush}"/&amp;gt;
                        &amp;lt;Button Content="{Binding PersonalButtonContent}" Style="{StaticResource SidebarButtonStyle}"/&amp;gt;
                        &amp;lt;Button Content="{Binding WorkButtonContent}" Style="{StaticResource SidebarButtonStyle}"/&amp;gt;
                        &amp;lt;Button Content="{Binding GroceryListButtonContent}" Style="{StaticResource SidebarButtonStyle}"/&amp;gt;
                    &amp;lt;/StackPanel&amp;gt;
                &amp;lt;/DockPanel&amp;gt;
            &amp;lt;/Border&amp;gt;
            &amp;lt;Grid Grid.Column="1"&amp;gt;
                &amp;lt;Grid.RowDefinitions&amp;gt;
                    &amp;lt;RowDefinition Height="Auto"/&amp;gt;
                    &amp;lt;!-- Header/Greeting --&amp;gt;
                    &amp;lt;RowDefinition Height="*"/&amp;gt;
                    &amp;lt;!-- Task List Area --&amp;gt;
                    &amp;lt;RowDefinition Height="Auto"/&amp;gt;
                    &amp;lt;!-- New Task Input --&amp;gt;
                &amp;lt;/Grid.RowDefinitions&amp;gt;

                &amp;lt;StackPanel Background="{StaticResource CardBackgroundBrush}" Grid.Row="0" Margin="0,10,20,30"&amp;gt;
                    &amp;lt;TextBlock Text="Good Afternoon, VectoArt." 
               FontSize="28" 
               FontWeight="Bold" 
               Margin="20,5,0,0"
               Foreground="{StaticResource TextBrush}"/&amp;gt;
                    &amp;lt;TextBlock Text="Let's make an impact" 
               FontSize="18" 
               Opacity="0.7" 
               Margin="20,5,0,10"/&amp;gt;

                    &amp;lt;!-- Date and Calendar Info --&amp;gt;
                    &amp;lt;Border Background="{StaticResource LightBorderBrush}" 
            CornerRadius="12" 
            Padding="10"
            Margin="10,10,10,20"&amp;gt;
                        &amp;lt;StackPanel&amp;gt;
                            &amp;lt;!-- Date (Now bound to the CurrentDateTimeString property) --&amp;gt;
                            &amp;lt;TextBlock FontSize="14" Opacity="0.7" Margin="0,0,0,5" 
                       Text="{Binding CurrentDateTimeString}" 
                       TextWrapping="Wrap"&amp;gt;
                            &amp;lt;/TextBlock&amp;gt;

                            &amp;lt;!-- Calendar Connect CTA has been removed --&amp;gt;
                        &amp;lt;/StackPanel&amp;gt;
                    &amp;lt;/Border&amp;gt;
                &amp;lt;/StackPanel&amp;gt;
                &amp;lt;ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto"&amp;gt;
                    &amp;lt;!-- ItemsSource is now correctly bound to the Tasks property --&amp;gt;
                    &amp;lt;ItemsControl ItemsSource="{Binding Tasks}" 
                  ItemTemplate="{StaticResource TaskTemplate}"&amp;gt;
                    &amp;lt;/ItemsControl&amp;gt;
                &amp;lt;/ScrollViewer&amp;gt;

                &amp;lt;Border Grid.Row="2" 
        Background="{StaticResource CardBackgroundBrush}" 
        CornerRadius="12" 
        Padding="15" 
        Margin="10,20,10,10"
        BorderThickness="1"
        BorderBrush="{StaticResource LightAccentBrush}"&amp;gt;
                    &amp;lt;StackPanel&amp;gt;
                        &amp;lt;Grid Margin="0,0,0,10"&amp;gt;
                            &amp;lt;Grid.ColumnDefinitions&amp;gt;
                                &amp;lt;ColumnDefinition Width="*"/&amp;gt;
                                &amp;lt;ColumnDefinition Width="Auto"/&amp;gt;
                            &amp;lt;/Grid.ColumnDefinitions&amp;gt;

                            &amp;lt;!-- Text box for input --&amp;gt;
                            &amp;lt;TextBox x:Name="NewTaskTextBox" 
             Grid.Column="0" 
             FontSize="15" 
             BorderThickness="0"
             VerticalContentAlignment="Center"
             Padding="5,0"
             Text="Add a new task..."
             Foreground="#AAAAAA"
             GotFocus="NewTaskTextBox_GotFocus"
             LostFocus="NewTaskTextBox_LostFocus"/&amp;gt;

                            &amp;lt;!-- Add Task Button --&amp;gt;
                            &amp;lt;Button Grid.Column="1" 
            Content="+" 
            Width="30" Height="30"
            Click="AddTaskButton_Click"
            Background="{StaticResource AccentBrush}"
            Foreground="{StaticResource SidebarBackgroundBrush}"
            FontWeight="Bold"
            FontSize="30"
            Padding="0,-8,0,0" 
            BorderThickness="0"
            Margin="0,0,0,0" FontFamily="Segoe UI Semibold"/&amp;gt;

                        &amp;lt;/Grid&amp;gt;
                        &amp;lt;StackPanel Orientation="Horizontal"&amp;gt;
                            &amp;lt;!-- Category Dropdown --&amp;gt;

                            &amp;lt;hc:ComboBox ItemsSource="{Binding AvailableCategories}" SelectedItem="{Binding NewTaskCategory, Mode=TwoWay}" Width="150" Height="30" Margin="0,0,10,0"/&amp;gt;
                            &amp;lt;!-- Date Picker --&amp;gt;
                            &amp;lt;hc:DatePicker SelectedDate="{Binding NewTaskDueDate, Mode=TwoWay}" Height="30" Width="120"/&amp;gt;

                        &amp;lt;/StackPanel&amp;gt;
                    &amp;lt;/StackPanel&amp;gt;
                &amp;lt;/Border&amp;gt;
            &amp;lt;/Grid&amp;gt;
        &amp;lt;/Grid&amp;gt;
    &amp;lt;/Border&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Sleek &amp; Modern WPF: Build a Real-Time Word Counter Dashboard (C# Tutorial)</title>
      <dc:creator>VectoArt</dc:creator>
      <pubDate>Sun, 05 Oct 2025 20:47:04 +0000</pubDate>
      <link>https://dev.to/vectoart_a497c516fac199bd/sleek-modern-wpf-build-a-real-time-word-counter-dashboard-c-tutorial-58dh</link>
      <guid>https://dev.to/vectoart_a497c516fac199bd/sleek-modern-wpf-build-a-real-time-word-counter-dashboard-c-tutorial-58dh</guid>
      <description>&lt;p&gt;If you’ve ever needed a fast, clean word counter, you know they often look pretty boring. Today, we’re changing that. We’re going to build this sleek, dark-themed Real-Time Word Counter Dashboard using WPF and C#.&lt;/p&gt;

&lt;p&gt;This project is a perfect hands-on exercise for mastering fundamental WPF concepts like Data Binding, the MVVM Pattern (Simplified), custom XAML Styling, and some neat C# Regex tricks for accurate text analysis.&lt;/p&gt;

&lt;p&gt;Let’s dive in and transform a standard desktop application into a modern tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Visual Studio (Community Edition or higher)&lt;/li&gt;
&lt;li&gt;A new WPF Application (.NET) or WPF App (.NET Framework) project.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up the Data Model (The ‘M’ in MVVM)
&lt;/h2&gt;

&lt;p&gt;The foundation of any dynamic WPF application is a robust data model that can notify the UI of changes. This is achieved using the INotifyPropertyChanged interface.&lt;/p&gt;

&lt;p&gt;We’ll create a simple data class, StatisticItem, to represent each card in our dashboard (Words, Characters, Sentences, etc.).&lt;/p&gt;

&lt;p&gt;Add this class inside your MainWindow.xaml.cs file (or a separate Models folder):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// StatisticItem.cs (or inside MainWindow.xaml.cs)

using System.ComponentModel;
using System.Windows.Media;

public class StatisticItem : INotifyPropertyChanged
{
    private string _value = "0";

    // Value changes and notifies the UI
    public string Value 
    {
        get =&amp;gt; _value;
        set
        {
            if (_value != value)
            {
                _value = value;
                OnPropertyChanged(nameof(Value)); 
            }
        }
    }

    // Static properties for UI definition (Label, Icon, Color)
    public string Label { get; set; }       
    public string IconGlyph { get; set; }   
    public SolidColorBrush IconColor { get; set; } 

    // Standard INotifyPropertyChanged Implementation
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Implement the Main Logic (The Simplified ‘VM’ and Code-Behind)
&lt;/h2&gt;

&lt;p&gt;In your MainWindow.xaml.cs, we will initialize the statistics list, set the DataContext, and include the core logic for counting the text.&lt;/p&gt;

&lt;p&gt;The UpdateCounts() method is where the analysis happens, using powerful Regular Expressions (Regex) for accurate word and sentence segmentation.&lt;/p&gt;

&lt;p&gt;Replace the contents of your MainWindow.xaml.cs with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WordCounterApp
{
    // -------------------------------------------------------------------------
    // 1. STATISTIC MODEL CLASS
    // Used to hold and display the data for each of the five tiles.
    // -------------------------------------------------------------------------
    public class StatisticItem : INotifyPropertyChanged
    {
        private string _value = "0";
        public string Value
        {
            get =&amp;gt; _value;
            set
            {
                if (_value != value)
                {
                    _value = value;
                    OnPropertyChanged(nameof(Value));
                }
            }
        }

        public string Label { get; set; }
        public string IconGlyph { get; set; } // Segoe MDL2 Assets glyph code
        public SolidColorBrush IconColor { get; set; }

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

    // -------------------------------------------------------------------------
    // 2. MAIN WINDOW LOGIC
    // -------------------------------------------------------------------------
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string _inputText = "";

        public string InputText
        {
            get =&amp;gt; _inputText;
            set
            {
                if (_inputText != value)
                {
                    _inputText = value;
                    OnPropertyChanged(nameof(InputText));
                }
            }
        }

        public List&amp;lt;StatisticItem&amp;gt; Statistics { get; set; }

        public MainWindow()
        {
            // Initialize the list of statistics with their static properties (Labels, Icons, Colors)
            Statistics = new List&amp;lt;StatisticItem&amp;gt;
            {
                new StatisticItem { Label = "WORDS", IconGlyph = "\xE762", IconColor = (SolidColorBrush)new BrushConverter().ConvertFromString("#4CAF50") }, // Green, Chart Icon
                new StatisticItem { Label = "CHARACTERS", IconGlyph = "\xE7C3", IconColor = (SolidColorBrush)new BrushConverter().ConvertFromString("#2196F3") }, // Blue, Document Icon
                new StatisticItem { Label = "SENTENCES", IconGlyph = "\xE8A4", IconColor = (SolidColorBrush)new BrushConverter().ConvertFromString("#9C27B0") }, // Purple, Question/Exclamation Mark Icon
                new StatisticItem { Label = "PARAGRAPHS", IconGlyph = "\xE8E2", IconColor = (SolidColorBrush)new BrushConverter().ConvertFromString("#FF9800") }, // Orange, Text Align Icon
                new StatisticItem { Label = "~MIN READ", IconGlyph = "\xE916", IconColor = (SolidColorBrush)new BrushConverter().ConvertFromString("#00BCD4") } // Teal, Clock/Time Icon
            };

            InitializeComponent();

            // Set the DataContext so XAML bindings work against this object
            DataContext = this;

            // Initialize counts
            UpdateCounts();
        }

        // ---------------------------------------------------------------------
        // DATA COUNTING LOGIC
        // ---------------------------------------------------------------------

        private void InputTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            // The InputText property is already updated via TwoWay binding,
            // so we just need to call the count update method.
            UpdateCounts();
        }

        private void UpdateCounts()
        {
            string text = InputText.Trim();

            // 1. Character Count
            int characterCount = text.Length;

            // 2. Word Count
            // Use regex to find sequences of word characters (\w+) separated by word boundaries (\b)
            int wordCount = Regex.Matches(text, @"\b\w+\b").Count;

            // 3. Sentence Count
            // Find periods, question marks, or exclamation marks followed by a space or the end of the string
            int sentenceCount = Regex.Matches(text, @"[.!?](\s|$)").Count;
            // Handle edge case of completely empty input
            if (string.IsNullOrWhiteSpace(text)) { sentenceCount = 0; }
            // Handle case where text exists but has no punctuation (e.g., a single phrase)
            else if (sentenceCount == 0 &amp;amp;&amp;amp; wordCount &amp;gt; 0) { sentenceCount = 1; }


            // 4. Paragraph Count
            // Split by double newline characters, removing empty entries (standard paragraph separation)
            string[] paragraphs = text.Split(new[] { "\r\n\r\n", "\n\n" }, StringSplitOptions.RemoveEmptyEntries);
            int paragraphCount = paragraphs.Length;
            if (string.IsNullOrWhiteSpace(text)) { paragraphCount = 0; } // Ensure empty input is 0

            // 5. Min Read (assuming average reading speed of 200 Words Per Minute)
            double minRead = (double)wordCount / 200.0;
            string minReadDisplay = minRead &amp;lt; 1.0 ? $"{(int)(minRead * 60)} SEC" : $"{minRead:0.0} MIN";
            if (wordCount == 0) { minReadDisplay = "0"; }

            // Update the bound properties
            Statistics[0].Value = wordCount.ToString("N0");
            Statistics[1].Value = characterCount.ToString("N0");
            Statistics[2].Value = sentenceCount.ToString("N0");
            Statistics[3].Value = paragraphCount.ToString("N0");
            Statistics[4].Value = minReadDisplay;
        }

        // ---------------------------------------------------------------------
        // BUTTON EVENT HANDLERS
        // ---------------------------------------------------------------------

        private void ClearText_Click(object sender, RoutedEventArgs e)
        {
            InputText = "";
            InputTextBox.Focus();
        }

        private void CopyText_Click(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(InputText))
            {
                Clipboard.SetText(InputText);
            }
        }

        // ---------------------------------------------------------------------
        // INotifyPropertyChanged IMPLEMENTATION
        // ---------------------------------------------------------------------
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic happens inside the UpdateCounts() method, which is called every time the text in our main input box changes.&lt;/p&gt;

&lt;p&gt;The challenge here is not just counting characters, but accurately counting words, sentences, and paragraphs, which requires handling tricky punctuation and whitespace.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Character Count: This is the easiest. We just take the length of the string after trimming any excess space: text.Length.&lt;/li&gt;
&lt;li&gt;Word Count: For accurate word counting, we use a regular expression. This expression looks for word characters (\w+) separated by boundaries (\b). This correctly ignores punctuation and multiple spaces:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int wordCount = Regex.Matches(text, @"\b\w+\b").Count;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Sentence Count: Similarly, we use Regex to find end-of-sentence punctuation — periods, question marks, or exclamation points — that are immediately followed by a space or the end of the input string:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int sentenceCount = Regex.Matches(text, @"[.!?](\s|$)").Count;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Min Read: We calculate the reading time assuming a standard speed of 200 words per minute. We use a little conditional logic to display it as seconds if it’s under a minute, or minutes otherwise:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;double minRead = (double)wordCount / 200.0; string minReadDisplay = minRead &amp;lt; 1.0 ? $"{(int)(minRead * 60)} SEC" : $"{minRead:0.0} MIN";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we update the Value property of each StatisticItem in our list. Since these items implement INotifyPropertyChanged, the UI automatically refreshes!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Design the Modern Dark UI (XAML)
&lt;/h2&gt;

&lt;p&gt;The dark mode design uses two primary colors: a deep background (#1F2430) and a slightly lighter panel color (#2A303C).&lt;/p&gt;

&lt;p&gt;The key to a clean, maintainable dashboard is the ItemsControl. Instead of manually defining five statistic cards, we bind the control to our Statistics list and use an ItemTemplate and UniformGrid to lay out the data automatically.&lt;/p&gt;

&lt;p&gt;Replace the contents of your MainWindow.xaml file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Window x:Class="WordCounterApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WordCounterApp"
        mc:Ignorable="d"
        Title="Word Counter" Height="650" Width="900"
        Background="#1F2430"&amp;gt;

    &amp;lt;!-- Window/App-wide Styles --&amp;gt;
    &amp;lt;Window.Resources&amp;gt;
        &amp;lt;!-- Style for the statistic numbers (0) --&amp;gt;
        &amp;lt;Style x:Key="StatisticValue" TargetType="TextBlock"&amp;gt;
            &amp;lt;Setter Property="FontSize" Value="32"/&amp;gt;
            &amp;lt;Setter Property="FontWeight" Value="Bold"/&amp;gt;
            &amp;lt;Setter Property="Foreground" Value="#E0E6F0"/&amp;gt;
            &amp;lt;Setter Property="Margin" Value="0,10,0,5"/&amp;gt;
            &amp;lt;Setter Property="HorizontalAlignment" Value="Center"/&amp;gt;
        &amp;lt;/Style&amp;gt;

        &amp;lt;!-- Style for the statistic labels (WORDS, CHARACTERS, etc.) --&amp;gt;
        &amp;lt;Style x:Key="StatisticLabel" TargetType="TextBlock"&amp;gt;
            &amp;lt;Setter Property="FontSize" Value="14"/&amp;gt;
            &amp;lt;Setter Property="Foreground" Value="#9AA3B5"/&amp;gt;
            &amp;lt;Setter Property="HorizontalAlignment" Value="Center"/&amp;gt;
            &amp;lt;Setter Property="FontWeight" Value="SemiBold"/&amp;gt;
        &amp;lt;/Style&amp;gt;

        &amp;lt;!-- Style for the statistic card icons --&amp;gt;
        &amp;lt;Style x:Key="StatisticIcon" TargetType="TextBlock"&amp;gt;
            &amp;lt;Setter Property="FontSize" Value="24"/&amp;gt;
            &amp;lt;Setter Property="FontFamily" Value="Segoe MDL2 Assets"/&amp;gt;
            &amp;lt;Setter Property="Width" Value="30"/&amp;gt;
            &amp;lt;Setter Property="Height" Value="30"/&amp;gt;
            &amp;lt;!-- 
            *** FIX APPLIED HERE: CornerRadius property removed from TextBlock style. ***
            The CornerRadius is correctly applied to the Border element in the ItemTemplate. 
            &amp;lt;Setter Property="CornerRadius" Value="8"/&amp;gt; 
            --&amp;gt;
            &amp;lt;Setter Property="VerticalAlignment" Value="Center"/&amp;gt;
            &amp;lt;Setter Property="HorizontalAlignment" Value="Left"/&amp;gt;
            &amp;lt;Setter Property="Padding" Value="6"/&amp;gt;
            &amp;lt;Setter Property="TextAlignment" Value="Center"/&amp;gt;
            &amp;lt;Setter Property="Foreground" Value="White"/&amp;gt;
            &amp;lt;Setter Property="Margin" Value="15,0,0,0"/&amp;gt;
        &amp;lt;/Style&amp;gt;

        &amp;lt;!-- Style for the main content panels (Input/Statistics) --&amp;gt;
        &amp;lt;Style x:Key="PanelHeader" TargetType="TextBlock"&amp;gt;
            &amp;lt;Setter Property="FontSize" Value="20"/&amp;gt;
            &amp;lt;Setter Property="FontWeight" Value="SemiBold"/&amp;gt;
            &amp;lt;Setter Property="Foreground" Value="#E0E6F0"/&amp;gt;
            &amp;lt;Setter Property="Margin" Value="0,0,0,10"/&amp;gt;
        &amp;lt;/Style&amp;gt;
    &amp;lt;/Window.Resources&amp;gt;

    &amp;lt;Grid Margin="20"&amp;gt;
        &amp;lt;Grid.RowDefinitions&amp;gt;
            &amp;lt;RowDefinition Height="Auto"/&amp;gt;
            &amp;lt;!-- Header --&amp;gt;
            &amp;lt;RowDefinition Height="*"/&amp;gt;
            &amp;lt;!-- Input Text Area --&amp;gt;
            &amp;lt;RowDefinition Height="Auto"/&amp;gt;
            &amp;lt;!-- Statistics Header --&amp;gt;
            &amp;lt;RowDefinition Height="Auto"/&amp;gt;
            &amp;lt;!-- Statistics Cards --&amp;gt;
        &amp;lt;/Grid.RowDefinitions&amp;gt;

        &amp;lt;!-- 1. Header --&amp;gt;
        &amp;lt;DockPanel Grid.Row="0" Margin="0,0,0,10"&amp;gt;
            &amp;lt;!-- Simple Calculator Icon (using Segoe MDL2 Assets) --&amp;gt;
            &amp;lt;TextBlock FontSize="28" Foreground="#FF9800" VerticalAlignment="Center" Margin="0,0,10,0"
                       Text="🧾"/&amp;gt;
            &amp;lt;TextBlock Text="Word Counter" FontSize="28" FontWeight="Bold" Foreground="#E0E6F0"/&amp;gt;
        &amp;lt;/DockPanel&amp;gt;

        &amp;lt;!-- 2. Input Text Area Panel --&amp;gt;
        &amp;lt;Border Grid.Row="1" Background="#2A303C" CornerRadius="10" Padding="15" Margin="0,0,0,20"&amp;gt;
            &amp;lt;Grid&amp;gt;
                &amp;lt;Grid.RowDefinitions&amp;gt;
                    &amp;lt;RowDefinition Height="Auto"/&amp;gt;
                    &amp;lt;RowDefinition Height="*"/&amp;gt;
                &amp;lt;/Grid.RowDefinitions&amp;gt;

                &amp;lt;!-- Input Header and Actions --&amp;gt;
                &amp;lt;DockPanel Grid.Row="0" Margin="0,0,0,10"&amp;gt;
                    &amp;lt;StackPanel Orientation="Horizontal" DockPanel.Dock="Right"&amp;gt;
                        &amp;lt;!-- Clear Button (Trash icon) --&amp;gt;
                        &amp;lt;Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" 
                                Click="ClearText_Click" Margin="0,0,5,0" ToolTip="Clear Text"&amp;gt;
                            &amp;lt;TextBlock Text="&amp;amp;#xE74D;" FontFamily="Segoe MDL2 Assets" Foreground="#9AA3B5" FontSize="16"/&amp;gt;
                        &amp;lt;/Button&amp;gt;
                        &amp;lt;!-- Copy Button (Clipboard icon) --&amp;gt;
                        &amp;lt;Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" 
                                Click="CopyText_Click" ToolTip="Copy Text to Clipboard"&amp;gt;
                            &amp;lt;TextBlock Text="&amp;amp;#xE8C8;" FontFamily="Segoe MDL2 Assets" Foreground="#9AA3B5" FontSize="16"/&amp;gt;
                        &amp;lt;/Button&amp;gt;
                    &amp;lt;/StackPanel&amp;gt;
                    &amp;lt;TextBlock Text="Input Text" Style="{StaticResource PanelHeader}"/&amp;gt;
                &amp;lt;/DockPanel&amp;gt;

                &amp;lt;!-- Input TextBox --&amp;gt;
                &amp;lt;TextBox Grid.Row="1" 
                         x:Name="InputTextBox" 
                         Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" 
                         AcceptsReturn="True" 
                         TextWrapping="Wrap"
                         VerticalScrollBarVisibility="Auto"
                         Background="#1F2430"
                         Foreground="#E0E6F0"
                         BorderBrush="#3B4450"
                         BorderThickness="1"
                         Padding="10"
                         FontSize="16"
                         CaretBrush="#E0E6F0"
                         TextChanged="InputTextBox_TextChanged"&amp;gt;
                    &amp;lt;TextBox.Resources&amp;gt;
                        &amp;lt;Style TargetType="{x:Type Border}"&amp;gt;
                            &amp;lt;Setter Property="CornerRadius" Value="5"/&amp;gt;
                        &amp;lt;/Style&amp;gt;
                    &amp;lt;/TextBox.Resources&amp;gt;
                    &amp;lt;!-- Placeholder Text --&amp;gt;
                    &amp;lt;TextBox.ToolTip&amp;gt;
                        &amp;lt;TextBlock Text="Paste or type your text here..."/&amp;gt;
                    &amp;lt;/TextBox.ToolTip&amp;gt;
                &amp;lt;/TextBox&amp;gt;

            &amp;lt;/Grid&amp;gt;
        &amp;lt;/Border&amp;gt;

        &amp;lt;!-- 3. Statistics Header --&amp;gt;
        &amp;lt;TextBlock Grid.Row="2" Text="Statistics" Style="{StaticResource PanelHeader}" Margin="0,0,0,10"/&amp;gt;

        &amp;lt;!-- 4. Statistics Cards: Refactored to use ItemsControl with a UniformGrid ItemsPanel --&amp;gt;
        &amp;lt;ItemsControl Grid.Row="3" ItemsSource="{Binding Statistics}" MinHeight="150"&amp;gt;

            &amp;lt;!-- ItemsPanel defines the layout for the collection (a 5-column UniformGrid) --&amp;gt;
            &amp;lt;ItemsControl.ItemsPanel&amp;gt;
                &amp;lt;ItemsPanelTemplate&amp;gt;
                    &amp;lt;UniformGrid Columns="5"/&amp;gt;
                &amp;lt;/ItemsPanelTemplate&amp;gt;
            &amp;lt;/ItemsControl.ItemsPanel&amp;gt;

            &amp;lt;!-- ItemTemplate defines the visual appearance of each StatisticItem --&amp;gt;
            &amp;lt;ItemsControl.ItemTemplate&amp;gt;
                &amp;lt;DataTemplate&amp;gt;
                    &amp;lt;Border Background="#2A303C" CornerRadius="10" Margin="5" Padding="10" Width="Auto"&amp;gt;
                        &amp;lt;StackPanel VerticalAlignment="Center"&amp;gt;
                            &amp;lt;!-- Icon Placeholder --&amp;gt;
                            &amp;lt;Border Width="40" Height="40" CornerRadius="8" Padding="0,10,0,0" 
                                    HorizontalAlignment="Left" Margin="5,0,0,10"
                                    Background="{Binding IconColor}"&amp;gt;
                                &amp;lt;TextBlock Text="{Binding IconGlyph}" Style="{StaticResource StatisticIcon}" 
                                           Foreground="White" FontFamily="Segoe MDL2 Assets" Margin="0" Padding="0"/&amp;gt;
                            &amp;lt;/Border&amp;gt;

                            &amp;lt;!-- Value --&amp;gt;
                            &amp;lt;TextBlock Text="{Binding Value}" Style="{StaticResource StatisticValue}"/&amp;gt;

                            &amp;lt;!-- Label --&amp;gt;
                            &amp;lt;TextBlock Text="{Binding Label}" Style="{StaticResource StatisticLabel}" Margin="0,0,0,5"/&amp;gt;
                        &amp;lt;/StackPanel&amp;gt;
                    &amp;lt;/Border&amp;gt;
                &amp;lt;/DataTemplate&amp;gt;
            &amp;lt;/ItemsControl.ItemTemplate&amp;gt;

        &amp;lt;/ItemsControl&amp;gt;

    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dark Theme&lt;/strong&gt;: We set the main window background to a dark gray &lt;strong&gt;#1F2430&lt;/strong&gt; and use a slightly lighter gray &lt;strong&gt;#2A303C&lt;/strong&gt; for the panel backgrounds. This contrast is key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout&lt;/strong&gt;: We use a simple &lt;strong&gt;Grid&lt;/strong&gt; with four rows: Header, Input Area, Statistics Header, and the Statistics Cards. The Input Area row uses Height="*" so it expands to fill the remaining space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Input Box&lt;/strong&gt;: The main &lt;strong&gt;TextBox&lt;/strong&gt; is bound directly to our &lt;strong&gt;InputText&lt;/strong&gt; property in C#. Crucially, we use the &lt;strong&gt;TextChanged="InputTextBox_TextChanged"&lt;/strong&gt; event, which triggers our UpdateCounts() method every time the user types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Statistics Cards (ItemsControl Fix)&lt;/strong&gt;: This is where we make the magic happen with the list. Instead of manually creating five cards, we use an &lt;strong&gt;ItemsControl&lt;/strong&gt; bound to our **Statistics **list:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ItemsControl Grid.Row="3" ItemsSource="{Binding Statistics}"&amp;gt;
    &amp;lt;ItemsControl.ItemsPanel&amp;gt;
        &amp;lt;!-- Lays out the items in a 5-column grid --&amp;gt;
        &amp;lt;ItemsPanelTemplate&amp;gt;
            &amp;lt;UniformGrid Columns="5"/&amp;gt;
        &amp;lt;/ItemsPanelTemplate&amp;gt;
    &amp;lt;/ItemsControl.ItemsPanel&amp;gt;

    &amp;lt;ItemsControl.ItemTemplate&amp;gt;
        &amp;lt;!-- Defines the look of ONE single StatisticItem --&amp;gt;
        &amp;lt;DataTemplate&amp;gt;
            &amp;lt;Border Background="#2A303C" CornerRadius="10" Margin="5" Padding="10"&amp;gt;
                &amp;lt;StackPanel&amp;gt;
                    &amp;lt;!-- Icon Border, uses IconColor and CornerRadius --&amp;gt;
                    &amp;lt;Border Background="{Binding IconColor}" CornerRadius="8"&amp;gt;
                        &amp;lt;TextBlock Text="{Binding IconGlyph}" Style="{StaticResource StatisticIcon}"/&amp;gt;
                    &amp;lt;/Border&amp;gt;
                    &amp;lt;!-- Value Block --&amp;gt;
                    &amp;lt;TextBlock Text="{Binding Value}" Style="{StaticResource StatisticValue}"/&amp;gt;
                    &amp;lt;!-- Label Block --&amp;gt;
                    &amp;lt;TextBlock Text="{Binding Label}" Style="{StaticResource StatisticLabel}"/&amp;gt;
                &amp;lt;/StackPanel&amp;gt;
            &amp;lt;/Border&amp;gt;
        &amp;lt;/DataTemplate&amp;gt;
    &amp;lt;/ItemsControl.ItemTemplate&amp;gt;
&amp;lt;/ItemsControl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ItemsControl automatically loops through our Statistics list, uses the **UniformGrid **to arrange them evenly, and applies the beautiful dark-themed design from the **ItemTemplate **to each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And there you have it! A fully functional, modern word counter built entirely with WPF and C#. We covered data modeling with INotifyPropertyChanged, effective counting using Regex, and powerful UI layout using the ItemsControl.&lt;/p&gt;

&lt;p&gt;You can use the principles here for any dashboard or statistics panel you need to build in WPF.&lt;/p&gt;

&lt;p&gt;If you have any questions about the Regex, the MVVM binding, or want to know how to add features like syllable count, let me know in the comments below!&lt;/p&gt;

&lt;p&gt;Happy coding, and I’ll see you in the next one!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>softwaredevelopment</category>
      <category>csharp</category>
    </item>
    <item>
      <title>🚀 Build a Sleek, Real-Time Dashboard in WPF using Data Binding and ItemsControl (C# .NET)</title>
      <dc:creator>VectoArt</dc:creator>
      <pubDate>Sat, 04 Oct 2025 16:40:14 +0000</pubDate>
      <link>https://dev.to/vectoart_a497c516fac199bd/build-a-sleek-real-time-dashboard-in-wpf-using-data-binding-and-itemscontrol-c-net-3ddm</link>
      <guid>https://dev.to/vectoart_a497c516fac199bd/build-a-sleek-real-time-dashboard-in-wpf-using-data-binding-and-itemscontrol-c-net-3ddm</guid>
      <description>&lt;p&gt;&lt;a href="https://youtu.be/wCVaYI9GOWs" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are several new NuGet packages that can help you design modern desktop applications in WinForms. Here are some popular ones, along with examples of how to use them:&lt;/p&gt;

&lt;h2&gt;
  
  
  MaterialSkin
&lt;/h2&gt;

&lt;p&gt;Description: MaterialSkin is a library for bringing Google’s Material Design principles to Windows Forms applications. It provides a modern look and feel, making your application visually appealing and consistent with contemporary UI standards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Material Design Components: Buttons, text fields, switches, sliders, and more.&lt;br&gt;
Themes: Light and dark themes with customizable color schemes.&lt;br&gt;
Typography: Follows Material Design typography standards.&lt;br&gt;
Usage Example: To use MaterialSkin, install the package via NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package MaterialSkin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using MaterialSkin;
using MaterialSkin.Controls;

public partial class MainForm : MaterialForm
{
    private MaterialSkinManager materialSkinManager;

    public MainForm()
    {
        InitializeComponent();
        materialSkinManager = MaterialSkinManager.Instance;
        materialSkinManager.AddFormToManage(this);
        materialSkinManager.Theme = MaterialSkinManager.Themes.LIGHT;
        materialSkinManager.ColorScheme = new ColorScheme(
            Primary.Blue600, Primary.Blue700,
            Primary.Blue200, Accent.LightBlue200,
            TextShade.WHITE
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ModernUI
&lt;/h2&gt;

&lt;p&gt;Description: ModernUI (also known as MetroFramework) is a library that provides a set of modern UI controls for Windows Forms applications. It mimics the Metro design language introduced by Microsoft.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Metro-style Controls: Buttons, tiles, progress bars, and more.&lt;br&gt;
Themes and Styles: Light and dark themes with customizable color options.&lt;br&gt;
Layout Management: Easy layout management for creating responsive UIs.&lt;br&gt;
Usage Example: To use ModernUI, install the package via NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package ModernUI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using ModernUI.Forms;
using ModernUI.Controls;

public partial class MainForm : MetroForm
{
    public MainForm()
    {
        InitializeComponent();
        var metroButton = new MetroButton
        {
            Text = "Click Me",
            Location = new Point(20, 20)
        };
        this.Controls.Add(metroButton);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LiveCharts
&lt;/h2&gt;

&lt;p&gt;Description: LiveCharts is a flexible and powerful charting library for .NET that provides a variety of chart types and interactive features for data visualization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Chart Types: Line charts, bar charts, pie charts, and more.&lt;br&gt;
Interactivity: Tooltips, animations, and zooming.&lt;br&gt;
Customization: Extensive options for customizing the appearance and behavior of charts.&lt;br&gt;
Usage Example: To use LiveCharts, install the package via NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package LiveCharts.WinForms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using LiveCharts;
using LiveCharts.WinForms;
using LiveCharts.Wpf;

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
        var cartesianChart = new CartesianChart
        {
            Location = new Point(10, 10),
            Size = new Size(600, 400)
        };
        cartesianChart.Series = new SeriesCollection
        {
            new LineSeries
            {
                Title = "Series 1",
                Values = new ChartValues&amp;lt;double&amp;gt; { 3, 5, 7, 4 }
            }
        };
        this.Controls.Add(cartesianChart);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Syncfusion WinForms UI Controls
&lt;/h2&gt;

&lt;p&gt;Description: Syncfusion offers a comprehensive suite of over 100 WinForms UI controls, providing everything needed to create modern and feature-rich desktop applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rich Set of Controls: Grids, charts, calendars, data visualization, and more.&lt;br&gt;
Performance: Optimized for high performance with large datasets.&lt;br&gt;
Customization: Extensive customization options and styling capabilities.&lt;br&gt;
Support: Professional support and regular updates.&lt;br&gt;
Usage Example: To use Syncfusion controls, install the package via NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package Syncfusion.Windows.Forms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Syncfusion.Windows.Forms;
using Syncfusion.WinForms.Controls;

public partial class MainForm : SfForm
{
    public MainForm()
    {
        InitializeComponent();
        var sfButton = new SfButton
        {
            Text = "Click Me",
            Location = new Point(20, 20)
        };
        this.Controls.Add(sfButton);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;These packages provide various controls and features that can help you design modern and visually appealing WinForms applications. Each package has its own set of controls and capabilities, so you can choose the ones that best fit your needs.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>uidesign</category>
    </item>
  </channel>
</rss>
