Examples of Code Using Capabilities of C# 9 and C# 10
1 Processing Data about Books on Bookshelf (Laboratory Training 1)
The only significant addition related to the verification of potentially possible null
value is recommended in the statement in which the string is entered from the keyboard. Instead of the statement
string sequence = Console.ReadLine();
the following statement should be used:
string sequence = Console.ReadLine() ?? "";
This will make it impossible to assign the null
value.
2 The Hierarchy of Bookshelves (Laboratory Training 2)
Considering the features of C# 9 and C# 10, we can modify the program code in this way:
using System; namespace LabSecond { // Structure that represents an author public struct Author { public string Surname, Name; // Definition of the equivalence public override bool Equals(object? obj) { if (obj == null) { return false; } Author author = (Author)obj; return author.Surname == Surname && author.Name == Name; } // Definition of string representation: public override string ToString() { return Name + " " + Surname; } // Defined paired with Equals() public override int GetHashCode() { return base.GetHashCode(); } public static bool operator ==(Author left, Author right) { return left.Equals(right); } public static bool operator !=(Author left, Author right) { return !(left == right); } } // Book public class Book { public string Title { get; set; } public int Year { get; set; } public Author[] Authors { get; set; } // Constructor public Book(string title, int year, params Author[] authors) { Title = title; Year = year; Authors = authors; } // Definition of string representation // string.Format() provides a format similar to Console.WriteLine() public override string ToString() { string s = string.Format("Title: \"{0}\". Year of publication: {1}", Title, Year); s += "\n" + " Authors:"; for (int i = 0; i < Authors.Length; i++) { s += string.Format(" {0}", Authors[i]); if (i < Authors.Length - 1) { s += ","; } else { s += "\n"; } } return s; } // Definition of the equivalence public override bool Equals(object? obj) { if (obj is Book b) { if (b.Authors.Length != Authors.Length) { return false; } for (int i = 0; i < Authors.Length; i++) { if (!b.Authors[i].Equals(Authors[i])) { return false; } } return b.Title == Title && b.Year == Year; } return false; } // Defined paired with Equals() public override int GetHashCode() { return base.GetHashCode(); } } // Bookshelf public class Bookshelf { public Book[] Books { get; set; } // Constructor public Bookshelf(params Book[] books) { Books = books; } // Indexer public Book this[int index] { get { return Books[index]; } set { Books[index] = value; } } // Definition of string representation public override string ToString() { string result = ""; foreach (Book book in Books) { result += book; } return result; } // Looking for books with a certain sequence of characters public Book[] ContainsCharacters(string characters) { Book[] found = Array.Empty<Book>(); foreach (Book book in Books) { if (book.Title.Contains(characters)) { // Add a new item: Array.Resize(ref found, found.Length + 1); found[^1] = book; } } return found; } // Adding book public void Add(Book book) { Book[] books = Books; Array.Resize(ref books, Books.Length + 1); Books = books; Books[^1] = book; } // Removal of a book with the specified data public void Remove(Book book) { int i, k; Book[] newBooks = new Book[Books.Length]; for (i = 0, k = 0; i < Books.Length; i++, k++) { if (Books[i].Equals(book)) { k--; } else { newBooks[k] = Books[i]; } } if (i > k) { Array.Resize(ref newBooks, Books.Length - 1); } Books = newBooks; } // Overloaded operator of adding book public static Bookshelf operator +(Bookshelf bookshelf, Book book) { Bookshelf newBookshelf = new(bookshelf.Books); newBookshelf.Add(book); return newBookshelf; } // Overloaded operator of removal book public static Bookshelf operator -(Bookshelf bookshelf, Book book) { Bookshelf newBookshelf = new(bookshelf.Books); newBookshelf.Remove(book); return newBookshelf; } } // Titled bookshelf public class TitledBookshelf : Bookshelf { public string Title { get; set; } public TitledBookshelf(string title, params Book[] books) : base(books) { Title = title; } // Definition of string representation public override string ToString() { return Title + "\n" + base.ToString(); } // Overloaded operator of adding book public static TitledBookshelf operator +(TitledBookshelf titled, Book book) { TitledBookshelf newBookshelf = new(titled.Title, titled.Books); newBookshelf.Add(book); return newBookshelf; } // Overloaded operator of removal book public static TitledBookshelf operator -(TitledBookshelf titled, Book book) { TitledBookshelf newBookshelf = new(titled.Title, titled.Books); newBookshelf.Remove(book); return newBookshelf; } } class Program { static void Main() { // Create an empty shelf: Bookshelf bookshelf = new(); // Adding books bookshelf += new Book("The UML User Guide", 1999, new Author() { Name = "Grady", Surname = "Booch" }, new Author() { Name = "James", Surname = "Rumbaugh" }, new Author() { Name = "Ivar", Surname = "Jacobson" }); bookshelf += new Book("Pro C# 2010 and the .NET 4 Platform", 2010, new Author() { Name = "Andrew", Surname = "Troelsen" }); bookshelf += new Book("Thinking in Java", 2005, new Author() { Name = "Bruce", Surname = "Eckel" }); // Display source data: Console.WriteLine(bookshelf); Console.WriteLine(); // Looking for books with a certain sequence of characters: Console.WriteLine("Enter sequence of characters:"); string sequence = Console.ReadLine() ?? ""; Bookshelf newBookshelf = new(bookshelf.ContainsCharacters(sequence)); // Output to the screen: Console.WriteLine("The found books:"); Console.WriteLine(newBookshelf); Console.WriteLine(); // Remove Java book Book javaBook = bookshelf[2]; // indexer bookshelf -= javaBook; Console.WriteLine("After removal of the book:"); Console.WriteLine(bookshelf); Console.WriteLine(); // Create a new shelf TitledBookshelf titledBookshelf = new TitledBookshelf("Java"); titledBookshelf += javaBook; Console.WriteLine("New bookshelf:"); Console.WriteLine(titledBookshelf); } } }
The code uses a pattern matching mechanism, additional control of nullable types, new opportunities for creating objects (new
()
), overloading operations ==
and !=
, as well as the mechanism of obtaining indices through the use of Index
type.
3 Creating a Hierarchy of Generic Classes (Laboratory Training 3)
The example of this laboratory work contains a class library and a console application. The following code should be allocated in the class library:
using System.Xml.Serialization; namespace BookshelfLib { // Structure that represents an author public struct Author { [System.Xml.Serialization.XmlAttributeAttribute()] public string Surname { get; set; } [System.Xml.Serialization.XmlAttributeAttribute()] public string Name { get; set; } // Definition of the equivalence public override bool Equals(object? obj) { if (obj == null) { return false; } Author author = (Author)obj; return author.Surname == Surname && author.Name == Name; } // Definition of string representation: public override string ToString() { return Name + " " + Surname; } // Defined paired with Equals() public override int GetHashCode() { return base.GetHashCode(); } public static bool operator ==(Author left, Author right) { return left.Equals(right); } public static bool operator !=(Author left, Author right) { return !(left == right); } } // Book public class Book<TAuthor> { [System.Xml.Serialization.XmlAttributeAttribute()] public string Title { get; set; } [System.Xml.Serialization.XmlAttributeAttribute()] public int Year { get; set; } public List<TAuthor> Authors { get; set; } // Constructors public Book() { Title = ""; Authors = new List<TAuthor>(); } public Book(string title, int year, params TAuthor[] authors) { Title = title; Year = year; Authors = new List<TAuthor>(authors); } // Definition of string representation override public string ToString() { string s = string.Format("Title: \"{0}\". Year of publication: {1}", Title, Year); s += "\n" + " Author(s):"; for (int i = 0; i < Authors.Count; i++) { s += string.Format(" {0}", Authors[i]); if (i < Authors.Count - 1) { s += ","; } else { s += "\n"; } } return s; } // Definition of the equivalence public override bool Equals(object? obj) { if (obj is Book<TAuthor> b) { if (b.Authors.Count != Authors.Count) { return false; } for (int i = 0; i < Authors.Count; i++) { if (!b.Authors[i].Equals(Authors[i])) { return false; } } return b.Title == Title && b.Year == Year; } return false; } // Defined paired with Equals() public override int GetHashCode() { return base.GetHashCode(); } } // Bookshelf public class Bookshelf<TAuthor> { public List<Book<TAuthor>> Books { get; set; } // Constructor public Bookshelf(params Book<TAuthor>[] books) { Books = new List<Book<TAuthor>>(books); } // Indexer public Book<TAuthor> this[int index] { get { return Books[index]; } set { Books[index] = value; } } // Definition of string representation override public string ToString() { string result = ""; foreach (Book<TAuthor> book in Books) { result += book; } return result; } // Looking for books with a certain sequence of characters public List<Book<TAuthor>> ContainsCharacters(string characters) { List<Book<TAuthor>> found = new(); foreach (Book<TAuthor> book in Books) { if (book.Title.Contains(characters)) { // Adding a new element to a list: found.Add(book); } } return found; } // Adding book public void Add(Book<TAuthor> book) { Books.Add(book); } // Removal of a book with the specified data public void Remove(Book<TAuthor> book) { Books.Remove(book); } // Reading books using deserialization mechanism public bool ReadBooks(string fileName) { XmlSerializer deserializer = new(typeof(List<Book<TAuthor>>)); using TextReader textReader = new StreamReader(fileName); var data = deserializer.Deserialize(textReader); if (data == null) { return false; } Books = (List<Book<TAuthor>>)data; return true; } // Saving books using serialization mechanism public void WriteBooks(string fileName) { XmlSerializer serializer = new(typeof(List<Book<TAuthor>>)); using TextWriter textWriter = new StreamWriter(fileName); serializer.Serialize(textWriter, Books); } // Nested class for comparison books in alphabetical order of names class CompareByTitle : IComparer<Book<TAuthor>> { public int Compare(Book<TAuthor>? b1, Book<TAuthor>? b2) { if (b1 == null || b2 == null) { return 0; } return string.Compare(b1.Title, b2.Title); } } // Nested class for comparison books by the number of authors class CompareByAuthorsCount : IComparer<Book<TAuthor>> { public int Compare(Book<TAuthor>? b1, Book<TAuthor>? b2) { if (b1 == null || b2 == null) { return 0; } return b1.Authors.Count < b2.Authors.Count ? -1 : (b1.Authors.Count == b2.Authors.Count ? 0 : 1); } } // Sort by name public void SortByTitle() { Books.Sort(new CompareByTitle()); } // Sort by count of authors public void SortByAuthorsCount() { Books.Sort(new CompareByAuthorsCount()); } // Overloaded operator of adding book public static Bookshelf<TAuthor> operator +(Bookshelf<TAuthor> bookshelf, Book<TAuthor> book) { Bookshelf<TAuthor> newShelf = new() { Books = bookshelf.Books }; newShelf.Add(book); return newShelf; } // Overloaded operator of removal book public static Bookshelf<TAuthor> operator -(Bookshelf<TAuthor> bookshelf, Book<TAuthor> book) { Bookshelf<TAuthor> newShelf = new() { Books = bookshelf.Books }; newShelf.Remove(book); return newShelf; } } // Titled bookshelf public class TitledBookshelf<TAuthor> : Bookshelf<TAuthor> { public string Title { get; set; } // Constructor with parameters public TitledBookshelf(string title, params Book<TAuthor>[] books) : base(books) { Title = title; } // Definition of string representation override public string ToString() { return Title + "\n" + base.ToString(); } // Overloaded operator of adding book public static TitledBookshelf<TAuthor> operator +(TitledBookshelf<TAuthor> titled, Book<TAuthor> book) { TitledBookshelf<TAuthor> newShelf = new(titled.Title) { Books = titled.Books }; newShelf.Add(book); return newShelf; } // Overloaded operator of removal book public static TitledBookshelf<TAuthor> operator -(TitledBookshelf<TAuthor> titled, Book<TAuthor> book) { TitledBookshelf<TAuthor> newShelf = new(titled.Title) { Books = titled.Books }; newShelf.Remove(book); return newShelf; } } }
The code that represents the console application will be as follows:
using BookshelfLib; namespace BookshelfApp { class Program { static void Main() { // Create an empty shelf: Bookshelf<Author> bookshelf = new(); // Adding books bookshelf += new Book<Author>("The UML User Guide", 1999, new Author() { Name = "Grady", Surname = "Booch" }, new Author() { Name = "James", Surname = "Rumbaugh" }, new Author() { Name = "Ivar", Surname = "Jacobson" }); bookshelf += new Book<Author>("Pro C# 2010 and the .NET 4 Platform", 2010, new Author() { Name = "Andrew", Surname = "Troelsen" }); bookshelf += new Book<Author>("Thinking in Java", 2005, new Author() { Name = "Bruce", Surname = "Eckel" }); // Display source data: Console.WriteLine(bookshelf); Console.WriteLine(); // Looking for books with a certain sequence of characters: Console.WriteLine("Enter sequence of characters:"); string sequence = Console.ReadLine() ?? ""; Bookshelf<Author> newBookshelf = new() { Books = bookshelf.ContainsCharacters(sequence) }; // Output to the screen: Console.WriteLine("The found books:"); Console.WriteLine(newBookshelf); Console.WriteLine(); try { // Save data concerned with books: bookshelf.WriteBooks("Bookshelf.xml"); // Sort by name and save in a file: bookshelf.SortByTitle(); Console.WriteLine("By title:"); Console.WriteLine(bookshelf); Console.WriteLine(); bookshelf.WriteBooks("ByTitle.xml"); // Sort by count of authors and save in a file: bookshelf.SortByAuthorsCount(); Console.WriteLine("By count of authors:"); Console.WriteLine(bookshelf); Console.WriteLine(); bookshelf.WriteBooks("ByAuthorsCount.xml"); // Restore the first shelf in the original version bookshelf.ReadBooks("Bookshelf.xml"); Console.WriteLine("Original state:"); Console.WriteLine(bookshelf); Console.WriteLine(); // Remove a book about Java Book<Author> javaBook = bookshelf[2]; // indexer bookshelf -= javaBook; Console.WriteLine("After removal of the book:"); Console.WriteLine(bookshelf); Console.WriteLine(); // Create a new shelf. Use string type to store data TitledBookshelf<string> titledBookshelf = new TitledBookshelf<string>("Java"); titledBookshelf += new Book<string>("Thinking in Java", 2005, "Bruce Eckel"); Console.WriteLine("Shelf with books on Java language:"); Console.WriteLine(titledBookshelf); titledBookshelf.WriteBooks("JavaBooks.xml"); } catch (Exception ex) { Console.WriteLine("-----------Exception:-----------"); Console.WriteLine(ex.GetType()); Console.WriteLine("------------Message:------------"); Console.WriteLine(ex.Message); Console.WriteLine("----------Stack Trace:----------"); Console.WriteLine(ex.StackTrace); } } } }
As in the code of previous lab, the code contains a pattern matching mechanism, additional control of nullable types, new opportunities for creating objects (new
()
), overloading operations ==
and !=
for value types. Also unnecessary using
directives are deleted.
4 Creation of WPF Application for Working with Bookshelf (Laboratory Training 4)
The example of this laboratory training provides extension of the class library. The source code of BookshelfWithLINQ
class will be like this:
// BookshelfWithLINQ.cs namespace BookshelfLib { public class BookshelfWithLINQ<TAuthor> : Bookshelf<TAuthor> { // Constructor public BookshelfWithLINQ(params Book<TAuthor>[] books) : base(books) { } public new List<Book<TAuthor>> ContainsCharacters(string characters) { var found = from book in Books where book.Title.Contains(characters) select book; // return the search result found, // creating on its basis a new list of type List<Book<TAuthor>> // to ensure match types: return new List<Book<TAuthor>>(found); } // Sort by name: public new void SortByTitle() { Books = new List<Book<TAuthor>>( from book in Books orderby book.Title select book); } // Sort by count of authors: public new void SortByAuthorsCount() { Books = new List<Book<TAuthor>>( from book in Books orderby book.Authors.Count select book); } // Overloaded operator of adding book public static BookshelfWithLINQ<TAuthor> operator + (BookshelfWithLINQ<TAuthor> bookshelf, Book<TAuthor> newBook) { var newBooks = new List<Book<TAuthor>>(bookshelf.Books) { newBook }; return new BookshelfWithLINQ<TAuthor>() { Books = newBooks }; } // Overloaded operator of removal book. // Using LINQ is purely illustrative public static BookshelfWithLINQ<TAuthor> operator - (BookshelfWithLINQ<TAuthor> bookshelf, Book<TAuthor> oldBook) { var newBooks = new List<Book<TAuthor>>( from book in bookshelf.Books where !book.Equals(oldBook) select book); return new BookshelfWithLINQ<TAuthor>() { Books = newBooks }; } } }
Appropriate console application:
using BookshelfLib; namespace BookshelfWithLINQApp { class Program { static void Main(string[] args) { // Create an empty shelf: BookshelfWithLINQ<Author> bookshelf = new BookshelfWithLINQ<Author>(); // Adding books bookshelf += new Book<Author>("The UML User Guide", 1999, new Author() { Name = "Grady", Surname = "Booch" }, new Author() { Name = "James", Surname = "Rumbaugh" }, new Author() { Name = "Ivar", Surname = "Jacobson" }); bookshelf += new Book<Author>("Pro C# 2010 and the .NET 4 Platform", 2010, new Author() { Name = "Andrew", Surname = "Troelsen" }); bookshelf += new Book<Author>("Thinking in Java", 2005, new Author() { Name = "Bruce", Surname = "Eckel" }); // Display source data: Console.WriteLine(bookshelf); Console.WriteLine(); // Looking for books with a certain sequence of characters: Console.WriteLine("Enter sequence of characters:"); string sequence = Console.ReadLine() ?? ""; BookshelfWithLINQ<Author> newBookshelf = new() { Books = bookshelf.ContainsCharacters(sequence) }; // Output to the screen: Console.WriteLine("The found books:"); Console.WriteLine(newBookshelf); Console.WriteLine(); try { // Save data concerned with books: bookshelf.WriteBooks("Bookshelf.xml"); // Sort by name and save in a file: bookshelf.SortByTitle(); Console.WriteLine("By title:"); Console.WriteLine(bookshelf); Console.WriteLine(); bookshelf.WriteBooks("ByTitle.xml"); // Sort by count of authors and save in a file: bookshelf.SortByAuthorsCount(); Console.WriteLine("By count of authors:"); Console.WriteLine(bookshelf); Console.WriteLine(); bookshelf.WriteBooks("ByAuthorsCount.xml"); // Create a new shelf. Use string type to store data TitledBookshelf<string> titledBookshelf = new("Java"); titledBookshelf += new Book<string>("Thinking in Java", 2005, "Bruce Eckel"); Console.WriteLine("Shelf with books on Java language:"); Console.WriteLine(titledBookshelf); titledBookshelf.WriteBooks("JavaBooks.xml"); // Restore the first shelf in the original version bookshelf.ReadBooks("Bookshelf.xml"); Console.WriteLine("Original state:"); Console.WriteLine(bookshelf); Console.WriteLine(); // Remove a book about Java Book<Author> javaBook = bookshelf[2]; // indexer bookshelf -= javaBook; Console.WriteLine("After removal of the book:"); Console.WriteLine(bookshelf); Console.WriteLine(); } catch (Exception ex) { Console.WriteLine("-----------Exception:-----------"); Console.WriteLine(ex.GetType()); Console.WriteLine("------------Message:------------"); Console.WriteLine(ex.Message); Console.WriteLine("----------Stack Trace:----------"); Console.WriteLine(ex.StackTrace); } } } }
The file with the XAML code of the main window remains unchanged:
<Window x:Class="WpfBookshelfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfBookshelfApp" mc:Ignorable="d" Title="Bookshelf" Height="450" Width="685"> <DockPanel> <Grid Height="54" DockPanel.Dock="Top" VerticalAlignment="Top"> <Button Content="Open" Height="42" HorizontalAlignment="Left" Margin="5,6,0,0" Name="ButtonOpen" VerticalAlignment="Top" Width="103" Click="ButtonOpen_Click"/> <Button Height="42" HorizontalAlignment="Left" Margin="115,6,0,0" Name="ButtonSortByTitle" VerticalAlignment="Top" Width="103" Click="ButtonSortByTitle_Click"> <Grid> <TextBlock Text="Sort by Title" TextWrapping="Wrap" TextAlignment="Center" /> </Grid> </Button> <Button Height="42" HorizontalAlignment="Left" Margin="225,6,0,0" Name="ButtonSortByAuthorsCount" VerticalAlignment="Top" Width="103" Click="ButtonSortByAuthorsCount_Click"> <Grid> <TextBlock Text="Sort by Authors Count" TextWrapping="Wrap" TextAlignment="Center" /> </Grid> </Button> <TextBox Height="24" HorizontalAlignment="Left" Margin="335,14,0,0" Name="TextBoxSearch" VerticalAlignment="Top" Width="109" /> <Button Content="Search" Height="42" HorizontalAlignment="Left" Margin="450,6,0,0" Name="ButtonSearch" VerticalAlignment="Top" Width="103" Click="ButtonSearch_Click"/> <Button Content="Save" Height="42" HorizontalAlignment="Left" Margin="560,6,0,0" Name="ButtonSave" VerticalAlignment="Top" Width="103" Click="ButtonSave_Click"/> </Grid> <DataGrid AutoGenerateColumns="False" Height="130" Name="DataGridAuthors" DockPanel.Dock="Bottom" VerticalAlignment="Bottom"> <DataGrid.Columns> <DataGridTextColumn Header="Name" Width="100" x:Name="ColumnName" /> <DataGridTextColumn Header="Surname" Width="100" x:Name="ColumnSurname" /> </DataGrid.Columns> </DataGrid> <DataGrid AutoGenerateColumns="False" Name="DataGridBooks" SelectionChanged="DataGridBooks_SelectionChanged"> <DataGrid.Columns> <DataGridTextColumn Header="Title" Width="350" x:Name="ColumnTitle" /> <DataGridTextColumn Header="Year" x:Name="ColumnYear" /> </DataGrid.Columns> <DataGrid.ContextMenu> <ContextMenu> <MenuItem Header="Add item" Name="MenuItemAdd" Click="MenuItemAdd_Click"/> <MenuItem Header="Remove Item" Name="MenuItemRemove" Click="MenuItemRemove_Click"/> </ContextMenu> </DataGrid.ContextMenu> </DataGrid> </DockPanel> </Window>
Similarly, we leave unchanged the description of an additional window:
<Window x:Class="WpfBookshelfApp.WindowSearchResults" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfBookshelfApp" mc:Ignorable="d" Title="Search Results" Height="300" Width="600"> <Grid> <TextBox Name="TextBoxSearchResults" /> </Grid> </Window>
MainWindow
and Author
class code will be like that:
using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using BookshelfLib; namespace WpfBookshelfApp { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private BookshelfWithLINQ<Author> bookshelf = new(); public MainWindow() { InitializeComponent(); // Add an empty book: bookshelf += new Book<Author>() { Title = "", Authors = new List<Author> { new Author() { Name = "", Surname = "" } } }; InitGrid(); } void InitGrid() { // Bind the DataGridBooks table to the book list: DataGridBooks.ItemsSource = bookshelf.Books; DataGridBooks.CanUserAddRows = false; // Indicate which columns are associated with what properties: ColumnTitle.Binding = new Binding("Title"); ColumnYear.Binding = new Binding("Year"); // Show authors for the selected book: ShowAuthors(DataGridBooks.Items.IndexOf(DataGridBooks.SelectedItem)); } private void ShowAuthors(int index) { // If wrong index, set the index to 0 if (index < 0 || index >= bookshelf.Books.Count) { index = 0; } // Bind the DataGridAuthors with a list of authors: DataGridAuthors.ItemsSource = bookshelf.Books[index].Authors; DataGridAuthors.CanUserAddRows = false; // Indicate which columns are associated with what properties: ColumnName.Binding = new Binding("Name"); ColumnSurname.Binding = new Binding("Surname"); } private void DataGridBooks_SelectionChanged(object sender, SelectionChangedEventArgs e) { // Display the authors of the book selected in DataGridBooks: ShowAuthors(DataGridBooks.Items.IndexOf(DataGridBooks.SelectedItem)); } private void MenuItemAdd_Click(object sender, RoutedEventArgs e) { // Confirm changes in the table: DataGridBooks.CommitEdit(); // Add an empty book: bookshelf += new Book<Author>() { Title = "", Authors = new List<Author> { new Author() { Name = "", Surname = "" } } }; InitGrid(); } private void MenuItemRemove_Click(object sender, RoutedEventArgs e) { // Determine the index of the active line: int index = DataGridBooks.SelectedIndex; // Confirm changes in the table: DataGridBooks.CommitEdit(); // Remove the active row: bookshelf.Books.RemoveAt(index); // If all the rows are deleted, add a new one empty: if (bookshelf.Books.Count == 0) { bookshelf = new BookshelfWithLINQ<Author>(); bookshelf += new Book<Author>("", 0, new Author()); InitGrid(); } DataGridBooks.ItemsSource = null; InitGrid(); } private void ButtonOpen_Click(object sender, RoutedEventArgs e) { // Create a dialog windows and adjust its properties: Microsoft.Win32.OpenFileDialog dlg = new(); dlg.InitialDirectory = System.AppDomain.CurrentDomain.BaseDirectory; // current folder dlg.DefaultExt = ".xml"; dlg.Filter = "Файли XML (*.xml)|*.xml|Усі файли (*.*)|*.*"; if (dlg.ShowDialog() == true) { try { bookshelf.ReadBooks(dlg.FileName); } catch (Exception) { MessageBox.Show("Error reading from file"); } InitGrid(); } } private void ButtonSave_Click(object sender, RoutedEventArgs e) { // Create a dialog windows and adjust its properties: Microsoft.Win32.SaveFileDialog dlg = new(); dlg.InitialDirectory = System.AppDomain.CurrentDomain.BaseDirectory; dlg.DefaultExt = ".xml"; dlg.Filter = "Файли XML (*.xml)|*.xml|Усі файли (*.*)|*.*"; if (dlg.ShowDialog() == true) { try { bookshelf.WriteBooks(dlg.FileName); MessageBox.Show("File saved"); } catch (Exception) { MessageBox.Show("Error writing to file"); } } } private void ButtonSortByTitle_Click(object sender, RoutedEventArgs e) { bookshelf.SortByTitle(); InitGrid(); } private void ButtonSortByAuthorsCount_Click(object sender, RoutedEventArgs e) { bookshelf.SortByAuthorsCount(); InitGrid(); } private void ButtonSearch_Click(object sender, RoutedEventArgs e) { string sequence = TextBoxSearch.Text; if (sequence.Length == 0) { return; } // Find books: var found = bookshelf.ContainsCharacters(sequence); // Form a result string: string text = ""; foreach (var book in found) { text += book + "\n"; } // Create a new window: WindowSearchResults windowSearchResults = new(); windowSearchResults.TextBoxSearchResults.Text = text; windowSearchResults.ShowDialog(); } } // Class that represents an author public class Author { [System.Xml.Serialization.XmlAttributeAttribute()] public string Surname { get; set; } = ""; [System.Xml.Serialization.XmlAttributeAttribute()] public string Name { get; set; } = ""; // Definition of the equivalence public override bool Equals(object? obj) { if (obj == null) { return false; } Author author = (Author)obj; return author.Surname == Surname && author.Name == Name; } // Definition of string representation: public override string ToString() { return Name + " " + Surname; } // Defined paired with Equals2() public override int GetHashCode() { return base.GetHashCode(); } } }
The code contains additional control of nullable types, new opportunities for creating objects (new
()
) and initialization of lists and properties. Also unnecessary using
directives are deleted.