C# Projects For Beginners
C# Projects For Beginners
BEGINNERS
Problem-based Learning
LYNN SMITH
Copyright © 2020 Lynn Smith
All rights reserved.
ISBN: 9798662670174
CONTENTS
Acknowledgments I
1 Get Ready 1
2 Your First Assignment 5
3 The Tiny Library Has a New Book 7
4 The Library Is Growing with More Books 10
5 Save Data Permanently in a Text File 14
6 Retrieve and Display the Books from a Text File 17
7 Organizing Statements by Methods 20
8 User Menu Displays Until Quit 26
9 Practice Makes Perfect 32
10A Book Has More Than Just a Title and Author 34
11Can We Have More Than One Author for a Book? 40
12Use the Book Class in the Tiny Library Program 44
13ENUM for Genre 49
14Graphical User Interface (GUI) 52
15Graphical User Interface (GUI) Continued 63
16Graphical User Interface (GUI) – Display All 68
Books
17Graphical User Interface (GUI) – Dropdown Menu 71
18Graphical User Interface (GUI) – Update a Book 75
19Graphical User Interface (GUI) – Delete a Book 87
20Graphical User Interface (GUI) – Check Out a 94
Book
21Graphical User Interface (GUI) – Return a Book 102
22Print a Receipt for Checking Out a Book 110
23Don’t Repeat Yourself (DRY) 114
24Practice, Practice, Practice 121
ACKNOWLEDGMENTS
This book is impossible without the support of my family.
MISSION 1: GET READY
Programming is writing instructions for computers to follow. Before you can
learn how to write the instructions, you need an application in which to write
your instructions. This book uses Visual Studio as such application. You will
be able to write and test instructions on Visual Studio.
Your task for this mission is to install Visual Studio and test one instruction
on it.
Search the Internet for “Visual Studio download”. Most likely, you will come
to the following site:
https://visualstudio.microsoft.com/downloads/
Find “Community” edition and click on “Free Download”.
After the download is completed, double click on the downloaded file to
install. The installer will prompt you to pick which component(s) to install.
Make sure you check the “.NET desktop development”.
Select the one that has all the terms (most likely the first one) and click on the
“Next” button on the bottom right of the window.
On the “Configure your new project” window, type in a project name, say
Mission1. Click on the “Create” button on bottom right to create the project.
You should not put any spaces in the project name.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mission1
{
class Program
{
static void Main( string [] args)
{
}
}
}
You can ignore the first five lines of “using” for now. You may notice the
three pairs of curly braces. Think of each pair as a container. It is how C#
organizes the code. The largest, outer most container is a namespace called
Mission1. Anything inside that pair of curly braces is part of the Mission1
namespace. One of the components of Mission1 is a class called Program.
Similarly, components of that class should be inside the pair of curly braces.
Further inside the class container is a method container called Main(). All
content of the method should be inside its pair of curly braces. Think of a
method as a function. Right now, there is no instructions in this method.
All your instructions for the computers to follow should be inside the Main()
method, or inside the inner most pair of the curly braces in the above code
template.
Let’s add one line of instruction to the Mission1 project so the screen looks
like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mission1
{
class Program
{
static void Main( string [] args)
{
Console.ReadKey();
}
}
}
Save and run the program. Click on the “Start” icon on the menu to run the
program.
A blank console screen will pop up. Push any key on the keyboard. The
console closes.
The line of instruction you added tells the computer to receive any keystroke
from the console. If you don’t touch the keyboard, the console screen will
always be there waiting for a key press. After you press any key on the
keyboard, the program receives it and the computer has no more instructions
to follow and thus closes the console window. If you remove this line and run
the program, you will see the console window opens and closes immediately.
Congratulations! You are ready to have fun in C#.
MISSION 2: YOUR FIRST
ASSIGNMENT
A tiny library has only one book, The Old Man and the Sea by Ernest
Hemingway. Develop an application to display the book information to the
user.
Your task is to write instructions for displaying the book title and author on
the computer console screen.
Start a new C# Console .Net framework project called Mission2. Add the
following code in bold inside the Main() method (We include the method
header and the pair of curly braces for your convenience. Do not make your
code in Visual Studio bold. ):
static void Main( string [] args)
{
Console.WriteLine( "***** Tiny Library Book List *****" );
Console.WriteLine( "The Old Man and the Sea by Hemingway" );
Console.ReadKey();
}
Save and run the program by clicking on the “Start” button. You should see
the only book from the library displayed on a console screen. Then, press any
key on the keyboard to close the window.
In this mission, you have learned how to display a string on the console
screen with Console.WriteLine() method. Put that string inside the method’s
pair of parentheses. The string you want to display must be inside a pair of
quotation marks. The pair of quotation marks indicates the content is a string
literal.
Console is a prebuilt class that is part of System namespace. That is why at
the top of the program, you have the using System directive.
The WriteLine() method is part of the Console class. It is prebuilt. You just
use it.
Finally, every statement must terminate with a semicolon “;”.
Phenomenal! You have completed the first day’s job for the Tiny Library.
You have learned how to display a string text to a console window.
Take a break to cool down the computer before you continue on Mission 3.
MISSION 3: THE TINY
LIBRARY HAS A NEW
BOOK
The owner of the library was very happy with your application and decided to
buy a new book, The Adventures of Tom Sawyer by Mark Twain.
Your task in this mission is to develop an application that allows the owner of
the library to enter this new book into the system.
Start a new C# Console .Net framework project called Mission3. Add the
following code in bold inside the Main() method:
static void Main ( st r ing [] args)
{
Console.Writ eLi ne("Enter a new book title and author: " );
string newBook = Console.ReadLine();
Console.WriteLine("***** Tiny Library Book Li s t *****" );
Console.WriteLine("The Old Man and the Sea by Hemingway" );
Console.WriteLine(new Book);
Console.ReadKey();
}
After this line, the variable newBook can be used to hold any text string.
The keyword “string” is a data type in C#. Anything a user enters from
keyboard is a string, even a number. You will learn other data types later on.
The newBook is a programmer named variable name. There are simple rules
for naming a variable: 1) begin with a letter, an underscore, or @ symbol.
The remaining part of the name can contain letters, digits and underscore
symbol; 2) whitespaces are not allowed; 3) cannot be a C# keyword, such as
static, Main, or string; 4) case-sensitive, meaning newBook and newbook are
two different names.
The statement Console.ReadLine() receives a line of user input from the
keyboard.
And, string newBook = Console.ReadLine(); receives a line of user input and
assigns the value to the variable newBook. The “=” sign is used to assign a
value to a variable.
The above line of statement can also be broken up into two lines like this:
string newBook;
newBook = Console.ReadLine();
After this, the variable newBook holds the new title and author from the user.
As a result, you can display the content of the variable back on console
screen like displaying a string of text:
Console.WriteLine(newBook);
Note that you don’t use quotation marks around newBook because it is a
string variable, not a string literal value itself.
Great job! You now know how to get user input and display something to the
user.
Take a break to cool down the computer before you continue on Mission 4.
MISSION 4: THE LIBRARY
IS GROWING WITH MORE
BOOKS
The owner of the library is so happy with your application that she decides to
buy more books.
You learned how to add one new book to the system. How difficult could it
be to add more books? To handle multiple books, you can copy and paste the
following two lines of code multiple times.
Console.WriteLine( "Enter the new book title and author: " );
string newBook = Console.ReadLine();
However, you don’t know how many books the library will buy and thus
don’t know how many times you need to copy and paste. Every time, the
owner buys a new book, you have to add two lines of code to the application.
How realistic is that?
A solution is to use a list type to hold as many titles as needed. Unlike a
variable that can hold only one value at a time, a list can hold many values at
a time. You can think of a list as a container that can hold many smaller
containers at once.
Your task in this mission is to enhance the program so that it can ask a user
for many books, one at a time, and display them all.
Start a new C# Console .Net framework project called Mission4 with the
following code inside the Main() method:
List< string > allBooks = new List< string >();
Console.WriteLine( "Enter the new book title and author: " );
string newBook = Console.ReadLine();
while (newBook != "" )
{
allBooks.Add(newBook);
Console.WriteLine( "Enter new book title and author: " );
newBook = Console.ReadLine();
}
Console.WriteLine( "***** Tiny Library Book List *****" );
Console.WriteLine( "The Old Man and the Sea by Hemingway" );
foreach ( string aBook in allBooks)
{
Console.WriteLine(aBook);
}
Console.ReadKey();
To add an element to a list variable, you use the Add() method of the list
type.
Next, take a look at the following code:
while (newBook != "" )
{
allBooks.Add(newBook);
Console.WriteLine( "Enter new book title and author: " );
newBook = Console.ReadLine();
}
This is a while loop. It will repeat the block of statements inside the curly
braces {} again and again until the condition after the keyword “while” is no
longer true. In other words, as long as the newBook is not an empty string,
the block of statements will be executed. That is why after you enter the first
book, the program prompts for the second book. After you enter the second
book, it prompts for the third book. For the third book, instead of entering a
title, you just push the “enter” key, which is an empty string. The while loop
stops looping and the program execution continues after the loop.
A while loop is defined with a keyword “while” followed by a Boolean
condition (true or false). Followed by a pair of curly braces. All statements
that you want to repeat should be inside the pair of curly braces.
The next new statements are the following code:
foreach ( string aBook in allBooks)
{
Console.WriteLine(aBook);
}
This is another type of loop. The foreach loop is often used together with a
list. It is able to loop through every element in a list. Here, the code takes out
one element from the allBooks list at a time and displays it with the
Console.WritelIne() method.
The foreach loop terminates when all elements of the list are read.
Well done! You now know how to use a list type to hold multiple values and
how to use a while loop to add multiple values to a list and use a foreach loop
to display the content of a list.
Take a break to cool down the computer before you continue on Mission 5.
MISSION 5: SAVE THE
BOOK DATA
PERMANENTLY IN A
TEXT FILE
The owner of the library tries to run the application by herself while you are
out for a cup of coffee. She realizes that all books she entered are gone once
the program completes the execution. Is there a way to keep the book data
she enters?
Your task in this mission is to save the book data in an external text file.
Start a new C# Console .Net framework project called Mission5 with the
following code inside the Main() method (the new code statements for saving
the data are displayed in bold):
List< string > allBooks = new List< string >();
Console.WriteLine( "Enter the new book title and author: " );
string newBook = Console.ReadLine();
while (newBook != "" )
{
allBooks.Add(newBook);
Console.WriteLine( "Enter new book title and author: " );
newBook = Console.ReadLine();
}
Console.WriteLine( "***** Tiny Library Book List *****" );
Console.WriteLine( "The Old Man and the Sea by Hemingway" );
foreach ( string aBook in allBooks)
{
Console.WriteLine(aBook);
}
using (StreamWriter sw = File.AppendText( "books.txt" ))
{
foreach ( string aBook in allBooks)
{
sw.WriteLine(aBook);
}
}
Console.ReadKey();
You need to add the following directive to the group of using statements at
the top of the program in order to use the prebuilt StreamWriter and File
classes.
using System.IO;
Save and run the program as you did in Mission 4. The only difference
between this mission and Mission 4 is the book data is now also saved to a
text file in your computer.
Let’s go to find the “books.txt” file and see if our books are saved.
You can search your computer for the Mission5 folder. Then, the bin folder
inside it. Finally, the debug folder inside the bin folder. The “books.txt” file
should be inside the debug folder.
You can open the file with any text editor, but do not modify it. You should
see the two books you entered are saved in the file as two separate lines.
The following statements are new in this mission:
using (StreamWriter sw = File.AppendText( "books.txt" ))
{
foreach ( string aBook in allBooks)
{
sw.WriteLine(aBook);
}
}
The first line allows you to save the content to a file called books.txt. This is
similar to displaying the content to console. You use a foreach loop to write
every element in the allBooks list to the books.txt file.
The prebuilt AppendText() method of the File class makes it possible to
append text to a file. If the file books.txt does not exit, a new file with that
name will be created. If the file already exists, new records will be added to
the end of the file.
Way to go! You now know how to save data to a text file for later use.
Take a break to cool down the computer before you continue on Mission 6.
MISSION 6: RETRIEVE
AND DISPLAY THE BOOKS
FROM A TEXT FILE
After learning how to save data to a text file, your next goal is to learn how to
retrieve the data from the text file.
Your task in this mission is to retrieve and display all books from the
books.txt file.
Start a new C# Console .Net framework project called Mission6 with the
following code inside the Main() method
List< string > allBooks = new List< string >();
Also add using Sytem.IO; statement to the directive group on top of the program.
This is required because the statements inside the Main() method include the
prebuilt class StreamReader.
Copy the books.txt file from Mission5\bin\debug folder to
Mission6\bin\debug folder.
Save and run the program. You should see the two books you saved in the
previous lesson from the text file are displayed on console screen.
The new code used in this mission to retrieve data from the text file is:
using (StreamReader sr = new StreamReader( "books.txt" ))
{
string aBook;
while ((aBook = sr.ReadLine()) != null )
{
allBooks.Add(aBook);
}
}
The code is similar to the statements for writing data to a text file. Instead of
using StreamWriter class, this time you use the StreamReader class.
Whenever you use StreamWriter or StreamReader classes, you need to add
using System.IO directive.
The following code inside the while loop condition: aBook = sr.ReadLine() reads a
line from the text file and assign it to a variable called aBook. Then, (aBook =
sr.ReadLine()) != null, compares the retrieved value to a special value called
“null”. If aBook is not null, the condition is true and the statement block
inside the while loop will be executed. In other words, the book will be added
to the list as long as there are more lines of record in the text file.
The “null” is a special value that actually indicates no value. The != is a
comparison if two values are NOT the same. If two values are not the same,
the result is true. Otherwise, it is false.
When StreamReader.ReadLine() reads a line, it puts a mark at the end of the
line (or the beginning of the next line to be more accurate.). The next time,
ReadLine() is called, the reading will continue from the mark. This is similar
to a bookmark we use when reading a book.
Very good! You now know how to retrieve data from a text file and add the
retrieved data to a list type. From there, you can display it to the console
screen.
Take a break to cool down the computer before you continue on Mission 7.
MISSION 7: ORGANIZING
STATEMENTS BY
METHODS
Your code is getting more complicated. You cannot ask the owner of the
library to run two programs: one to save the data and the other to retrieve the
data. You want to put two features in one application.
Your task in this mission is to add a menu to the application for the user to
choose whether to save or retrieve the data. Then execute corresponding code
based on the user choice.
=== Menu (SELECT A TASK BY NUMBER) ===
1. Add new books.
2. Display all books.
3. Quit the program.
Start a new C# Console .Net framework project called Mission7 with the
following code inside the Main() method
Console.WriteLine( "=== Menu (SELECT A TASK BY NUMBER) ===" );
Console.WriteLine( "1. Add new books." );
Console.WriteLine( "2. Display all books." );
Console.WriteLine( "3. Quit the program." );
string menuSelection = Console.ReadLine();
switch (menuSelection)
{
case "1" :
AddNewBooks();
Console.WriteLine( "The books have been added." );
break ;
case "2" :
DisplayBooks();
Console.WriteLine( "****** End of the list ******" );
break ;
default :
Console.WriteLine( "Thank you for using the system" );
break ;
}
Console.ReadKey();
You may notice that the AddNewBooks() and DisplayBooks() are red
squiggly underlined. These are method calls. A method is a container for a
group of statements. You need to define the two methods to make the
program work. You can think of a method as a smaller container inside a
class container.
Next, add the following code outside of the closing curly brace for the Main()
method, but inside the Program class closing curly brace. The code declares
two methods: AddNewBooks() and DisplayBooks():
If the user enters anything other than 1, 2, or 3, the menu keeps appearing.
Such tasks can be accomplished with a loop.
If the user enters 1 or 2, the program will execute the specific task and return
to the menu for next user selection. Another loop will accomplish this.
Specifically, if the user enters “1”, the instructions for adding new books will
execute. If the user enters “2”, all books saved in the text file will be
displayed. If the user enters “3”, the program will terminate. A switch case
statement fits the purpose.
Start a new C# Console .Net framework project called Mission8 with the
following code inside the Main() method:
int menuSelection;
do
{
DisplayMenu();
menuSelection = int .Parse(Console.ReadLine());
} while (menuSelection < 1 || menuSelection > 3);
bool menuFlag = true ;
while (menuFlag)
{
switch (menuSelection)
{
case 1:
AddNewBooks();
Console.WriteLine( "The books have been added." );
break ;
case 2:
DisplayBooks();
Console.WriteLine( "***** End of the list*****" );
break ;
default :
Console.WriteLine( "Thank you!" );
menuFlag = false ;
break ;
}
if (menuFlag)
{
DisplayMenu();
menuSelection = int .Parse(Console.ReadLine());
}
}
Console.ReadKey();
Next, outside the Main() method, but inside the Program class, add the
DisplayMenu(), AddNewBooks(), and DisplayBooks() methods:
static void DisplayMenu()
{
Console.WriteLine( "===Menu (SELECT A TASK BY NUMBER)===" );
Console.WriteLine( "1. Add new books." );
Console.WriteLine( "2. Display all books." );
Console.WriteLine( "3. Exit the program." );
}
Unlike a string type variable you used in earlier missions, an int variable
allows you to have arithmetic operations on it, e, g. you can compare if the
number is less than 1 (menuSelection <1). You cannot do the same with a
string variable.
We introduce a new type of loop, do while loop, in the following code:
do
{
DisplayMenu();
menuSelection = int .Parse(Console.ReadLine());
} while (menuSelection < 1 || menuSelection > 3);
The do while loop is very similar to while loop you learned in an earlier
mission. The while loop checks a condition before a loop starts. The do while
loop, on the other hand, checks a condition after a loop completes its loop. If
the condition is true, the loop will continue. The do while loop will execute
the block of statements at least once even though the condition may be fault.
The while loop may not execute the block of statements at all if the condition
is false before the first loop.
The next line of statement uses the prebuilt int.Parse() method to parse a
string to an integer. Anything a user enters in the keyboard is a string, even a
number.
menuSelection = int .Parse(Console.ReadLine());
You use the menuFlag variable to control the following while loop:
while (menuFlag)
{
switch (menuSelection)
{
case 1:
AddNewBooks();
Console.WriteLine( "The books have been added." );
break ;
case 2:
DisplayBooks();
Console.WriteLine( "***** End of the list *****" );
break ;
default :
Console.WriteLine( "Thank you for using the system" );
menuFlag = false ;
break ;
}
if (menuFlag)
{
DisplayMenu();
menuSelection = int .Parse(Console.ReadLine());
}
}
With this loop, you can have the menu appear after each task unless the user
selects “3” to exit the program.
The value of menuFlag is true so that the while loop can continue until its
value is set to false. The value is set to false when the user selects 3.
The switch statement is very similar to the one you learned in an earlier
mission. Note the difference between the following two lines
case “1”:
case 1:
The quotation around a number indicates the number is a string type of data.
A number without quotation marks indicates an int type of data.
A switch statement allows the execution of instructions to be selective based
on certain condition. Another similar statement is if statement. The following
code shows the use of an if statement:
if (menuFlag)
{
DisplayMenu();
menuSelection = int .Parse(Console.ReadLine());
}
The two lines of statement inside the curly braces is the body of an if
statement. If the condition is true, the block will be executed. Otherwise, the
program will ignore the block of code.
Superb! In this mission, you have learned the difference between the while
loop and the do while loop. Both are used for iterating. You also learned the
difference between a switch statement and an if statement. Both are used for
selective execution.
Take a break to cool down the computer before you continue on Mission 9.
MISSION 9: PRACTICE
MAKES PERFECT
You have accomplished a lot in the previous eight missions. In this mission,
we want you to develop a project alone.
We need a phone book to keep a record of all contacts with a name and a
phone number. The application will display a console menu to let the user
pick a task:
=== Menu (SELECT A TASK BY NUMBER) ===
1. Add new contacts.
2. Display all contacts.
3. Exit the program.
The contact data should be saved in an external text file called phonebook.txt.
Feel free to check the code in the previous missions. You don’t need to
memorize any of those statements. After you develop several projects, you
will use them like a tool you are familiar with.
Additionally, we recommend that you develop some projects that you can
use, such as keeping a shopping list, or keeping records of miles you walk
every day.
Remember, practice makes perfect.
A summary of the first nine missions: You have learned that an application is
made up of instructions for computers to follow. An application is organized
by namespace, class, and method. All instructions should be inside the
Main() method. Instructions are executed sequentially by default. You can
repeat a certain block of instructions many times by using a loop. You can
also selectively execute certain blocks by using switch or if statements.
Finally, an application is more maintainable by having your own custom
methods. You group a block of statements for the same purpose into a
method. Instructions in a custom method will not be executed until being
called. As a result, the sequence of instructions inside a method makes
different output while the sequence of methods inside a class does not
matter.
MISSION 10: A BOOK HAS
MORE THAN JUST A TITLE
AND AUTHOR
The owner of the library wants to keep more data about the books. For
example, a book has a publishing year, language, genre, the page number,
price purchased, copies purchased, and copies available, and most important,
ISBN (International Standard Book Number). One possible solution is to
duplicate the code for handling the book title and author as you did in the
earlier missions. However, the code will be messy and hard to work with. For
example, if you want to display all books published before the year 2000, it
will be a major challenge.
Another solution is to use “class”. You will create a custom class for book. It
is like a blueprint for buildings or a cookie cutter for cookies. You will have a
book class for books.
The book class defines all properties of the book of the Tiny Library.
Title
Author
ISBN
Language
Genre
Copyright Year
Pages
Price
Total Copies (Copies purchased)
Available Copies (Copies not checked out)
Your task in this mission is to create a class called Book that defines the
characteristics of a book owned by the Tiny Library.
Start a new C# Console .Net framework project called Mission10. In the
“Solution Explorer” window
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mission10
{
class Book
{
}
}
The default content includes a block of five “using” directives which can be
removed if you prefer (you may add them back as needed) or just leave them
alone. It also has a namespace called Mission10. Inside the namespace, it has
a class called Book. You will add content inside the Book class. First, add the
following lines inside the class.
public string Title { get ; set ; }
public string Author { get ; set ; }
public string ISBN { get ; set ; }
public string Language { get ; set ; }
public string Genre { get ; set ; }
public int CopyrightYear { get ; set ; }
public int Pages { get ; set ; }
public double Price { get ; set ; }
public int TotalCopies { get ; set ; }
public int AvailableCopies { get ; set ; }
These are called properties. A property starts with an access modifier that
restricts the accessibility of this property. The “public” access modifier
allows this property to be accessed anywhere. Next, comes a data type. Every
property has a data type much like a variable. Followed by the property
name. By convention, a property name starts with a capital letter and the
naming of a property follows the same naming rules for a variable. The last
part of a property is getter/setter, which allows this property to get the value
from a hidden variable or set the value to the hidden variable. You don’t need
to know anything about this hidden variable. You just need to know this
property.
For example, if you want to get the value from this hidden title variable and
store it in a variable called myTitle, do this:
string myTitle = Title;
If you want to set a value of “The Old Man and the Sea” to the hidden title
variable, do this:
Title = “The Old Man and the Sea”;
Next, still inside the class, add a constructor after the properties:
public Book ( string title, string author, string isbn,
string language, string genre, int copyrightYear, int pages, double price, int totalCopies,
int availableCopies)
{
Title = title;
Author = author;
ISBN = isbn;
Language = language;
Genre = genre;
CopyrightYear = copyrightYear;
Pages = pages;
Price = price;
TotalCopies = totalCopies;
AvailableCopies = availableCopies;
}
A constructor of a class is a method without the return data type. The purpose
of a constructor is to create an instance of the class by initializing its field
values. Similar to a cookie cutter that allows you to cut a cookie, a
constructor of a class allows you to create a book with initial values. The
class of the book defines the structure of books. An instance (often called an
object) of a book class is a specific book (e. g. The Old Man and the Sea with
purchase price of 9.99 etc.).
You have just created a custom class that can be used throughout the
application.
Let’s go to the Main() method of the Program.cs file as you have done in all
previous missions. Add the following lines inside the Main() method:
Book myFavoriteBook = new Book( "The Old Man and The Sea" , "Ernest Hemingway" , "123456789"
"English" , "Fiction" , 1952, 160, 19.99, 3, 3);
Console.WriteLine(myFavoriteBook);
Console.ReadKey();
The “new” keyword in the statement actually calls the constructor of the
Book class. So the arguments inside the pair of parentheses after new Book()
should match the constructor of the Book class. For example, the first
argument should match the first parameter in the constructor. If you pass
“19.99” as the first argument to the constructor, then this book’s title will be
19.99! If the data types do not match, you will see a compiler error.
If you save and run the program, you will see Mission10.Book appears on the
console screen. To make the real book information on the screen, you need to
modify the Book class by adding a ToString() method to the class. The
method will be automatically called when you print an object.
Go to the Book.cs file. Add the following code inside the Book class, after
the constructor (after the constructor’s closing curly brace).
public override string ToString()
{
string str;
str = $"Title: {Title} \nAuthor: { Author} \nISBN: {ISBN} " ;
str += $"\nLanguage: { Language} \nGenre: { Genre} " ;
str += $"\nYear: { CopyrightYear} \nPages: { Pages} " ;
str += $"\nPrice: { Price} \nTotal Copies: { TotalCopies} " ;
str += $"\nAvailable Copies: { AvailableCopies} " ;
return str;
}
The access modifier for this method is public that means it is available
anywhere. The “override” keyword means this method inherits from another
class with the same method name. You must provide specific format for the
data in the current class. The return data type for this method is “string”.
Inside the ToString() method, you return the book data well formatted. The $
sign is for string interpolation. An interpolated string is like a template string
with dynamic content inside a pair of burly braces. The “\n” indicates a line
break. The += operator is for string concatenation that combines strings into
one longer string.
Save and run the program. You will see the details of the book displayed on
the console screen.
Title: The Old Man and The Sea
Author: Ernest Hemingway
ISBN: 123456789
Language: English
Genre: Fiction
Year: 1952
Pages: 160
Price: 19.99
Total Copies: 3
Available Copies: 3
Terrific. You have created a custom class in this mission. A class is a way to
programming that simplifies the coding. Your Book class has a group of
properties, a constructor, and a ToString() method.
Take a break to cool down the computer before you continue on Mission 11.
MISSION 11: CAN WE
HAVE MORE THAN ONE
AUTHOR FOR A BOOK?
If you just add more authors to a book, it will be difficult for the computers to
know the name of each author. Most authors have first name and last name.
However, an author may have a middle name. In addition, you may not know
how many authors are in a book. All these make it difficult to handle the
authors of a book.
One solution is to make the Author property of the Book class a list type so
that you can add as many authors to the list as you need to and still be able to
distinct names in the author list.
Your task in this mission is to update the Author property of the Book class
and update all corresponding part in the constructor and the ToString()
method.
Start a new C# Console .Net framework project called Mission11. In the
“Solution Explorer” window, right click on “C# Mission11” (not Solution).
In the context menu, select “Add”. On the submenu, select “class”. On the
popup window, name this class “Book.cs” (without quotation marks). Then
click on the “Add” button on the bottom right to create an empty class called
Book.
Copy all content of the Book class (not the whole file because they have
different namespaces) from the previous mission to this class with the
following updates.
First, update the Author property
public string Author { get ; set ; }
to the following:
public List<string > Authors { get ; set ; }
This time, the type is no longer just a string, it is a list of strings. The
properties now look like (the difference is in bold font):
public string Title { get ; set ; }
public List< string > Authors { get ; set ; }
public string ISBN { get ; set ; }
public string Language { get ; set ; }
public string Genre { get ; set ; }
public int CopyrightYear { get ; set ; }
public int Pages { get ; set ; }
public double Price { get ; set ; }
public int TotalCopies { get ; set ; }
public int AvailableCopies { get ; set ; }
Second, copy and update the constructor to the following (the difference is in
bold font):
public Book ( string title, List< string > authors , string isbn,
string language, string genre, int copyrightYear, int pages, double price, int totalCopies,
availableCopies)
{
Title = title;
Authors = authors;
ISBN = isbn;
Language = language;
Genre = genre;
CopyrightYear = copyrightYear;
Pages = pages;
Price = price;
TotalCopies = totalCopies;
AvailableCopies = availableCopies;
}
You use a foreach loop to add each individual author to the output string.
Finally, you need to add the following statements to the Main() method of the
Program.cs file.
List< string > authors1 = new List< string >() { "Hemingway" };
Book myFavoriteBook1 = new Book( "The Old Man and The Sea" ,
authors1, "123456789" , "English" ,
"Fiction" , 1952, 160, 19.99, 3, 3);
Console.WriteLine(myFavoriteBook1);
List< string > authors2 = new List< string >() { "Alan Moore" ,
"Dave Gibbons" };
Book myFavoriteBook2 = new Book( "Watchmen" , authors2,
"223456789" , "English" , "Mystery" ,
2019, 416, 29.99, 3, 3);
Console.WriteLine(myFavoriteBook2);
Console.ReadKey();
The first book is almost the same as the code in the last mission. An author
cannot be directly added to a book constructor. You need to create a list and
put that author in the list and then use the list in the book. This is a little
difficult to understand. Even a single author must be put in a list because the
constructor wants a list.
To declare a list of string types:
List<string > authors1 = new List<string >()
The second book starts with declaring a list and initialize it with two authors:
List<string > authors2 = new List<string >() { "Alan Moore" , "Dave Gibbons" };
Save and run the program. You should see the two books correctly displayed.
Terrific! In this mission, you have learned how to use a list in a class. Such
technique allows you to construct complex classes.
Take a break to cool down the computer before you continue on Mission 12.
.
MISSION 12: USE THE
BOOK CLASS IN THE TINY
LIBRARY PROGRAM
Using a class allows the program to handle more properties of a book.
Writing code to work with 10 properties of a book is almost the same as
handling one property thanks to the Book class.
Your task in this mission is to update the Tiny Library application you
created in Mission 8 with a custom Book class.
Copy the Mission 8 folder and name this new folder Mission12. Do not
rename any files inside. This way, if you make any errors, you still have a
working version. Open Mission12 project with Visual Studio by double
clicking on Mission8.sln file inside the Mission12 folder.
First, go to the “Solution Explorer” window. Add a class called Book.cs to
the project with the following content (Visual Studio generates the Book
class header and its pair of curly braces when you add the class, we include
them here for your comparison purpose):
class Book
{
public string Title { get ; set ; }
public List< string > Authors { get ; set ; }
public string ISBN { get ; set ; }
public string Language { get ; set ; }
public string Genre { get ; set ; }
public int CopyrightYear { get ; set ; }
public int Pages { get ; set ; }
public double Price { get ; set ; }
public int TotalCopies { get ; set ; }
public int AvailableCopies { get ; set ; }
public Book ( string title, List< string > authors, string
isbn, string language, string genre, int
copyrightYear, int pages, double price, int
totalCopies, int availableCopies)
{
Title = title;
Authors = authors;
ISBN = isbn;
Language = language;
Genre = genre;
CopyrightYear = copyrightYear;
Pages = pages;
Price = price;
TotalCopies = totalCopies;
AvailableCopies = availableCopies;
}
Next, update the AddNewBooks() method inside the Program.cs class with
the following code (We keep the old code as comment-out for your
comparison purpose. You add “ //” in front of a line to tell compiler to ignore
the statement.):
Save and run the program. When there are no more additional authors, just
push the “Enter” key to signal that there are no more authors. Similarly, when
there are no more new books, just push the “Enter” key to terminate the data
input.
In this updated AddNewBook() method, a user can enter more than one
author and enter as many books as desired.
A do while loop is used for author input. This allows multiple authors for a
book. Each author is now an element of the authors list. It is easy to access
each author.
do
{
Console.WriteLine( "Enter author no. " + authorCount + ": " );
author = Console.ReadLine();
if (author != "" )
{
authors.Add(author);
authorCount++;
}
} while (author != "" );
Wonderful! In this mission, you have learned how to use a custom class in an
application.
Take a break to cool down the computer before you continue on Mission 13
MISSION 13: ENUM FOR
GENRE
One problem with the book data entry is that users can enter incorrect data,
such as genre can be fiction or fictions, or other variations. To avoid such
errors and to limit the user choice for genre, you can use an Enum data type.
You can think of an enum type as a class which contains a group of
constants. A variable of an enum data type can only store one of those values.
Enum members cannot have an access modifier. It is always public.
Your task in this mission is to create an enum data type so that the genre of
the books in the Tiny Library can only be one of the following: Romance,
Mystery, Fantasy, Thriller, Fiction, Inspiration, and Biography.
Open Mission12 project in Visual Studio. Go to “Solution Explorer” window
and double click on Book.cs to open it. Add the following statements to
declare an enum data type. Add the code right inside the namespace, but
outside the Book class:
Then update the corresponding property of the Book class (we keep the old
code as comment-out for comparison purpose):
// public string Genre { get; set; }
public BookGenre Genre { get ; set ; }
The data type for Genre used to be string. Now it is the BookGenre type you
declared earlier.
Also update the Constructor of the Book class:
//public Book(string title, List<string> authors, string isbn,
// string language, string genre, int
// copyrightYear, int pages,
// double price, int totalCopies, int
// availableCopies)
public Book ( string title, List< string > authors, string isbn,
string language, BookGenre genre, int
copyrightYear, int pages,
double price, int totalCopies, int
availableCopies)
You use a prebuilt method of TryParse(). The method will do two things: 1)
Convert a string to a BookGenre enum type. 2) Return a Boolean value. If the
conversion is successful, the method will return a true value. Otherwise, it
will return a false value. You add a “!” in front of the method to turn the
Boolean value to an opposite value. “!” is an Boolean operator for NOT. If
the value is true, the NOT operator will turn it to false. If the value is false,
the NOT operator will turn it to true.
In our example, if the conversion is NOT successful, we will prompt the user
to enter the genre again. Otherwise, the execution will move on for
statements after the do while loop.
Save and run the program. Enter some spelling variations of a genre (note the
case of the characters) and see how the application works.
Wonderful! You have learned how to create an enum data type and how to
use it to limit the possible values for the genre of a book.
Take a break to cool down the computer before you continue on Mission 14.
MISSION 14: GRAPHICAL
USER INTERFACE (GUI)
Eventually, an application for our Tiny Library needs a graphical user
interface. Windows Forms Application is a quick way to develop such an
application.
Your task in this mission is to get familiar with Windows Forms Application
and design the main form, add the new book form, and add the author form to
replace the console application of the Tiny Library.
Start a new project the same way as you did for Console application, but on
the “Create a new project” window, search for “Windows Forms App (.NET
Framework)”.
You can double click on the “Windows Forms App (.NET Framework) panel
to move on to the next window or single click to select it and click on the
“Next” button to continue.
On the “Configure your new project” window, name the project “Mission14”
(no quotation marks) and click on the “Create” button to have a new
Windows Forms Application.
You will see a blank windows form on the screen.
You can add controls to the form. A control, such as a label control, a textbox
control, or a button control can be added to the form to make the form
interactive and event driven.
You can find all controls on the Toolbox window:
A toolbox is a window that should appear on the left side by default. If you
don’t see one, you can go to “View” on the menu, then toolbox, or just hit
control+Alt+x on keyboard.
Your tasks in this and the next few missions are to convert the Tiny Library
Program from a console application to a graphical user interface with
enhanced features.
Let’s add a button onto this first form. Go to the Toolbox and expand
“Common Controls”. Double clicking on the “Button” will add one button to
the Form (drag a control from toolbox to a form will also work).
By default, this button on the form should be selected. If not, click on the
button to select it. Then go to the Properties window (located on the bottom
right by default). If not found, you can right click on the button and select
property:
Finally, add the button event handlers to make the program work.
Go to Form1 in design view. Double click on the “Add a New Book” button.
This will generate the button click event handler in the code view:
private void addNewBookButton_Click( object sender,
EventArgs e)
{
All the statements you want to execute when the user clicks on the button
should be inside this pair of curly braces. All you need is to open the
AddNewBookForm. Add two lines of statement inside (we show the method
header for your comparison convenience):
private void addNewBookButton_Click( object sender,
EventArgs e)
{
AddNewBookForm addNewBookForm = new AddNewBookForm();
addNewBookForm.ShowDialog();
}
The above code will open the Add Author Form when the “Add Author”
button of the Add New Book Form is clicked at run time.
In addition to creating an instance of AddAuthorForm, you also pass the
“this.authorsListBox” object to the new form. The keyword “this” indicates
the current form which has a control called authorsListBox. You pass this
control to the new form so that items (author names) can be added to the
listbox on current form from the new form.
Go to the “AddAuthorForm.cs” in code view (in Solution Explorer window,
with the AddNewBookForm.cs selected, click on the <> icon to open a file in
code view).
You may want to read the above two paragraphs one more time. The
AddNewBookForm passes an authorListBox to the AddAuthorForm. The
latter needs a variable to hold it. The value is passed on through the
constructor.
Go to the AddAuthorForm in design view. Double click on the “Add Author”
button to create an event handler to add a new author to the authorListBox.
Save and run the program. You should be able to add one or more authors to
the AddNewBookForm. Nothing else works at this time.
Note: if you ever see “To prevent possible data loss before loading the
designer, the following errors must be resolved:” error, click on the “Go to
code” on top right of the window and comment out the error line.
Splendid! In this lesson, you have learned how to start a Windows Forms
application. You have learned how to add a new form to the project, how to
add controls to a form, and how to write simple statements to make a form
interactive.
Take a break to cool down the computer before you continue on Mission 15.
MISSION 15: GRAPHICAL
USER INTERFACE (GUI)
CONTINUED
Have the Mission14 project open in Visual Studio before continuing.
Your task in this mission is to make the “Remove Author” button, “Add
Book” button, and “Close Form and Save Books” button on the
AddNewBookForm work.
Go to “Solution Explorer” window, double click on AddNewBookForm.cs to
open it in design view. Double click on the “Remove Author” button to
generate the button event handler. Add code below (bold code only):
private void removeAuthorButton_Click( object sender,
EventArgs e)
{
if (authorsListBox.SelectedIndex != -1)
{
authorsListBox.Items
.Remove(authorsListBox.SelectedItem);
}
else
{
MessageBox.Show( "Select an author to remove." );
}
}
Save and run the program. You can now add an author and remove an author
as needed.
The authorsListBox.SelectedIndex != -1 checks if any item on the listbox is
selected. When nothing on a listbox is selected. The selectedIndex property
of the listbox will equal to -1.
Next, you are going to add a new book with the authors to a list.
First, go to the “Solution Explorer” window. Add a class called Book.cs to
the project with the following content:
class Book
{
public string Title { get ; set ; }
public List< string > Authors { get ; set ; }
public string ISBN { get ; set ; }
public string Language { get ; set ; }
public string Genre { get ; set ; }
public int CopyrightYear { get ; set ; }
public int Pages { get ; set ; }
public double Price { get ; set ; }
public int TotalCopies { get ; set ; }
public int AvailableCopies { get ; set ; }
public Book ( string title, List< string > authors, string
isbn, string language, string genre, int
copyrightYear, int pages, double price,
int totalCopies, int availableCopies)
{
Title = title;
Authors = authors;
ISBN = isbn;
Language = language;
Genre = genre;
CopyrightYear = copyrightYear;
Pages = pages;
Price = price;
TotalCopies = totalCopies;
AvailableCopies = availableCopies;
}
Save and run the code. You can add a new book with author(s) to the
allBooks list, but not to the books.txt file yet.
Finally, make the “Close Form and Save Books” button work.
Go to AddNewBookForm in design view. Double click on the “Close Form
and Save Books” button and add the code (Remember to add using
Systems.IO; in the directive group on top of the file because you use the
prebuilt StreamWriter class):
private void closeFormButton_Click( object sender, EventArgs e)
{
using (StreamWriter sw = File.AppendText( "books.txt" ))
{
//foreach (string aBook in allBooks)
foreach (Book aBook in allBooks)
{
sw.WriteLine(aBook);
}
}
Close();
}
Go back to the Form1 in design view. Double click on the “Display All
Books” button and add the following code in bold:
private void displayAllButton_Click( object sender, EventArgs e)
{
DisplayAllForm displayAllForm = new DisplayAllForm();
displayAllForm.ShowDialog();
}
Finally, still inside the add book button click event handler, you need to make
sure the user selected a genre before the book can be added. This is similar to
checking if an author is added to a book. Add the following if statement right
after the code of checking if at least one author is added:
if (genreComboBox.SelectedIndex == -1)
{
allFieldsFilled = false ;
}
Splendid! In this mission, you have learned how to use ComboBox for a
dropdown menu in windows forms applications.
Take a break to cool down the computer before you continue on Mission 18
MISSION 18: GRAPHICAL
USER INTERFACE (GUI)
UPDATE A BOOK
You have successfully migrated the Tiny Library application from console to
windows forms. The owner of the library wants more functionalities. A user
may enter wrong data when adding a new book. There must be a way to
update an existing book in the system.
Your task in this mission is to add a feature to allow a user to update an
existing book.
Have the Mission14/15/16/17 project open before continuing.
Open the Form1 in design view. Add a button with the (Name) property set to
“updateBookButton” and the Text property set to “Update a book”.
The following figure shows the form after the properties are set.
Start coding.
Go to the “Solution Explorer” window and double click on Form1.cs to open
it in design view. Double click on the “Update a book” button on Form1 and
add the following code:
private void updateBookButton_Click( object sender, EventArgs e)
{
UpdateBookForm updateBookForm = new UpdateBookForm();
updateBookForm.ShowDialog();
}
To retrieve records from the text file and put them in the list, you need the
form load event handler. Go to the UpdateBookForm in design view, Double
click on any empty space on the form and add the code. Note the list
declaration is already in the file and thus should not be duplicated. We show
it here so that you can compare the code (you will need using System.IO
directive because the StreadReader class is used):
List<Book> allBooks = new List<Book>();
private void UpdateBookForm_Load( object sender, EventArgs e)
{
using (StreamReader sr = new StreamReader( "books.txt" ))
{
string title;
while ((title = sr.ReadLine()) != null )
{
title = getContent(title);
List< string > allAuthors = new List< string >();
string author = "" ;
while ((author = sr.ReadLine()).Substring(0, 6)
== "Author" )
{
author = getContent(author);
allAuthors.Add(author);
}
string isbn = getContent(author);
string language = getContent(sr.ReadLine());
string genre = getContent(sr.ReadLine());
int year = int .Parse(getContent(sr.ReadLine()));
int pages = int .Parse(getContent(sr.ReadLine()));
double price =
double .Parse(getContent(sr.ReadLine()));
int totalCopies =
int .Parse(getContent(sr.ReadLine()));
int availableCopies =
int .Parse(getContent(sr.ReadLine()));
Book newBook = new Book(title, allAuthors, isbn,
language, genre, year, pages,
price, totalCopies, availableCopies);
allBooks.Add(newBook);
}
}
}
Use a method called getContent() to retrieve the part of text file with just the
content, not with the row header added. For example, the book title is “The
Old Man and the Sea”. The record saved in the books.txt file is “Title: The
Old Man and the Sea”. You don’t want to save the word “Title: “ in the list.
You use a prebuilt string.IndexOf() method of the string to find the position
number of a string. The counting starts from zero. Suppose you have the
following statements:
string message = “I love C#.”;
int position = message.IndexOf(“ve”);
The position value will be 4. The IndexOf() method returns an integer for the
location position of the first appearance of the string you search for. The
space also counts as a character.
You also use a prebuilt string.Substring() method to return a substring of a
given string with certain position requirements. If you pass only a number, it
will return the substring beginning on that position and to the end of the
string.
string message = “I love C#.”;
string substring = message.Substring(4);
The substring value will be “love”. The number “2” indicates the beginning
position of 3rd character (remember, the position counting starts from 0). The
second number “4” indicates that the length of the substring is four
characters.
Note the list of allBooks is declared outside the event handler so that other
event handlers can use its values.
Next, you want to search a book by its ISBN.
Go to the “Solution Explorer” windows and double click on
UpdateBookForm.cs to open it in design view. Double click on the search
button of the UpdateBookForm and add the code:
private void searchButton_Click( object sender, EventArgs e)
{
foreach (Book book in allBooks)
{
if (book.ISBN == searchISBNTextBox.Text)
{
titleTextBox.Text = book.Title;
isbnTextBox.Text = book.ISBN;
languageTextBox.Text = book.Language;
genreComboBox.ResetText();
genreComboBox.SelectedText = book.Genre;
yearTextBox.Text =book.CopyrightYear.ToString();
pagesTextBox.Text = book.Pages.ToString();
priceTextBox.Text = book.Price.ToString();
totalCopiesTextBox.Text =
book.TotalCopies.ToString();
availableCopiesTextBox.Text =
book.AvailableCopies.ToString();
authorsListBox.Items.Clear();
authorsListBox.Items
.AddRange(book.Authors.ToArray());
}
}
}
You are using an existing form, AddAuthorForm for adding a new author.
You used the form in an earlier mission when you want to add a new book.
Then, make the “Remove Author” button on the UpdateBookForm work.
Keep UpdateBookForm in design view. Double click on the “Remove
Author” button and add the following code:
private void removeAuthorButton_Click( object sender,
EventArgs e)
{
if (authorsListBox.SelectedIndex != -1)
{
authorsListBox.Items
.Remove(authorsListBox.SelectedItem);
}
else
{
MessageBox.Show( "Select an author to remove." );
}
}
Save and run the program. You should see that authors can be added to or
removed from the book.
Next, make the “Update book” button work.
Still have the UpdateBookForm in design view. Double click on the “Update
book” button and add the following code:
Save and run the program. You may not see the book being updated if you
close the form and go to display all books. This is because you only update
the books in the list. The list has not been saved back to the text file.
The major structure of the above code is similar to the “Add new book”
feature. We would like to further explain the following line from the code
above:
Book bookToUpdate = allBooks.Find(b => b.ISBN == searchISBNTextBox.Text);
It finds a book from the allBooks list that matches the given ISBN and
assigns the found book to the bookToUpdate variable.
The statement uses a C# technique called Lambda, with the symbol =>. It is a
shorthand way to write a method. On the left side of the => is like an input to
a method (parameter list). On the right side of the => is the output of the
method (method body). In the above Lambda expression, it will accept a
book from allBooks, one at a time, and return a Boolean value by comparing
the given book’s ISBN to the user typed in ISBN. If the two ISBNs match, it
returns the true value. Otherwise, return the false value. Based on the
Boolean value, the Find() method determines which value of the allBooks list
to keep.
Finally, make the “Close and Save” button on UpdateBookForm work.
Have the UpdateBookForm in design view. Double click on the “Close and
Save” button and add the following code:
Save and run the program. You can update any book. Use “display all books”
feature to check if the application works as expected.
Splendid. This is a long mission, but most statements are similar to the “Add
new book” feature. In this mission, you learned how to retrieve data from a
text file and put them into objects (books in our case) instead of just working
with a string as you learned in displaying all books mission. With all books in
a list of Book objects, it is much easier to handle them. You have also learned
Lambda expressions to find a book from the allBooks list. The concise code
of a Lambda maybe difficult to understand at first, but it will be your friend
when you become a developer.
Take a break to cool down the computer before you continue on Mission 19.
MISSION 19: GRAPHICAL
USER INTERFACE (GUI)
DELETE A BOOK
Sometimes it is necessary to delete a book from the system.
Your task in this mission is to add a feature that allows a user to delete a book
by its ISBN.
Open the project from the previous mission in Visual Studio to continue.
Go to “Solution Explorer” window. Double click on Form1.cs to open it in
design view. Add a button to the form. Set the (Name) property of the button
to “deleteButton” and the Text property of the button to “Delete a book”
if (!isFound)
{
bookDetailsListBox.Items.Clear();
bookDetailsListBox.Items.Add( "No book ISBN found." );
}
}
To make the “Delete” button work, double click on the “Delete” button and
add the following code:
private void deleteButton_Click( object sender, EventArgs e)
{
if (isFound)
{
allBooks.RemoveAll(b => b.ISBN ==
searchISBNTextBox.Text);
MessageBox.Show( "The book is deleted." );
bookDetailsListBox.Items.Clear();
isFound = false ;
}
else
{
MessageBox.Show( "No books to delete!" );
}
}
Set properties for the controls by following the table below (Empty cell
indicates no changes necessary):
Start coding.
Start from Form1.cs in design view. Double click on the “Checkout a book”
button and add the following code:
private void checkoutButton_Click( object sender, EventArgs e)
{
CheckoutForm checkoutForm = new CheckoutForm();
checkoutForm.ShowDialog();
}
When the CheckoutForm loads, you want to retrieve the books from the text
file and load them into a list.
Go to the CheckoutForm in design view. Double click on any empty space on
the form to generate the form loads event handler. Add the code below
(again, the allBooks list is declared outside the form load event handler):
List<Book> allBooks = new List<Book>();
private void CheckoutForm_Load( object sender, EventArgs e)
{
using (StreamReader sr = new StreamReader( "books.txt" ))
{
string title;
while ((title = sr.ReadLine()) != null )
{
title = getContent(title);
List< string > allAuthors = new List< string >();
string author = "" ;
while ((author = sr.ReadLine()).Substring(0, 6)
== "Author" )
{
author = getContent(author);
allAuthors.Add(author);
}
string isbn = getContent(author);
string language = getContent(sr.ReadLine());
string genre = getContent(sr.ReadLine());
int year = int .Parse(getContent(sr.ReadLine()));
int pages =
int .Parse(getContent(sr.ReadLine()));
double price =
double .Parse(getContent(sr.ReadLine()));
int totalCopies =
int .Parse(getContent(sr.ReadLine()));
int availableCopies =
int .Parse(getContent(sr.ReadLine()));
Book newBook = new Book(title, allAuthors, isbn,
language, genre, year, pages, price,
totalCopies, availableCopies);
allBooks.Add(newBook);
}
}
}
private string getContent( string str)
{
int position = str.IndexOf( ": " );
return str.Substring(position + 2);
}
The above code is similar to the search code in previous missions. But this
time, you don’t search by ISBN. You search for any titles that contain the
search string. Both the search string and the book title are converted to all
upper case by the prebuilt ToUpper() method. As a result, you don’t need to
worry about the case.
In the above code, you also see a Where() method of list (IEnumerable to be
exact). For beginners, you can treat Where() the same as Find() method of a
list. It will return all elements that meet the condition.
Next, make the checkout button on CheckoutForm work.
Go to CheckoutForm in design view. Double click on the checkout button.
You want to deduct the available copy of the book by one if the number is
more than 0. Otherwise, display a message that no copies of the book are
available.
private void checkoutButton_Click( object sender, EventArgs e)
{
if (booksListBox.SelectedIndex != -1)
{
string selectedBook = booksListBox.SelectedItem.ToString();
int isbnStartPosition = selectedBook.IndexOf( "ISBN: " ) + 6;
int isbnEndPosition = selectedBook.IndexOf( "Language: " ) -2;
int isbnLength = isbnEndPosition - isbnStartPosition + 1;
string isbn = selectedBook.Substring(isbnStartPosition,
isbnLength);
int availableCopiesPosition =
selectedBook.IndexOf( "Available Copies: " );
int availableCopies =
int .Parse(selectedBook.Substring(availableCopiesPosition +
17));
if (availableCopies > 0)
{
Book bookToCheckout = allBooks.Find(b => b.ISBN == isbn);
bookToCheckout.AvailableCopies--;
MessageBox.Show( "Checkout completed." );
searchButton_Click( this , e);
}
else
{
MessageBox.Show( "No copies available" );
}
}
else
{
MessageBox.Show( "You must select a book to checkout." );
}
}
One business logic in the above code is to find the length of a string from a
certain position to another certain position in order to retrieve the number for
available copies.
Given the following string:
abcdefg
What is the length of the substring from position index of 2 to position index
of 5? Remember to count from zero. If we include the position of 5, the
answer is 4 (cdef). If you don’t include the position of 5, the answer is 3
(cde). The formula is ending position minus starting position plus 1.
Why does the following statement have a plus 6?
int isbnStartPosition = selectedBook.IndexOf( "ISBN: " ) + 6;
It is because the value from IndexOf is the position of “I” in “ISBN: “. Plus 6
to move the start position from “I” to the one right after the space in “ISBN:
“.
The following statement:
bookToCheckout.AvailableCopies--;
Finally, make the “Save and Close” button on CheckoutForm work. Double
click on the “Save and Close” button to add code:
private void closeButton_Click( object sender, EventArgs e)
{
using (StreamWriter sw = File.CreateText( "books.txt" ))
{
foreach (Book aBook in allBooks)
{
sw.WriteLine(aBook);
}
}
Close();
}
Save and run the program.
Fantastic! In this mission you have learned the Where() method of a list and
apply complex business logic into the application.
Take a break to cool down the computer before you continue on Mission 21.
MISSION 21: GRAPHICAL
USER INTERFACE (GUI)
RETURN A BOOK
Returning a book is very similar to checking out a book. The user interface is
the same except that the “checkout” button is replaced with a “return” button.
The user searches for a book by its title. All books contain the search string
are displayed in a listbox. Then the user selects a book from the listbox and
clicks on the “return” button to return it. The result of returning a book is to
increase the available copies by one. The available number copy should never
be greater than the total copies.
Your task in this mission is to allow a user to return a book.
Have the Tiny Library Windows Forms Application (Mission 20) open in
Visual Studio. Go to Form1 in design view. Add a button. Set its (Name)
property to returnBookButton and its Text property to “Return a book”. Add
another button. Set its (Name) property to exitButton and its Text property to
“Exit”. The Form1 now looks like this:
Go to “Solution Explorer” window. Add a form named “ReturnBookForm”.
Add two label controls, three button controls, one textbox, and one listbox to
the form.
Note: If your booksListBox is not wide enough, not all book information may
be shown. You can add a horizontal scroll bar to the listbox by setting its
HorizontalScrollBar property from the default “false” to “true”.
Start coding.
Start from Form1.cs in design view. Double click on the “Return a book”
button on Form1 and add the code below:
private void returnBookButton_Click( object sender,
EventArgs e)
{
ReturnBookForm returnBookForm = new ReturnBookForm();
returnBookForm.ShowDialog();
}
Still with Form1 in design view, double click on the “Exit” button and add
the code below:
private void exitButton_Click( object sender, EventArgs e)
{
Close();
}
The above code is used in several previous missions for retrieving data from
a text file.
Still with ReturnBookForm in design view. Double click on the “Search”
button and add the following code:
private void searchButton_Click( object sender, EventArgs e)
{
List<Book> booksFound = new List<Book>();
booksFound = allBooks.Where(b => b.Title.ToUpper()
.Contains(searchTitleTextBox.Text.ToUpper())).ToList();
booksListBox.Items.Clear();
foreach (Book book in booksFound)
{
string bookstring = book.ToString().Replace( "\n" , " " );
booksListBox.Items.Add(bookstring);
}
}
Double click on the “Print” button and add the one line code:
private void printButton_Click( object sender, EventArgs e)
{
printDocument1.Print();
}
The above statement calls the Print() method of printDocument1 control.
Before you can print the books receipt in this checkout, you need a list of
checked out books. To keep it simple, we assume all books checked out
before closing the current form are by the same borrower. Additionally, you
only list the ISBN of a book on the receipt. Add the following line (may go to
CheckoutForm in code view) anywhere inside the class, but outside any
methods. The checkoutBooks list is used to hold all books checked out in the
current form before closing the form.
List<string > checkoutBooks = new List<string >();
Then inside the checkout button click event handler, add the book isbn to the
checkoutBooks list. Right before the following line:
MessageBox.Show("Checkout completed." );
namespace Mission14
{
static class UtilityManager
{
public static List<Book> RetrieveBooks()
{
List<Book> allBooks = new List<Book>();
return allBooks;
}
}
}
Note the adding of “static” keyword to make both the class and method static.
A static method allows the use of the method without creating an instance.
This is convenient. A static class can only have static members and cannot be
instantiated. To access a member of a static class, you just use
ClassName.MemberName.
Open the “UpdateBookForm.cs” in file code view.
Go to “Solution Explorer” window, click on the UpdateBookForm.cs to
select it. Then on the top menu of the explorer window, find <> symbol and
click on it.
Cut all the code inside the UpdateBookForm_Load event handler. Paste the
code inside the RetrieveBooks() method of the UtilityManager.cs class
between the two existing lines of the statement.
List<Book> allBooks = new List<Book>();
// paste your code here.
return allBooks;
You will need the using System.IO; directive because the code includes the
prebuilt StreamReader class.
Next inside the UpdateBookForm.cs, cut the whole getContent() method and
paste it inside the UtilityManager.cs class, but outside the RetrieveBooks()
method. You may notice that the sequence of methods inside a class does not
matter because they are not executed like statements. A method has to be
called to execute all statements inside it.
After pasting getContent(), add a keyword “static” (no quotation marks) right
after the “private” keyword in the method header. This is because the
getContent() method is used in a static method RetrieveBooks(). Only a static
method can be used in another static method.
The completed UtilityManager.cs file now looks like this:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mission14
{
static class UtilityManager
{
public static List<Book> RetrieveBooks()
{
List<Book> allBooks = new List<Book>();
using (StreamReader sr = new
StreamReader( "books.txt" ))
{
string title;
while ((title = sr.ReadLine()) != null )
{
title = getContent(title);
List< string > allAuthors = new
List< string >();
string author = "" ;
while ((author = sr.ReadLine())
.Substring(0, 6) == "Author" )
{
author = getContent(author);
allAuthors.Add(author);
}
string isbn = getContent(author);
string language =
getContent(sr.ReadLine());
string genre = getContent(sr.ReadLine());
int year =
int .Parse(getContent(sr.ReadLine()));
int pages =
int .Parse(getContent(sr.ReadLine()));
double price =
double .Parse(getContent(sr.ReadLine()));
int totalCopies =
int .Parse(getContent(sr.ReadLine()));
int availableCopies =
int .Parse(getContent(sr.ReadLine()));
Book newBook = new Book(title, allAuthors,
isbn, language, genre, year, pages,
price, totalCopies, availableCopies);
allBooks.Add(newBook);
}
}
return allBooks;
}
private static string getContent( string str)
{
int position = str.IndexOf( ": " );
return str.Substring(position + 2);
}
}
}
Save and run the program. You should not see any differences.
To call a static method, you just use classname.methodname as:
UtilityManager.RetrieveBooks();
You want to do the same for the DeleteBookForm.cs, because you already
created the RetrieveBooks() method. This time, you just delete the duplicated
code and use the RetrieveBooks() method in the DeleteBookForm.cs.
Go to the DeleteBookForm.cs in code view. Replace the content of the
DeleteBookForm load event handler with the following line:
private void DeleteBookForm_Load( object sender, EventArgs e)
{
allBooks = UtilityManager.RetrieveBooks();
}
If the user clicks on the “Add New” button, a new form is displayed as shown
below. This form uses a new control called GroupBox for organization
purposes. The form also needs three label controls, three textbox controls and
three button controls.
The save button on the form will save the new employee into a text file,
employee.txt, in the default debug folder. The Clear button will clear the
form and the Close button will close the form.
Back to the MainForm in design view, if the “Add Hours” button is clicked, a
new form is displayed. This form also uses a GroupBox control, three label
controls, three textbox controls, and three button controls.
The first two of the three TextBoxes on the right column are “readonly”. Set
their ReadOnly property to true. A read only textbox will not accept user
input. They are used in the form for displaying employee data.
When the “Add Hours Worked” form loads, the first employee from
employee.txt should be displayed. Then the user enters hours worked and
clicks on the “next” button. The hours worked should be added to the
employee in the List, and the second employee data is displayed and expects
the user to enter hours worked. When all hours worked data are entered, a
pop up will display “no more employees.” The user can then click on the
“Close” button, which will close the form and save all data from the list to
the same employee.txt file. For each employee, only the most recent hours
worked data is saved in the employee.txt file. In other words, each employee
has only one hours worked data.
Back to the MainForm in design view, when the Display All button is
clicked, the following form will be displayed. This form includes one listbox
and two button controls.
The “Print” button on the form will print all employee data in a nicely
formatted way on paper. The “Close” button will close the form.
Back to the main form in design view, the “Exit” button will exist the
program.
You are not expected to complete this project without any references. You
can go back to the earlier missions and copy and paste code.
Congratulations, and thank you for reading this book. Enjoy coding.