using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Runtime;
using System.Windows.Forms;
namespace Dongke.WinForm.Controls
{
internal partial class trvTreeView : TreeView, IDKControl, IAsyncControl
{
#region 构造函数
public trvTreeView()
{
InitializeComponent();
}
public trvTreeView(IContainer container)
{
container.Add(this);
InitializeComponent();
}
#endregion
#region 成员变量
private int indexUnchecked;
private int indexChecked;
private int indexIndeterminate;
private bool useCustomImages;
private bool useShowImage;
#endregion
#region 属性
[Category("CheckState")]
[DefaultValue(true)]
public bool UseShowImage
{
get { return this.useShowImage; }
set {this.useShowImage = value; }
}
[Category("CheckState")]
[DefaultValue(false)]
public bool UseCustomImages
{
get { return this.useCustomImages; }
set { this.useCustomImages = value; }
}
[Category("CheckState")]
[TypeConverter(typeof(TreeViewImageIndexConverter))]
[Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[DefaultValue(0)]
public int CheckedImageIndex
{
get
{
if (base.ImageList == null)
return -1;
if (this.indexChecked >= this.ImageList.Images.Count)
return Math.Max(0, this.ImageList.Images.Count - 1);
return this.indexChecked;
}
set
{
if (value == -1)
value = 0;
if (value < 0)
throw new ArgumentException(string.Format("Index out of bounds! ({0}) index must be equal to or greater then {1}.", value.ToString(), "0"));
if (this.indexChecked != value)
{
this.indexChecked = value;
if (base.IsHandleCreated)
base.RecreateHandle();
}
}
}
[Category("CheckState")]
[TypeConverter(typeof(TreeViewImageIndexConverter))]
[Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[DefaultValue(0)]
public int UncheckedImageIndex
{
get
{
if (base.ImageList == null)
return -1;
if (this.indexUnchecked >= this.ImageList.Images.Count)
return Math.Max(0, this.ImageList.Images.Count - 1);
return this.indexUnchecked;
}
set
{
if (value == -1)
value = 0;
if (value < 0)
throw new ArgumentException(string.Format("Index out of bounds! ({0}) index must be equal to or greater then {1}.", value.ToString(), "0"));
if (this.indexUnchecked != value)
{
this.indexUnchecked = value;
if (base.IsHandleCreated)
base.RecreateHandle();
}
}
}
[Category("CheckState")]
[TypeConverter(typeof(TreeViewImageIndexConverter))]
[Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[DefaultValue(0)]
public int IndeterminateImageIndex
{
get
{
if (base.ImageList == null)
return -1;
if (this.indexIndeterminate >= this.ImageList.Images.Count)
return Math.Max(0, this.ImageList.Images.Count - 1);
return this.indexIndeterminate;
}
set
{
if (value == -1)
value = 0;
if (value < 0)
throw new ArgumentException(string.Format("Index out of bounds! ({0}) index must be equal to or greater then {1}.", value.ToString(), "0"));
if (this.indexIndeterminate != value)
{
this.indexIndeterminate = value;
if (base.IsHandleCreated)
base.RecreateHandle();
}
}
}
#endregion
#region 事件
protected override void OnAfterCheck(TreeViewEventArgs e)
{
base.OnAfterCheck(e);
TreeNode node = e.Node;
if (node != null)
{
TriStateTreeNode clickedNode = node as TriStateTreeNode;
if (clickedNode != null)
{
if (clickedNode.CheckboxVisible)
{
ToggleNodeState(clickedNode);
}
}
}
}
///
/// Paints the node specified.
///
///
///
private void PaintTreeNode(TriStateTreeNode node, Graphics gx)
{
if (this.CheckBoxes)
{
// calculate boundaries
Rectangle ncRect = new Rectangle(node.Bounds.X - 35, node.Bounds.Y, 15, 15);
// make sure the default checkboxes are gone
ClearCheckbox(ncRect, gx);
// draw lines, if we are supposed to
if (this.ShowLines)
{
DrawNodeLines(node, ncRect, gx);
}
if (node.CheckboxVisible)
{
// now draw the checkboxes
switch (node.CheckState)
{
case CheckState.Unchecked: // Normal
DrawCheckbox(ncRect, gx, ButtonState.Normal | ButtonState.Flat);
break;
case CheckState.Checked: // Checked
DrawCheckbox(ncRect, gx, ButtonState.Checked | ButtonState.Flat);
break;
case CheckState.Indeterminate: // Pushed
DrawCheckbox(ncRect, gx, ButtonState.Pushed | ButtonState.Flat);
break;
}
}
}
}
///
/// Deletes the default checkboxes which are drawn when the treeview.Checkboxes property
/// is set tot true.
///
///
///
private void ClearCheckbox(Rectangle bounds, Graphics gx)
{
// make sure the default checkboxes are gone.
using (Brush brush = new SolidBrush(this.BackColor))
{
gx.FillRectangle(brush, bounds);
}
}
///
/// Draws thee node lines before the checkboxes are drawn
///
/// Graphics context
private void DrawNodeLines(TriStateTreeNode node, Rectangle bounds, Graphics gx)
{
// determine type of line to draw
NodeLineType lineType = node.NodeLineType;
if (lineType == NodeLineType.None) { return; }
using (Pen pen = new Pen(SystemColors.ControlDark, 1))
{
pen.DashStyle = DashStyle.Dot;
gx.DrawLine(pen, new Point(bounds.X, bounds.Y + 8), new Point(bounds.X + 15, bounds.Y + 8));
if (lineType == NodeLineType.WithChildren && node.IsExpanded)
{
gx.DrawLine(pen, new Point(bounds.X + 8, bounds.Y + 8), new Point(bounds.X + 8, bounds.Y + 16));
}
}
}
///
/// Draws a checkbox in the desired state and style
///
/// boundaries of the checkbox
/// graphics context object
/// state to draw the checkbox in
private void DrawCheckbox(Rectangle bounds, Graphics gx, ButtonState buttonState)
{
// get the right image index
//System.Windows.Forms.VisualStyles.CheckBoxState imageIndexss = System.Windows.Forms.VisualStyles.CheckBoxState.MixedPressed;
//if ((buttonState & ButtonState.Normal) == ButtonState.Normal)
// imageIndex = this.indexUnchecked;
//if ((buttonState & ButtonState.Checked) == ButtonState.Checked)
// imageIndex = this.indexChecked;
//if ((buttonState & ButtonState.Pushed) == ButtonState.Pushed)
// imageIndex = this.indexIndeterminate;
// if we don't have custom images, or no imagelist, draw default images
if (!this.useCustomImages || (this.useCustomImages && null == this.ImageList))
{
//ControlPaint.DrawMixedCheckBox(gx, bounds, buttonState);
CheckBoxRenderer.DrawCheckBox(gx, bounds.Location, System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
return;
}
// get the right image index
int imageIndex = -1;
if ((buttonState & ButtonState.Normal) == ButtonState.Normal)
imageIndex = this.indexUnchecked;
if ((buttonState & ButtonState.Checked) == ButtonState.Checked)
imageIndex = this.indexChecked;
if ((buttonState & ButtonState.Pushed) == ButtonState.Pushed)
imageIndex = this.indexIndeterminate;
if (imageIndex > -1 && imageIndex < this.ImageList.Images.Count)
{
// index is valid so draw the image
this.ImageList.Draw(gx, bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1, imageIndex);
}
else
{
// index is not valid so draw default image
ControlPaint.DrawMixedCheckBox(gx, bounds, buttonState);
}
}
///
/// Ovveride the WindowProcedure in order to intercept the itemdraw event
///
///
protected override void WndProc(ref Message m)
{
const int WM_NOTIFY = 0x4E;
int iResult = 0;
bool bHandled = false;
if (m.Msg == (0x2000 | WM_NOTIFY))
{
if (m.WParam.Equals(this.Handle))
{
iResult = HandleNotify(m);
m.Result = new IntPtr(iResult);
bHandled = (iResult != 0);
}
}
if (!bHandled)
base.WndProc(ref m);
}
#endregion
#region 私有方法
///
/// Toggles node state between checked & unchecked
///
///
private void ToggleNodeState(TriStateTreeNode node)
{
// no need to toggle state for non-existing node ( or non-tristatetreenode! )
if (null == node) return;
// toggle state
CheckState nextState;
switch (node.CheckState)
{
case CheckState.Unchecked:
nextState = CheckState.Checked;
break;
default:
nextState = CheckState.Unchecked;
break;
}
// notify the treeview that an update is about to take place
BeginUpdate();
// update the node state, and dependend nodes
node.SetCheckedState(nextState);
// force a redraw
EndUpdate();
}
private int HandleNotify(Message msg)
{
const int NM_FIRST = 0;
const int NM_CUSTOMDRAW = NM_FIRST - 12;
// Drawstage
const int CDDS_PREPAINT = 0x1;
const int CDDS_POSTPAINT = 0x2;
const int CDDS_ITEM = 0x10000;
const int CDDS_ITEMPREPAINT = (CDDS_ITEM | CDDS_PREPAINT);
const int CDDS_ITEMPOSTPAINT = (CDDS_ITEM | CDDS_POSTPAINT);
// Custom draw return flags
const int CDRF_DODEFAULT = 0x0;
const int CDRF_NOTIFYPOSTPAINT = 0x10;
const int CDRF_NOTIFYITEMDRAW = 0x20;
NMHDR tNMHDR;
NMTVCUSTOMDRAW tNMTVCUSTOMDRAW;
int iResult = 0;
object obj;
TreeNode node;
TriStateTreeNode tsNode;
try
{
if (!msg.LParam.Equals(IntPtr.Zero))
{
obj = msg.GetLParam(typeof(NMHDR));
if (obj is NMHDR)
{
tNMHDR = (NMHDR)obj;
if (tNMHDR.code == NM_CUSTOMDRAW)
{
obj = msg.GetLParam(typeof(NMTVCUSTOMDRAW));
if (obj is NMTVCUSTOMDRAW)
{
tNMTVCUSTOMDRAW = (NMTVCUSTOMDRAW)obj;
switch (tNMTVCUSTOMDRAW.nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
iResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
iResult = CDRF_NOTIFYPOSTPAINT;
break;
case CDDS_ITEMPOSTPAINT:
node = TreeNode.FromHandle(this, tNMTVCUSTOMDRAW.nmcd.dwItemSpec);
tsNode = node as TriStateTreeNode;
if (tsNode != null)
{
Graphics graph = Graphics.FromHdc(tNMTVCUSTOMDRAW.nmcd.hdc);
PaintTreeNode(tsNode, graph);
graph.Dispose();
}
iResult = CDRF_DODEFAULT;
break;
}
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return iResult;
}
#endregion
#region 结构
private struct RECT
{
internal int left;
internal int top;
internal int right;
internal int bottom;
}
private struct NMHDR
{
internal IntPtr hwndFrom;
internal IntPtr idFrom;
internal int code;
}
private struct NMCUSTOMDRAW
{
internal NMHDR hdr;
internal int dwDrawStage;
internal IntPtr hdc;
internal RECT rc;
internal IntPtr dwItemSpec;
internal int uItemState;
internal IntPtr lItemlParam;
}
private struct NMTVCUSTOMDRAW
{
internal NMCUSTOMDRAW nmcd;
internal int clrText;
internal int clrTextBk;
internal int iLevel;
}
#endregion
#region IAsyncControl 成员
#region 成员变量
///
/// 异步处理开始时,控件状态
///
private bool _asyncBeginStatus = false;
private bool _asyncBeginFocused = false;
#endregion
#region 公有方法
///
/// 开始异步处理
///
/// 是否处理焦点
public virtual void BeginAsync(ref bool doFocus)
{
if (doFocus && this.Focused)
{
this._asyncBeginFocused = true;
doFocus = false;
}
this._asyncBeginStatus = this.Enabled;
this.Enabled = false;
}
///
/// 结束异步处理
///
public virtual void EndAsync()
{
this.Enabled = this._asyncBeginStatus;
if (this._asyncBeginFocused)
{
this.Focus();
}
}
#endregion
#endregion
}
}