Build a Personal Task Tracker in C#

Build a C# console app to manage tasks with classes, lists, and loops.

Introduction

30 Second Summary

Everyone has a mental list of tasks they need to get done. What if you could build your own tool to manage that list, right from your terminal?

In this project, you will build a personal task tracker console application in C#. You'll create a command-line tool that lets you add tasks, mark them as complete, and view your entire to-do list.

What You'll Build

A command-line task tracker application built with C# and the .NET SDK that runs interactively in your terminal.

By the end of this project, you'll have:

  • A TaskItem class with properties for title, completion status, and creation date.
  • An interactive menu system that responds to user input in the terminal using a loop and switch statement.
  • List management using List<T> to store, display, and update tasks with edge-case handling.
  • Secret Mission: Add priority levels with an enum and sort tasks by priority.

Are there any prerequisites?

None. This project includes full setup instructions for every tool you need. You do not need any prior programming experience to get started.

Before We Start

Before We Start...

Set Up Your Development Environment

Before you can write any C# code, you need three things: the .NET SDK (which compiles and runs your code), a code editor, and a language extension that adds C# support to that editor.

In this step, you will install all three tools so your machine is ready to build C# applications.

In this step, get ready to:
  • Install the .NET 10 SDK.
  • Install Visual Studio Code and the C# Dev Kit extension.
  • Verify your setup by running a version check command.
Install the .NET 10 SDK

The .NET SDK is the toolkit that lets you create, build, and run C# applications from the command line. You need version 10 for this project.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal and press Enter to open it.

macOS

  • Open a browser and navigate to the .NET 10 download page:

https://dotnet.microsoft.com/download/dotnet/10.0

  • Under the SDK section, find the macOS row in the Installers column.
  • Select Arm64 if you have an Apple Silicon Mac (M1, M2, M3, M4).
  • Select x64 if you have an Intel Mac.
  • Once the download completes, open the .pkg file and follow the installer steps.

Not sure which Mac you have?

Click the Apple menu in the top left corner of your screen, then click About This Mac. If you see "Apple M1" or similar, choose Arm64. If you see "Intel", choose x64.

Windows

  • Install the .NET 10 SDK by running this command:
winget install Microsoft.DotNet.SDK.10

What does this command do?

This uses winget, the Windows Package Manager, to download and install the .NET 10 SDK from Microsoft's official package feed.

Accept any prompts

Winget may ask you to accept source agreements or license terms. Type Y and press Enter when prompted.

  • Once the installation finishes, close your terminal and reopen it so the new dotnet command is available.
Install Visual Studio Code and the C# Dev Kit extension

Visual Studio Code is a lightweight code editor. The C# Dev Kit extension adds language features like IntelliSense, solution management, and debugging support.

  • Open a browser and navigate to https://code.visualstudio.com.
  • Click the download button for your operating system and run the installer.
  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Visual Studio Code and press Enter to open it.
  • Open the Extensions panel by pressing Cmd+Shift+X (macOS) or Ctrl+Shift+X (Windows).
  • Type C# Dev Kit in the search bar at the top of the Extensions panel.
  • Click Install on the extension published by Microsoft (the identifier is ms-dotnettools.csdevkit).

What does C# Dev Kit install?

Installing C# Dev Kit automatically installs the base C# extension and the .NET Install Tool as dependencies. You do not need to install them separately.

Don't see C# Dev Kit in search results?

Make sure you are searching in the Extensions marketplace (the panel opened with Cmd+Shift+X or Ctrl+Shift+X), not the file search. The publisher should show as Microsoft.

Help me find the C# Dev Kit extension in VS Code.

Verify the installation

Now confirm that the .NET SDK is installed correctly and available from inside VS Code.

  • Open a new terminal inside VS Code by pressing Ctrl+` (backtick key, same shortcut on both macOS and Windows).
  • Check your .NET SDK version by running this command:
dotnet --version

✔️ I see version 10 or higher

You should see a version number starting with 10. (for example, 10.0.300). Your .NET SDK is ready to go.

ⓧ I see an older version

If you see a number starting with 8. or 9., you have an older .NET SDK installed. You need version 10 for this project.

  • On Windows, run the install command again. Winget will upgrade your SDK:
winget install Microsoft.DotNet.SDK.10
  • On macOS, download the .NET 10 SDK installer from https://dotnet.microsoft.com/download/dotnet/10.0 and run it. The new version installs alongside older ones.
  • Close and reopen your terminal, then run dotnet --version again to confirm it shows 10.x.x.

ⓧ Command not found

If you see an error like "command not found" or "not recognized", the SDK is either not installed or not on your system PATH.

  • On Windows, go back to the install step and run the winget install Microsoft.DotNet.SDK.10 command. Make sure it completes successfully.
  • On macOS, download and run the .pkg installer from https://dotnet.microsoft.com/download/dotnet/10.0. The installer automatically adds dotnet to your PATH.
  • After installing, close and reopen your terminal completely (quit the Terminal app and relaunch it).
  • Run dotnet --version again.

Still not working?

If you still see "command not found" after reinstalling and reopening the terminal, your PATH may not include the .NET install directory.

Help me fix my PATH so dotnet is recognized in my terminal.

Your development environment is ready. Next up, you will create your first C# console app project and see it run.

Create Your Console App Project

Your development environment is set up and ready. Now it's time to create the actual C# project that will become your task tracker.

The .NET CLI includes a scaffolding command that generates a ready-to-run console application in seconds. You'll use it to create your project, then verify everything works before writing any custom code.

In this step, get ready to:
  • Scaffold a new console app project using the .NET CLI.
  • Explore the generated project structure.
  • Run the default app to confirm everything works.
Scaffold the console app project

Every .NET project lives in its own folder. You'll create a folder on your Desktop, then use dotnet new console to generate the project files inside it.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir TaskTracker
cd TaskTracker
  • Scaffold a new console app by running:
dotnet new console

What does this command do?

dotnet new console tells the .NET CLI to generate a ready-to-run console application. It creates a Program.cs file with starter code and a .csproj project file that tells .NET how to build your app.

Because you ran the command inside a folder named TaskTracker, the project file is automatically named TaskTracker.csproj.

  • Confirm the files were created by running:
ls

You should see Program.cs, TaskTracker.csproj, and an obj folder in the output.

Don't see these files?

Make sure you ran cd TaskTracker before running dotnet new console. If you ran the scaffold command in the wrong folder, delete the generated files and start from the mkdir TaskTracker step again.

If you see a "command not found" error, your .NET SDK installation from Step 1 may not be on your system PATH. Close and reopen your terminal, then try again.

Help me troubleshoot this issue

Open and explore the project in VS Code

Now that the project files exist, open them in Visual Studio Code so you can browse the code.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Visual Studio Code and press Enter to open it.
  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the TaskTracker folder, and click Open.

In the Explorer sidebar on the left, you should see two files and one folder: Program.cs, TaskTracker.csproj, and obj/.

  • Click on Program.cs in the Explorer sidebar to open it.

You should see the default starter code that prints "Hello, World!" to the terminal.

What are these files?

Program.cs is your application's entry point. This is where your C# code runs. Right now it contains a single line that prints a message to the console.

TaskTracker.csproj is the project configuration file. It tells .NET which framework version to target (net10.0) and how to build your app. You won't need to edit this file in this project.

Run the default app

Before writing any custom code, confirm that the scaffolded app runs successfully.

  • Open the integrated terminal in VS Code by pressing Ctrl+` (backtick key, same on macOS and Windows).
  • Run the app by entering this command:
dotnet run

What does dotnet run do?

dotnet run compiles your C# code and immediately executes it. It looks for the .csproj file in the current directory to know what to build.

You should see Hello, World! printed in the terminal.

Not seeing Hello, World! in the output?

Make sure your terminal is inside the TaskTracker folder. If you opened VS Code's terminal and it points somewhere else, run cd ~/Desktop/TaskTracker to navigate there first.

If you see a build error, check that Program.cs and TaskTracker.csproj both exist in the same folder. Running ls should show both files.

Help me troubleshoot this issue

✔️ Awesome, I've got everything!

Great. Make sure you've saved any open files in VS Code before moving on.

ⓧ I'd like to double check the full code

Your Program.cs should contain the default template code:

using System;

Console.WriteLine("Hello, World!");

Your project is scaffolded and running. Next up, you'll build the TaskItem class that represents a single task in your tracker.

Build the Task Class

Every task in your tracker needs a consistent structure. In C#, a class defines the blueprint for objects that share the same properties and behaviors.

In this step, you'll create a TaskItem class that stores a task's title, completion status, and creation date. You'll also add methods that let a task mark itself complete and display its details as a formatted string.

In this step, get ready to:
  • Create a TaskItem class with properties for title, status, and creation date.
  • Add methods to mark tasks complete and display their details.
Create the TaskItem class file

Your project currently has a single Program.cs file. You'll create a separate file for the TaskItem class to keep your code organized.

  • In VS Code, right-click in the Explorer sidebar (the file panel on the left).
  • Click New File.
  • Type TaskItem.cs and press Enter.
  • Add the class definition with its properties and constructor by pasting this code into TaskItem.cs:
using System;

namespace TaskTracker;

public class TaskItem
{
    // Properties store data about each task
    public string Title { get; set; }
    public bool IsCompleted { get; set; }
    public DateTime CreatedAt { get; set; }

    // Constructor sets initial values when a new task is created
    public TaskItem(string title)
    {
        Title = title;
        IsCompleted = false;
        CreatedAt = DateTime.Now;
    }
}

What does this code do?

A class is like a template. Every TaskItem object created from this class will have the same three properties: a Title, an IsCompleted flag, and a CreatedAt timestamp.

The constructor (public TaskItem(string title)) runs automatically when you create a new task. It sets the title you provide, marks the task as not completed, and records the current date and time using DateTime.Now.

What are properties in C#?

Properties like public string Title { get; set; } are a C# feature that combines a variable with controlled access. The get part lets other code read the value. The set part lets other code change it.

  • Save TaskItem.cs.
  • Confirm the code compiles by running this command in your terminal:
dotnet build
  • You should see Build succeeded in the output with 0 errors.

Seeing build errors?

  • Check that your file is named exactly TaskItem.cs (capital T, capital I). C# is case-sensitive.
  • Make sure the namespace says TaskTracker and matches your project folder name.
  • Verify there are no missing curly braces. Every { needs a matching }.

Help me fix my TaskItem.cs build error

Add methods to display and complete tasks

Properties store data, but methods define what a task can do. You'll add two methods: one to mark a task complete, and one to produce a formatted display string.

  • In TaskItem.cs, find the closing } of the class (the last curly brace in the file).
  • Add the following two methods just above that closing }:
    // Marks this task as completed
    public void MarkComplete()
    {
        IsCompleted = true;
    }

    // Returns a formatted string showing the task's status and details
    public string GetDisplayString(int index)
    {
        string status = IsCompleted ? "[X]" : "[ ]";
        return $"  {index}. {status} {Title} (added {CreatedAt:MM/dd/yyyy})";
    }

What do these methods do?

  • MarkComplete() flips IsCompleted to true. That's all it takes to record that a task is done.
  • GetDisplayString(int index) builds a readable line like 1. [X] Buy groceries (added 05/24/2026). The ? : syntax is a ternary operator that picks between [X] and [ ] based on whether the task is complete.
  • The $"..." syntax is called string interpolation. It lets you embed variables directly inside a string using curly braces.
  • Save TaskItem.cs.
  • Confirm everything still compiles by running:
dotnet build
  • You should see Build succeeded again with 0 errors.

Build failed after adding methods?

  • Make sure both methods are inside the class (above the final }), not below it.
  • Check that GetDisplayString has a return type of string and actually returns a value.
  • Verify the dollar sign $ is present before the opening quote in the return statement. Without it, the curly braces won't interpolate.

Help me fix my methods in TaskItem.cs

✔️ Awesome, I've got everything!

Great. Double check you've saved TaskItem.cs and your build succeeds with 0 errors.

ⓧ I'd like to double check the full code

Here's the complete TaskItem.cs file. Compare your code against this:

using System;

namespace TaskTracker;

public class TaskItem
{
    public string Title { get; set; }
    public bool IsCompleted { get; set; }
    public DateTime CreatedAt { get; set; }

    public TaskItem(string title)
    {
        Title = title;
        IsCompleted = false;
        CreatedAt = DateTime.Now;
    }

    public void MarkComplete()
    {
        IsCompleted = true;
    }

    public string GetDisplayString(int index)
    {
        string status = IsCompleted ? "[X]" : "[ ]";
        return $"  {index}. {status} {Title} (added {CreatedAt:MM/dd/yyyy})";
    }
}

Your task blueprint is ready. Next up, you'll build the interactive menu that lets users create tasks, list them, and mark them complete using this class.

Create the Interactive Menu and Main Loop

Your TaskItem class is ready to represent individual tasks. Now you need a way for users to actually interact with those tasks.

In this step, you will build the main engine of your app. You will create a menu that loops continuously, waiting for the user to pick an action. You will also wire up the first real feature: adding a task to a list.

In this step, get ready to:
  • Build a menu that displays four options (Add, List, Complete, Exit).
  • Implement a while loop with a switch statement to handle user choices.
  • Add the "Add Task" feature using Console.ReadLine() and List<TaskItem>.
Build the menu and main loop

The heart of any interactive console app is a loop that keeps running until the user decides to quit. Your menu will display numbered options, read the user's choice, and route to the right action.

  • In VS Code, open Program.cs from the Explorer sidebar.
  • Select all the existing code (Cmd+A on macOS or Ctrl+A on Windows) and replace it with the following:
using System;
using System.Collections.Generic;

namespace TaskTracker;

class Program
{
    // Store all tasks in a shared list
    static List<TaskItem> tasks = new List<TaskItem>();

    static void Main(string[] args)
    {
        Console.WriteLine("=== Personal Task Tracker ===");
        Console.WriteLine();

        // Keep the app running until the user chooses to exit
        bool running = true;

        while (running)
        {
            // Display the menu options
            Console.WriteLine("What would you like to do?");
            Console.WriteLine("  1. Add a task");
            Console.WriteLine("  2. List all tasks");
            Console.WriteLine("  3. Complete a task");
            Console.WriteLine("  4. Exit");
            Console.Write("> ");

            // Read the user's choice
            string choice = Console.ReadLine() ?? "";

            // Route to the correct action based on input
            switch (choice)
            {
                case "1":
                    AddTask();
                    break;
                case "2":
                    ListTasks();
                    break;
                case "3":
                    CompleteTask();
                    break;
                case "4":
                    running = false;
                    Console.WriteLine("Goodbye!");
                    break;
                default:
                    Console.WriteLine("Invalid choice. Please enter 1-4.");
                    break;
            }

            Console.WriteLine();
        }
    }

    // Placeholder methods - we'll fill these in next
    static void AddTask()
    {
    }

    static void ListTasks()
    {
    }

    static void CompleteTask()
    {
    }
}

What does this code do?

This sets up the full skeleton of your app. Here are the key pieces:

  • static List<TaskItem> tasks creates a shared list that all methods can access. This is where your tasks live in memory.
  • bool running = true controls the while loop. The loop keeps running until the user picks option 4, which flips running to false.
  • The switch statement checks the user's input and calls the matching method. If no case matches, default prints an error message.
  • Console.ReadLine() ?? "" reads a line of text from the user. The ?? operator provides an empty string fallback if ReadLine() returns null.
  • Save Program.cs.
  • Run the app by entering this command in your terminal:
dotnet run
  • You should see the welcome header === Personal Task Tracker === followed by the four menu options and a > prompt.
  • Type 4 and press Enter to exit. You should see Goodbye! printed to the terminal.

Not seeing the menu?

  • Make sure you saved Program.cs before running dotnet run. Unsaved changes will not compile.
  • Check that the namespace TaskTracker; line in Program.cs matches the namespace in TaskItem.cs. Both must say TaskTracker.
  • If you see a build error about missing methods, make sure the three empty placeholder methods (AddTask, ListTasks, CompleteTask) are present inside the Program class.

Help me fix a build error in my C# task tracker project

Implement the AddTask method

Right now, choosing option 1 does nothing because AddTask() is empty. You will fill it in so it asks for a task title, validates the input, and adds a new TaskItem to the list.

  • In Program.cs, find the empty AddTask() method:
    static void AddTask()
    {
    }
  • Replace it with the following implementation:
    static void AddTask()
    {
        // Ask the user for a task title
        Console.Write("Enter task title: ");
        string title = Console.ReadLine() ?? "";

        // Validate that the title is not empty or whitespace
        if (string.IsNullOrWhiteSpace(title))
        {
            Console.WriteLine("Task title cannot be empty.");
            return;
        }

        // Create a new TaskItem and add it to the list
        tasks.Add(new TaskItem(title));
        Console.WriteLine($"Added: \"{title}\"");
    }

What does this code do?

  • Console.Write("Enter task title: ") prints a prompt without moving to a new line, so the user types on the same line.
  • string.IsNullOrWhiteSpace(title) catches empty input, spaces-only input, or null. The return statement exits the method early so no empty task gets created.
  • new TaskItem(title) calls the constructor you wrote in the previous step. The new object gets added straight into the tasks list.
  • Save Program.cs.
  • Run the app again:
dotnet run
  • Type 1 and press Enter to choose "Add a task".
  • Type a task title like Buy groceries and press Enter.
  • You should see Added: "Buy groceries" printed as confirmation.
  • Type 4 to exit the app.

Not seeing the confirmation message?

  • If you see "Task title cannot be empty" when entering a title, make sure you are not accidentally pressing Enter before typing the task name.
  • Check that tasks.Add(new TaskItem(title)) uses TaskItem (the class name from your other file), not Task or TaskItems.
  • Confirm that both files share the same namespace TaskTracker. If they differ, the Program class will not find TaskItem.

Help me debug my AddTask method

✔️ Awesome, I've got everything!

Great. Double check you have saved Program.cs before moving on.

ⓧ I'd like to double check the full code

Here is the complete Program.cs at this point:

using System;
using System.Collections.Generic;

namespace TaskTracker;

class Program
{
    static List<TaskItem> tasks = new List<TaskItem>();

    static void Main(string[] args)
    {
        Console.WriteLine("=== Personal Task Tracker ===");
        Console.WriteLine();

        bool running = true;

        while (running)
        {
            Console.WriteLine("What would you like to do?");
            Console.WriteLine("  1. Add a task");
            Console.WriteLine("  2. List all tasks");
            Console.WriteLine("  3. Complete a task");
            Console.WriteLine("  4. Exit");
            Console.Write("> ");

            string choice = Console.ReadLine() ?? "";

            switch (choice)
            {
                case "1":
                    AddTask();
                    break;
                case "2":
                    ListTasks();
                    break;
                case "3":
                    CompleteTask();
                    break;
                case "4":
                    running = false;
                    Console.WriteLine("Goodbye!");
                    break;
                default:
                    Console.WriteLine("Invalid choice. Please enter 1-4.");
                    break;
            }

            Console.WriteLine();
        }
    }

    static void AddTask()
    {
        Console.Write("Enter task title: ");
        string title = Console.ReadLine() ?? "";

        if (string.IsNullOrWhiteSpace(title))
        {
            Console.WriteLine("Task title cannot be empty.");
            return;
        }

        tasks.Add(new TaskItem(title));
        Console.WriteLine($"Added: \"{title}\"");
    }

    static void ListTasks()
    {
    }

    static void CompleteTask()
    {
    }
}

Your app now has a working menu loop and can add tasks to a list. Next up, you will build the features that let you see your tasks and mark them as complete.

Add List and Complete Features

Your menu is wired up and you can already add tasks to your tracker. The problem is you can't see them or check them off yet.

In this step, you'll fill in the two remaining methods: one to display all your tasks and one to mark them complete. You'll also handle edge cases like empty lists and invalid input so the app doesn't crash on unexpected choices.

In this step, get ready to:
  • Implement the ListTasks method to display all tasks with their index and status.
  • Implement the CompleteTask method that marks a task as done by index.
  • Test edge cases like empty lists, invalid input, and out-of-range numbers.
Implement listing all tasks with their index and status

The ListTasks() method needs to loop through the tasks list and print each task's details. It also needs to handle the case where no tasks exist yet.

  • In VS Code, open Program.cs.
  • Find the empty ListTasks() method.
  • Replace the empty method body with this implementation:
    static void ListTasks()
    {
        // Check if there are any tasks to display
        if (tasks.Count == 0)
        {
            Console.WriteLine("No tasks yet. Add one!");
            return;
        }

        // Print each task with its number and status
        Console.WriteLine("Your tasks:");
        for (int i = 0; i < tasks.Count; i++)
        {
            Console.WriteLine(tasks[i].GetDisplayString(i + 1));
        }
    }

What does this code do?

The method first checks if the tasks list is empty. If it is, it prints a friendly message and exits early with return.

If tasks exist, a for loop iterates through the list. It calls GetDisplayString(i + 1) on each task, passing i + 1 because users expect numbering to start at 1, not 0.

  • Save Program.cs.
  • Run the app by running:
dotnet run
  • Choose option 1 to add a task (enter any title like Buy groceries).
  • Choose option 2 to list tasks. You should see your task displayed with [ ] (uncompleted), the title, and the date.

Not seeing your task listed?

Make sure you replaced the entire empty ListTasks() method, including the curly braces. The new code should have the if check and the for loop inside the method body.

Also confirm you saved Program.cs before running dotnet run.

Why isn't my ListTasks method showing any output?

Add the complete task feature

The CompleteTask() method is more involved. It shows the current tasks, asks the user which one to mark done, and validates that the input is a real task number.

  • In Program.cs, find the empty CompleteTask() method.
  • Replace the empty method body with this implementation:
    static void CompleteTask()
    {
        // Exit early if there's nothing to complete
        if (tasks.Count == 0)
        {
            Console.WriteLine("No tasks to complete.");
            return;
        }

        // Show current tasks so the user knows which number to pick
        ListTasks();
        Console.Write("Enter task number to complete: ");
        string input = Console.ReadLine() ?? "";

        // Validate input: must be a number within the valid range
        if (int.TryParse(input, out int taskNumber) && taskNumber >= 1 && taskNumber <= tasks.Count)
        {
            tasks[taskNumber - 1].MarkComplete();
            Console.WriteLine($"Completed: \"{tasks[taskNumber - 1].Title}\"");
        }
        else
        {
            Console.WriteLine("Invalid task number.");
        }
    }

What does this code do?

First, the method guards against an empty list. Then it calls ListTasks() to show the user their options before asking for a number.

The key line is int.TryParse(input, out int taskNumber). This safely converts the user's text into an integer without crashing if they type something like "abc". The && conditions also check that the number falls within the valid range (1 to the list count).

If everything is valid, tasks[taskNumber - 1] accesses the correct task (subtracting 1 because lists are zero-indexed) and calls MarkComplete() on it.

  • Save Program.cs.
  • Run the app by running:
dotnet run
  • Add a task (option 1), then choose option 3 to complete it.
  • Enter 1 when prompted for the task number. You should see a confirmation like Completed: "Buy groceries".
  • Choose option 2 to list tasks again. The task should now show [X] instead of [ ].

Task not getting marked complete?

Check that the if condition includes both taskNumber >= 1 and taskNumber <= tasks.Count. If either is missing, valid numbers might fall through to the else branch.

Make sure you're entering just the number (like 1) and not extra text when prompted.

Why is my CompleteTask method saying invalid task number for valid input?

Test edge cases

Good software handles unexpected input gracefully. Your methods already include edge case handling. Let's verify it works.

  • Run the app by running:
dotnet run
  • Choose option 2 (List all tasks) immediately without adding anything first. You should see No tasks yet. Add one!.
  • Choose option 3 (Complete a task) with an empty list. You should see No tasks to complete..
  • Add a task, then choose option 3 and enter 99 as the task number. You should see Invalid task number..
  • Try entering abc as a task number. You should see the same Invalid task number. message.
  • Choose option 4 to exit. You should see Goodbye! and the program ends.

Why handle edge cases?

Without the empty-list guard, calling CompleteTask() with no tasks would still prompt for input, confusing the user. Without int.TryParse, typing "abc" would crash the program entirely.

These defensive checks are a core pattern in production C# code. They keep the app running smoothly no matter what the user types.

✔️ Awesome, I've got everything!

Great. Double check you've saved Program.cs and that all three edge case tests pass.

ⓧ I'd like to double check the full code

Here's the complete Program.cs with all methods implemented:

using System;
using System.Collections.Generic;

namespace TaskTracker;

class Program
{
    static List<TaskItem> tasks = new List<TaskItem>();

    static void Main(string[] args)
    {
        Console.WriteLine("=== Personal Task Tracker ===");
        Console.WriteLine();

        bool running = true;

        while (running)
        {
            Console.WriteLine("What would you like to do?");
            Console.WriteLine("  1. Add a task");
            Console.WriteLine("  2. List all tasks");
            Console.WriteLine("  3. Complete a task");
            Console.WriteLine("  4. Exit");
            Console.Write("> ");

            string choice = Console.ReadLine() ?? "";

            switch (choice)
            {
                case "1":
                    AddTask();
                    break;
                case "2":
                    ListTasks();
                    break;
                case "3":
                    CompleteTask();
                    break;
                case "4":
                    running = false;
                    Console.WriteLine("Goodbye!");
                    break;
                default:
                    Console.WriteLine("Invalid choice. Please enter 1-4.");
                    break;
            }

            Console.WriteLine();
        }
    }

    static void AddTask()
    {
        Console.Write("Enter task title: ");
        string title = Console.ReadLine() ?? "";

        if (string.IsNullOrWhiteSpace(title))
        {
            Console.WriteLine("Task title cannot be empty.");
            return;
        }

        tasks.Add(new TaskItem(title));
        Console.WriteLine($"Added: \"{title}\"");
    }

    static void ListTasks()
    {
        if (tasks.Count == 0)
        {
            Console.WriteLine("No tasks yet. Add one!");
            return;
        }

        Console.WriteLine("Your tasks:");
        for (int i = 0; i < tasks.Count; i++)
        {
            Console.WriteLine(tasks[i].GetDisplayString(i + 1));
        }
    }

    static void CompleteTask()
    {
        if (tasks.Count == 0)
        {
            Console.WriteLine("No tasks to complete.");
            return;
        }

        ListTasks();
        Console.Write("Enter task number to complete: ");
        string input = Console.ReadLine() ?? "";

        if (int.TryParse(input, out int taskNumber) && taskNumber >= 1 && taskNumber <= tasks.Count)
        {
            tasks[taskNumber - 1].MarkComplete();
            Console.WriteLine($"Completed: \"{tasks[taskNumber - 1].Title}\"");
        }
        else
        {
            Console.WriteLine("Invalid task number.");
        }
    }
}

Your task tracker is fully functional. You can add tasks, view them, and check them off. Next up, you'll extend the app with priority levels in the Secret Mission.

Secret mission

Add Priority Levels

Your task tracker treats every task equally right now. Real productivity means knowing what matters most. Add priority levels so you can sort and focus on what's urgent.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources, pause them to come back later, or delete them entirely.

Resources you used:

  • TaskTracker project folder on your Desktop.
  • .NET 10 SDK.
  • Visual Studio Code with the C# Dev Kit extension.

Keep everything running

No action needed. Choose this if you're still actively building or want to keep experimenting with your task tracker.

  • Your TaskTracker project folder stays on your Desktop, ready to open any time.
  • The .NET 10 SDK stays installed and is useful for any future C# or .NET projects.
  • VS Code and the C# Dev Kit extension remain available for other development work.

Pause - I'll come back to this later

Close VS Code to free up memory. Your project files and installed tools stay in place for when you return.

  • Close Visual Studio Code.
  • Your TaskTracker folder and all installed tools remain untouched.
  • When you're ready to resume, open VS Code and navigate back to your TaskTracker folder on your Desktop.

Delete - I don't want to use this again

Remove all project files and optionally uninstall the tools you set up during this project.

Delete the TaskTracker project folder:

macOS

  • Open Terminal and run this command to delete your project folder:
rm -rf ~/Desktop/TaskTracker

Windows

  • Open PowerShell and run this command to delete your project folder:
Remove-Item -Recurse -Force ~\Desktop\TaskTracker

Uninstall the .NET 10 SDK:

macOS

  • On macOS, .NET is installed to versioned directories under /usr/local/share/dotnet/. Open Terminal and run this command to remove the .NET 10 SDK:
sudo rm -rf /usr/local/share/dotnet/sdk/10.*

Windows

  • Open PowerShell and run this command:
winget uninstall --id Microsoft.DotNet.SDK.10

Uninstall Visual Studio Code:

macOS

  • Open Finder and navigate to the Applications folder.
  • Right-click on Visual Studio Code and select Move to Trash.
  • Empty the Trash to complete the removal.

Windows

  • Open Settings.
  • Select Apps, then Installed apps.
  • Search for Visual Studio Code in the list.
  • Click the three-dot menu next to it and select Uninstall.

Nice Work!

Nice Work!

You just built your first real C# application from scratch. You now have a fully functional task tracker running in your terminal that manages tasks with add, complete, list, and sort functionality.

You've learned how to:

  • Built a complete console application using the .NET CLI, from scaffolding with dotnet new console to running with dotnet run.
  • Created a TaskItem class with properties, a constructor, and methods, then used List<T> to store and manage a collection of those objects.
  • Implemented an interactive menu system with while loops, switch statements, and input validation to handle edge cases gracefully.
  • Secret Mission: Added priority levels using an enum and sorted tasks by priority with LINQ's OrderByDescending.

Ready to quiz yourself?