#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;
using SmartInstitute.Client.Automation;
using SmartInstitute.ObjectModel;
using SmartInstitute;
using SmartInstitute.Client.Automation.Helpers;
using System.Drawing.Drawing2D;
#endregion
namespace SmartInstitute.Client.Modules
{
public partial class OfferedCourseTree : TreeView
{
private Font _BoldFont;
#region Construction
public OfferedCourseTree()
{
InitializeComponent();
//base.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
this._BoldFont = new Font(base.Font, FontStyle.Bold);
}
public void Load()
{
// if( !base.DesignMode )
// this.PopulateDummyData();
_Application.Courses.OnItemChange += new ItemChangeHandler(Courses_OnItemChange);
this.LoadCourses();
}
#endregion
#region Tree Population
/// <summary>
/// Reload the course tree
/// </summary>
public void LoadCourses()
{
this.BeginUpdate();
this.Nodes.Clear();
foreach (CourseModel courseModel in _Application.Courses)
{
// Add the course node
this.AddCourseNode(courseModel);
}
this.EndUpdate();
}
/// <summary>
/// If the course node is added in the tree, refresh its status
/// otherwise add the course
/// </summary>
/// <param name="courseModel"></param>
private void RefreshCourse( CourseModel courseModel )
{
CourseNode node = this.FindCourseNode(courseModel);
if (null == node)
this.AddCourseNode(courseModel);
else
this.UpdateCourseNode(courseModel, node);
}
/// <summary>
/// Find the tree node which contains the specified course node
/// </summary>
/// <param name="courseModel"></param>
/// <returns></returns>
private CourseNode FindCourseNode( CourseModel courseModel )
{
foreach (CourseNode node in base.Nodes)
{
if (node.Course.ID == courseModel.Course.ID)
return node;
}
return null;
}
/// <summary>
/// Create a course node in the tree
/// </summary>
/// <param name="courseModel"></param>
private void AddCourseNode( CourseModel courseModel )
{
// 1. Create the course node
CourseNode courseNode = new CourseNode(courseModel.Course);
// 2. Create section nodes for the course
this.AddSectionNodes(courseNode, courseModel.Course.CourseSectionCollection);
// 3. Add the node in the tree
base.Nodes.Add(courseNode);
base.SelectedNode = courseNode;
courseNode.EnsureVisible();
}
/// <summary>
/// Create section nodes for the course
/// </summary>
/// <param name="courseNode"></param>
/// <param name="sections"></param>
private void AddSectionNodes(CourseNode courseNode, CourseSectionCollection sections)
{
courseNode.Nodes.Clear();
foreach (CourseSection section in sections)
{
SectionNode sectionNode = new SectionNode(section);
this.AddRoutineNode(sectionNode, section.SectionRoutineCollection);
courseNode.Nodes.Add(sectionNode);
}
}
/// <summary>
/// Create routine nodes for the section
/// </summary>
/// <param name="sectionNode"></param>
/// <param name="routines"></param>
private void AddRoutineNode( SectionNode sectionNode, SectionRoutineCollection routines )
{
sectionNode.Nodes.Clear();
foreach (SectionRoutine routine in routines)
{
RoutineNode routineNode = new RoutineNode(routine);
sectionNode.Nodes.Add(routineNode);
}
}
#endregion
#region protected override void OnDrawNode(DrawTreeNodeEventArgs e)
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
// colors that depend if the row is currently selected or not,
// assign to a system brush so should not be disposed
// not selected colors
Brush brushBack = SystemBrushes.Window;
Brush brushText = SystemBrushes.WindowText;
Brush brushDim = Brushes.Gray;
if ((e.State & TreeNodeStates.Selected) != 0)
{
if ((e.State & TreeNodeStates.Focused) != 0)
{
// selected and has focus
brushBack = SystemBrushes.Highlight;
brushText = SystemBrushes.HighlightText;
brushDim = SystemBrushes.HighlightText;
}
else
{
// selected and does not have focus
brushBack = SystemBrushes.Control;
brushDim = SystemBrushes.WindowText;
}
}
RectangleF rc = new RectangleF(e.Bounds.X, e.Bounds.Y, this.ClientRectangle.Width, e.Bounds.Height);
// background
e.Graphics.FillRectangle(brushBack, rc);
//base.OnDrawNode(e);
if (rc.Width > 0 && rc.Height > 0)
{
if (e.Node is CourseNode)
this.DrawCourseNode(e, rc, brushBack, brushText, brushDim);
else if (e.Node is SectionNode)
this.DrawSectionNode(e, rc, brushBack, brushText, brushDim);
else if (e.Node is RoutineNode)
this.DrawRoutineNode(e, rc, brushBack, brushText, brushDim);
}
}
#endregion
#region protected override void OnResize( EventArgs e )
protected override void OnResize( EventArgs e )
{
base.ItemHeight = (int)(base.CreateGraphics().MeasureString("Wy", base.Font).Height * 2) + 10;
}
#endregion
#region Course Node
public class CourseNode : TreeNode
{
private Course _Course;
public Course Course
{
get { return _Course; }
set { _Course = value; }
}
public CourseNode( Course course ) : base( course.Title )
{
this._Course = course;
}
public override string ToString()
{
return this._Course.Title;
}
public int Capacity
{
get
{
int capacity = 0;
foreach (SectionNode node in this.Nodes)
{
capacity += node.Section.Capacity;
}
return capacity;
}
}
public bool IsAllowed
{
get
{
bool allowed = true;
foreach (SectionNode section in this.Nodes)
{
allowed &= section.IsAllowed;
}
return allowed;
}
}
}
#endregion
#region Section node
public class SectionNode : TreeNode
{
private CourseSection _Section;
public CourseSection Section
{
get { return _Section; }
set { _Section = value; }
}
public bool IsAllowed
{
get
{
return this._Section.Capacity > 1; // this._Section.StudentCount;
}
}
public SectionNode(CourseSection section)
: base(section.Description)
{
this._Section = section;
}
public override string ToString()
{
return this._Section.Description;
}
}
#endregion
#region Routine Node
public class RoutineNode : TreeNode
{
private SectionRoutine _Routine;
private string _RoutineText;
private string _RoomTitle;
public SectionRoutine Routine
{
get { return _Routine;}
set { _Routine = value;}
}
public RoutineNode( SectionRoutine routine )
{
this._Routine = routine;
this._RoutineText = this._Routine.ToString() + " " +
this._Routine.Type.ToString();
}
public override string ToString()
{
return this._RoutineText;
}
}
#endregion
#region private void DrawSectionNode(DrawTreeNodeEventArgs e, RectangleF rc, Brush brushBack, Brush brushText, Brush brushDim )
private void DrawSectionNode(DrawTreeNodeEventArgs e, RectangleF rc, Brush brushBack, Brush brushText, Brush brushDim )
{
SectionNode node = e.Node as SectionNode;
if ((e.State & TreeNodeStates.Selected) == 0)
{
this.GradientFill(e.Graphics, rc, Color.Gainsboro, Color.White, 0);
}
SizeF lineSize = e.Graphics.MeasureString( node.Section.Description, base.Font );
// Draw the title of the section
float textX = rc.Left + 4;
Brush textBrush = brushText;
// if the current section is not allowed to regiter, dim it
if (!node.IsAllowed)
textBrush = brushDim;
// draw the section title
e.Graphics.DrawString(node.Section.Description, base.Font, textBrush, textX, rc.Top);
// Draw section status
float secondLineY = rc.Top + lineSize.Height;
// Class ID
textX += this.DrawString(e.Graphics, node.Section.ClassID, base.Font, textBrush, textX, secondLineY).Width;
// Decide the status text color according to the status of the section
this.DrawSectionStatus(e, node, ref textX, secondLineY);
this.DrawSectionCapacity(e, node, ref textX, secondLineY);
// Show student count and capacity
this.DrawSectionCount(e, node, textX, textBrush, secondLineY);
// horizontal separator
//e.Graphics.DrawLine(Pens.LightGray, rc.Left, rc.Bottom - 1, rc.Right, rc.Bottom - 1);
}
private void DrawSectionCount( DrawTreeNodeEventArgs e, SectionNode node, float textX, Brush textBrush, float secondLineY )
{
int capacity = node.Section.Capacity;
string count = " (" + capacity + ")";
e.Graphics.DrawString(count, base.Font, textBrush, textX, secondLineY);
}
private void DrawSectionCapacity( DrawTreeNodeEventArgs e, SectionNode node, ref float textX, float secondLineY )
{
// if (node.Section.Capacity <= node.Section.StudentCount)
// {
// textX += this.DrawString(e.Graphics, " Overflow ", base.Font, Brushes.DarkRed, textX, secondLineY).Width;
// }
}
private void DrawSectionStatus( DrawTreeNodeEventArgs e, SectionNode node, ref float textX, float secondLineY )
{
Brush statusBrush = Brushes.Green;
if (node.Section.Status == (int)SectionStatusEnum.Open || node.Section.Status == (int)SectionStatusEnum.Freshmen)
{
// green
}
else
{
statusBrush = Brushes.DarkRed;
}
// Show the status using status color
textX += this.DrawString(e.Graphics, " " + node.Section.Status.ToString() + " ", base.Font, statusBrush, textX, secondLineY).Width;
}
#endregion
#region private SizeF DrawString( Graphics g, string text, Font font, Brush brush, float x, float y )
private SizeF DrawString( Graphics g, string text, Font font, Brush brush, float x, float y )
{
SizeF statusSize = g.MeasureString(text, font);
g.DrawString(text, font, brush, x, y);
return statusSize;
}
#endregion
#region private void DrawCourseNode(DrawTreeNodeEventArgs e, RectangleF rc, Brush brushBack, Brush brushText, Brush brushDim)
private void DrawCourseNode(DrawTreeNodeEventArgs e, RectangleF rc, Brush brushBack, Brush brushText, Brush brushDim)
{
CourseNode node = e.Node as CourseNode;
// Draw the course icon
//Image image = imglistIcons.Images[0];
//e.Graphics.DrawImage(image, rc.Left, rc.Top);
if( (e.State & TreeNodeStates.Selected) == 0 )
{
this.GradientFill(e.Graphics, rc, Color.White, Color.Gainsboro, rc.Height/2);
}
float textX = rc.Left; // +image.Width;
SizeF titleSize = this.DrawString(e.Graphics, node.Course.Title, base.Font, brushText, textX, rc.Top);
float secondLineY = rc.Top + titleSize.Height;
if (!node.IsAllowed)
textX += this.DrawString(e.Graphics, "All Closed ", base.Font, Brushes.DarkRed, textX, secondLineY ).Width;
// Show student cound and capacity
int capacity = node.Capacity;
//int studentCount = node.StudentCount;
//string count = studentCount + "/" + capacity + " ";
string count = "(" + capacity + ") ";
textX += this.DrawString( e.Graphics, count, base.Font, brushText, textX, secondLineY ).Width;
// show number of sections
int sectioncount = node.Course.CourseSectionCollection.Count;
string sections = sectioncount.ToString() + " section" + (sectioncount>1?"s":"");
textX += this.DrawString(e.Graphics, sections, base.Font, brushText, textX, secondLineY).Width;
// draw a separator
//e.Graphics.DrawLine(Pens.LightGray, rc.Left, rc.Bottom-1, rc.Right, rc.Bottom-1);
}
#endregion
#region private void DrawRoutineNode( DrawTreeNodeEventArgs e, RectangleF rc, Brush brushBack, Brush brushText, Brush brushDim )
private void DrawRoutineNode( DrawTreeNodeEventArgs e, RectangleF rc, Brush brushBack, Brush brushText, Brush brushDim )
{
RoutineNode node = e.Node as RoutineNode;
this.GradientFill(e.Graphics, rc, Color.WhiteSmoke, Color.GhostWhite, 0);
string routineText = node.ToString();
SizeF size = this.DrawString(e.Graphics, routineText, base.Font, Brushes.Black, rc.Left, rc.Top);
//this.DrawString(e.Graphics, node.Routine.Teachers, base.Font, Brushes.Gray, rc.Left, rc.Top + size.Height);
}
#endregion
private delegate void RefreshCourseDelegate(CourseModel model);
void Courses_OnItemChange( ItemBase item, string changeType )
{
if (item is CourseModel)
{
this.BeginInvoke(new RefreshCourseDelegate(this.RefreshCourse), item as CourseModel);
}
}
private void OfferedCourseTree_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.Handled = true;
base.SelectedNode.Toggle();
}
}
private void UpdateCourseNode(CourseModel model, CourseNode node)
{
// 1. Clear child of the node
node.Nodes.Clear();
// 2. Reload the section nodes
this.AddSectionNodes(node, model.Course.CourseSectionCollection);
// 3. Make it current
base.SelectedNode = node;
node.EnsureVisible();
}
private void GradientFill(Graphics g, RectangleF rc, Color from, Color to, float offset)
{
RectangleF fillRect = rc;
fillRect.Size = new SizeF(rc.Width, rc.Height-offset);
fillRect.Offset(0, offset);
using (LinearGradientBrush brush = new LinearGradientBrush(fillRect, from, to, 90f))
{
g.FillRectangle(brush, fillRect);
}
}
}
}