DEV Community

Cover image for Building a Real-Time Day Planner in WPF: From Static Design to Dynamic Data
VectoArt
VectoArt

Posted on

Building a Real-Time Day Planner in WPF: From Static Design to Dynamic Data

See the app in action: https://youtu.be/P0J6JZ73m0Q

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.

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.

1. The XAML Blueprint: A Responsive, Custom Layout

A great user experience starts with a great layout. In WPF, this means mastering the Grid.

Our application uses a standard two-column layout defined in MainWindow.xaml:

  • Column 0 (Sidebar): We assign a fixed width of 280 pixels. This ensures our navigation panel is always stable.

  • 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.

Custom Styling with ControlTemplate
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.

This allows us to replace the default look of a control with our own structure — usually a Border—and apply properties like CornerRadius and
custom hover Trigger effects to that internal Border.

2. The Core: The Task Model and MVVM Fundamentals

The heart of any real-time application is its data model’s ability to communicate changes to the UI.

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

The Magic of INotifyPropertyChanged
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.

This is achieved by invoking the OnPropertyChanged() method within the setter of any critical property:

public class Task : INotifyPropertyChanged
{
    // ...
    public bool IsCompleted
    {
        get => _isCompleted;
        set
        {
            if (_isCompleted != value)
            {
                _isCompleted = value;
                OnPropertyChanged(); // <-- This signals the UI to update!
            }
        }
    }
    // ...
}
Enter fullscreen mode Exit fullscreen mode

The ObservableCollection
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.

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.

3. Real-Time Magic: Dynamic Counting with LINQ

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.

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#.

The Counting Logic
The logic is remarkably clean. We first find all incomplete tasks, then apply specific date or category filters to that subset:

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

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

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

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

    // 4. Update the bound strings for the XAML buttons
    MyDayButtonContent = $"My day ({MyDayCount})";
    // ...
}
Enter fullscreen mode Exit fullscreen mode

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.

4. Advanced Interactivity: Converters and Filtering

Two final components bring polish to the application: custom value converters and dynamic list filtering.

Visual Feedback with IValueConverter
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.

This is the job of the CompletedToStrikeConverter class, which implements IValueConverter.

  • Input: true (The task is completed).

  • Converter Output: TextDecorations.Strikethrough (The WPF style).

This pattern keeps our C# logic clean and isolates presentation rules in a reusable class.

Dynamic List Filtering with CollectionViewSource
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.

Instead of binding our main ItemsControl directly to the Tasks collection, we bind it to a View of that collection:

// Inside the button click handler:
TasksViewSource.View.Filter = TasksViewSource_Filter;
TasksViewSource.View.Refresh();
Enter fullscreen mode Exit fullscreen mode

Conclusion

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.

The result is a responsive, modern Day Planner ready to run on any Windows desktop!

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!

Complete Source Code Reference
For a complete and runnable project, here are the contents of the required files.

  1. MainWindow.xaml.cs (C# Logic and ViewModel)
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 => _description;
            set { _description = value; OnPropertyChanged(nameof(Description)); }
        }
        public string ListName
        {
            get => _listName;
            set { _listName = value; OnPropertyChanged(nameof(ListName)); }
        }
        public string Category
        {
            get => _category;
            set { _category = value; OnPropertyChanged(nameof(Category)); }
        }
        // NEW: Due Date property
        public DateTime DueDate
        {
            get => _dueDate;
            set { _dueDate = value; OnPropertyChanged(nameof(DueDate)); }
        }

        public bool IsCompleted
        {
            get => _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 && 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 && 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;
        }
    }
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    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 => _myDayButtonContent;
            set { _myDayButtonContent = value; OnPropertyChanged(); }
        }

        private string _nextSevenDaysButtonContent;
        public string NextSevenDaysButtonContent
        {
            get => _nextSevenDaysButtonContent;
            set { _nextSevenDaysButtonContent = value; OnPropertyChanged(); }
        }

        private string _allMyTasksButtonContent;
        public string AllMyTasksButtonContent
        {
            get => _allMyTasksButtonContent;
            set { _allMyTasksButtonContent = value; OnPropertyChanged(); }
        }

        private string _personalButtonContent;
        public string PersonalButtonContent
        {
            get => _personalButtonContent;
            set { _personalButtonContent = value; OnPropertyChanged(); }
        }

        private string _workButtonContent;
        public string WorkButtonContent
        {
            get => _workButtonContent;
            set { _workButtonContent = value; OnPropertyChanged(); }
        }

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


        private int _myDayCount;
        public int MyDayCount
        {
            get => _myDayCount;
            set
            {
                if (_myDayCount != value)
                {
                    _myDayCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _nextSevenDaysCount;
        public int NextSevenDaysCount
        {
            get => _nextSevenDaysCount;
            set
            {
                if (_nextSevenDaysCount != value)
                {
                    _nextSevenDaysCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _allMyTasksCount;
        public int AllMyTasksCount
        {
            get => _allMyTasksCount;
            set
            {
                if (_allMyTasksCount != value)
                {
                    _allMyTasksCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _personalCount;
        public int PersonalCount
        {
            get => _personalCount;
            set
            {
                if (_personalCount != value)
                {
                    _personalCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _workCount;
        public int WorkCount
        {
            get => _workCount;
            set
            {
                if (_workCount != value)
                {
                    _workCount = value;
                    OnPropertyChanged();
                }
            }
        }
        private int _groceryListCount;
        public int GroceryListCount
        {
            get => _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<string>
            {
                "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<Task>();
        }

        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) && taskDescription != placeholderText)
            {
                // 3. Create a new Task object
                // New tasks default to "My lists > 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 => !t.IsCompleted).ToList();

            MyDayCount = incompleteTasks.Count(t => t.DueDate.Date == today);

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

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

            // List Counts
            PersonalCount = incompleteTasks.Count(t => t.Category == "Personal");
            WorkCount = incompleteTasks.Count(t => t.Category == "Work");
            GroceryListCount = incompleteTasks.Count(t => 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})";
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. MainWindow.xaml (WPF Layout and Styling)
<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">


    <Window.Resources>
        <!-- Color Definitions -->
        <Color x:Key="PrimaryBackground">#F8F8F8</Color>
        <Color x:Key="SidebarBackground">#FFFFFF</Color>
        <Color x:Key="CardBackground">#FFFFFF</Color>
        <Color x:Key="AccentColor">#000</Color>
        <Color x:Key="TextColor">#000</Color>
        <Color x:Key="WhiteColor">#FFFFFF</Color>
        <Color x:Key="LightAccentColor">#000</Color>
        <Color x:Key="LightBorderColor">#E8E8E8</Color>

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

        <!-- Value Converters -->
        <local:CompletedToStrikeConverter x:Key="CompletedToStrikeConverter"/>
        <local:CompletedToFillConverter x:Key="CompletedToFillConverter"/>

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

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

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


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

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

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

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

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

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

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

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

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

                            <!-- Text box for input -->
                            <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"/>

                            <!-- Add Task Button -->
                            <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"/>

                        </Grid>
                        <StackPanel Orientation="Horizontal">
                            <!-- Category Dropdown -->

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

                        </StackPanel>
                    </StackPanel>
                </Border>
            </Grid>
        </Grid>
    </Border>
</Window>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)