2022-07-09 20:23:10 +00:00
using DataModel.Database.Common ;
2022-07-09 13:06:49 +00:00
using Microsoft.Extensions.DependencyInjection ;
using Processor ;
using UI_WinForms.Dialogs ;
using UI_WinForms.Resources ;
namespace UI_WinForms.Components ;
public partial class SectionTree : UserControl
{
private readonly DataContext db ;
public SectionTree ( )
{
this . InitializeComponent ( ) ;
// Get the DI context from the main form:
this . db = Program . SERVICE_PROVIDER . GetService < DataContext > ( ) ! ;
// Dispose of the context when the control is disposed:
this . Disposed + = ( _ , _ ) = > this . db . Dispose ( ) ;
// Create an image list from a resource:
var imgList = new ImageList ( ) ;
imgList . ImageSize = new Size ( 45 , 45 ) ;
imgList . ColorDepth = ColorDepth . Depth32Bit ;
imgList . Images . Add ( Icons . icons8_documents_folder_512 ) ;
// Set the image list to the tree view:
this . treeView . ImageList = imgList ;
// Subscribe to the load event:
2022-07-09 19:24:09 +00:00
this . Load + = this . LoadNodes ;
2022-07-09 13:06:49 +00:00
}
2022-07-09 19:24:09 +00:00
private async void LoadNodes ( object? sender , EventArgs e )
2022-07-09 13:06:49 +00:00
{
// A dictionary to cache all known tree nodes:
var treeNodes = new Dictionary < string , TreeNode > ( ) ;
// Get the max. depth of the tree:
var maxDepth = await SectionProcessor . GetDepth ( this . db ) ;
// Store nodes, where we cannot find the parents:
var missingParents = new List < TreeNode > ( ) ;
// Populate the tree view out of the database, layer by layer:
for ( var i = 0 ; i < = maxDepth ; i + + )
{
await foreach ( var section in SectionProcessor . LoadLayer ( this . db , i ) )
{
// Create the tree node:
var node = new TreeNode
{
Name = section . DataKey , // [sic] name is the key
Text = section . Name ,
StateImageIndex = 1 ,
} ;
// Cache the node:
treeNodes . Add ( section . DataKey , node ) ;
// Is this a root node?
if ( section . Depth is 0 )
// Set the root node:
this . treeView . Nodes . Add ( node ) ;
// Otherwise, attach this section to its parent node:
else
{
// Get the parent from our cache within O(1):
treeNodes . TryGetValue ( section . Parent ? . DataKey ? ? string . Empty , out var parent ) ;
// If the parent node is not found, skip this section:
if ( parent is null )
{
missingParents . Add ( node ) ;
continue ;
}
// Add the node to the parent:
parent . Nodes . Add ( node ) ;
}
}
}
// If we found any missing parents, show a dialog:
if ( missingParents . Any ( ) )
{
MessageBox . Show ( $"In {missingParents.Count} case(s) we could not found the matching parent. We added these nodes to a special root node, though." , "Parent not found" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
// Create a root node for all missing parents:
var rootMissedParents = new TreeNode
{
Name = "MISSING_PARENTS" ,
Text = "Missing Parents" ,
StateImageIndex = 1 ,
} ;
// Add the root node to the tree:
this . treeView . Nodes . Add ( rootMissedParents ) ;
// Add all missing parents to the root node:
foreach ( var node in missingParents )
rootMissedParents . Nodes . Add ( node ) ;
}
2022-07-09 20:23:10 +00:00
// Expand the tree:
this . treeView . ExpandAll ( ) ;
2022-07-09 13:06:49 +00:00
}
private async void buttonAdd_Click ( object sender , EventArgs e )
{
2022-07-09 13:29:22 +00:00
var result = InputDialog . Show ( new InputDialog . Options (
Message : "Please type the desired section name." ,
Title : "Add a section" ,
Placeholder : "My next section" ,
ShowQuestionCheckbox : true ,
QuestionCheckboxText : "Add a root node (i.e. ignoring the selected node)"
) ) ;
2022-07-09 13:06:49 +00:00
if ( result . DialogResult = = DialogResult . Cancel )
return ;
2022-07-09 13:29:22 +00:00
var addRootNode = result . AnswerToQuestion ;
2022-07-09 13:06:49 +00:00
// Get the currently selected section as parent:
var selectedNode = this . treeView . SelectedNode ;
// Add the new section to the database:
2022-07-09 13:29:22 +00:00
var addedSection = await SectionProcessor . AddSection ( this . db , result . Text , addRootNode ? null : selectedNode ? . Name ) ;
2022-07-09 13:06:49 +00:00
// Add the new section to the tree control:
var node = new TreeNode
{
Name = addedSection . DataKey , // [sic] name is the key
Text = addedSection . Name ,
StateImageIndex = 1 ,
} ;
2022-07-09 13:29:22 +00:00
if ( ! addRootNode & & selectedNode is not null )
2022-07-09 13:06:49 +00:00
selectedNode . Nodes . Add ( node ) ;
else
this . treeView . Nodes . Add ( node ) ;
// Ensure, that the added node is visible and gets the focus:
node . EnsureVisible ( ) ;
this . treeView . SelectedNode = node ;
}
2022-07-09 19:24:09 +00:00
private async void buttonRemove_Click ( object sender , EventArgs e )
{
// Get the currently selected section, which will be removed:
var selectedNode = this . treeView . SelectedNode ;
// Get the number of children:
// (notice, that the node's name is its key)
var numberChildren = await SectionProcessor . NumberChildren ( this . db , selectedNode . Name ) ;
// Ask the user, if he really wants to remove the section:
2022-07-10 09:19:50 +00:00
if ( MessageBox . Show ( numberChildren > 0 ? $"Are you sure, you want to remove the section '{selectedNode.Text}', its {numberChildren} children and so on?" : $"Are you sure, you want to remove the section '{selectedNode.Text}'?" , "Remove section" , MessageBoxButtons . YesNo , MessageBoxIcon . Question , MessageBoxDefaultButton . Button2 ) = = DialogResult . No )
2022-07-09 19:24:09 +00:00
return ;
// Remove the section from the database:
// (notice, that the node's name is its key)
await SectionProcessor . RemoveSection ( this . db , selectedNode . Name ) ;
// Remove all nodes from the tree control:
this . treeView . Nodes . Clear ( ) ;
// Reload the tree:
this . LoadNodes ( this , EventArgs . Empty ) ;
}
private void treeView_NodeMouseClick ( object sender , TreeNodeMouseClickEventArgs e )
{
// Get the currently selected section:
var selectedNode = this . treeView . SelectedNode ;
// If the selected node is not null, enable the remove button:
this . buttonRemove . Enabled = selectedNode is not null ;
}
2022-07-09 13:06:49 +00:00
}