We will develop a console application which could have a friendly UX and persistent data storage. In this beginner project, we will demonstrate various object-oriented programming features and list manipulations along with file handling. Our end project would something like this.
Persistent To-Do Application
Persistent To-Do Application
First, create a new console application and name it, such as - to-do app.
Persistent To-Do Application
A new project will start. First, we will start off by creating the persistent data communication layer. For this, we are maintaining a file where our to-do task would be stored. We will create a class named DataPersistence for that.
  1. public class DataPersistence
  2. {
  3. //constructor which would make the file if the file does not exist
  4. public DataPersistence()
  5. {
  6. if (!File.Exists(fileName))
  7. {
  8. File.Create(fileName);
  9. }
  10. }
  11. //the file would be stored at the base directory
  12. string fileName = AppDomain.CurrentDomain.BaseDirectory + "/app-data.txt";
  13. //Function for loading up the data
  14. public List<string> loadData()
  15. {
  16. return System.IO.File.ReadAllLines(fileName).ToList();
  17. }
  18. //adding new task on my list
  19. public bool addData(string model)
  20. {
  21. try
  22. {
  23. using (System.IO.StreamWriter file =
  24. new System.IO.StreamWriter(fileName, true))
  25. {
  26. file.WriteLine(model);
  27. }
  28. return true;
  29. }
  30. catch (Exception)
  31. {
  32. throw;
  33. }
  34. }
  35. //reseting the whole list
  36. public bool resetList()
  37. {
  38. File.WriteAllText(fileName, string.Empty);
  39. return true;
  40. }
  41. //complete the task or remove the task from the list
  42. internal bool completeTask(string selectedTask)
  43. {
  44. try
  45. {
  46. var lines = File.ReadAllLines(fileName).Where(line => line.Trim() != selectedTask.Trim()).ToArray();
  47. File.WriteAllLines(fileName, lines);
  48. return true;
  49. }
  50. catch (Exception e)
  51. {
  52. return false;
  53. }
  54. }
  55. }
We now need to access this data from our application. For that, we create our class ToDoApp. It is advisable to separate the data layer and the application layer. This would create an instance of the DataPersistence class and all the functions will first perform operations on the local list then will move on to the file operations.
  1. public class ToDoApp
  2. {
  3. private List<string> toDoList = new List<string>();
  4. DataPersistence dp = new DataPersistence(); //creating an instance of dataPersistence class
  5. public List<string> fetchData()
  6. {
  7. //loading the data from the file
  8. toDoList = dp.loadData();
  9. return toDoList;
  10. }
  11. //its time to add new task to our list
  12. public bool addData(string model)
  13. {
  14. try
  15. {
  16. if (model == null || model == "")
  17. {
  18. return false;
  19. }
  20. toDoList.Add(model);
  21. dp.addData(model);
  22. return true;
  23. }
  24. catch (Exception e)
  25. {
  26. return false;
  27. }
  28. }
  29. //user might want to reset the whole list
  30. public bool resetList()
  31. {
  32. try
  33. {
  34. toDoList.Clear();
  35. return dp.resetList();
  36. }
  37. catch (Exception e)
  38. {
  39. throw;
  40. }
  41. }
  42. //to complete or delete the task from our to-do list
  43. internal bool completeTask(string userInput)
  44. {
  45. try
  46. {
  47. toDoList = dp.loadData();
  48. int index = Convert.ToInt32(userInput) -1;
  49. if (index <= toDoList.Count)
  50. {
  51. string selectedTask = toDoList.ElementAt(index);
  52. toDoList.Remove(selectedTask);
  53. return dp.completeTask(selectedTask);
  54. }
  55. return false;
  56. }
  57. catch (Exception e)
  58. {
  59. return false;
  60. }
  61. }
  62. }
These classes are standalone classes. We need to access these from our main class.
  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Console.Title = "To-Do Application"; //creating a title for our application
  6. fetchData();
  7. Console.ReadLine();
  8. }
  9. private static void fetchData(string message = null)
  10. {
  11. Console.Clear();

  12. //N.B : We would never access the data accessing class from this main class.
  13. ToDoApp app = new ToDoApp(); //creating an instance of the middle layer

  14. List<string> list = app.fetchData();
  15. Console.WriteLine("\t \t \t \t \t Welcome to the To-Do application \n");
  16. Console.WriteLine("\t [ Press x to Exit ] \t [ Enter index to complete task ] \t [ Press r to Reset ] \n \n");
  17. int num = 1;
  18. foreach(string text in list)
  19. {
  20. Console.WriteLine("\t" + num.ToString() + ". " + text + "\n"); //for displaying the data from the list
  21. num++;
  22. }
  23. if(message!="" && message!= null)
  24. {
  25. Console.WriteLine("\t \t \t \t"+message);
  26. }
  27. Console.WriteLine("\n Write Task or press operational key to continue");
  28. string userInput = Console.ReadLine(); //awaiting user input
  29. switchTask(userInput);
  30. }
  31. //A function to operate differently on the basis of user input
  32. private static void switchTask(string userInput)
  33. {
  34. ToDoApp app = new ToDoApp();
  35. switch (userInput.ToLower())
  36. {
  37. case "x": //to exit the console
  38. Console.Clear();
  39. Environment.Exit(0);
  40. break;
  41. case "r":
  42. app.resetList();
  43. fetchData("Reset succesfull !");
  44. break;
  45. default:
  46. int index;
  47. bool isNumeric = int.TryParse(userInput, out index);
  48. if (!isNumeric)
  49. {
  50. app.addData(userInput);
  51. fetchData("Added Succesfully !");
  52. }
  53. else
  54. {
  55. bool isOkay = app.completeTask(userInput);
  56. if (isOkay)
  57. fetchData(@"Completed Task "+ userInput + " successfully !");
  58. else
  59. fetchData(@"No task at " + userInput + " !");
  60. }
  61. break;

  62. //Note that after every operation we are returning it back to the fetchData() function with a feedback message. This would allow the user to re-enter as much as they want.
  63. }
  64. }
  65. }