386 lines
11 KiB
C#
386 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.ComponentModel;
|
|
using System.Windows.Forms;
|
|
using System.Drawing;
|
|
using System.Windows.Forms.VisualStyles;
|
|
using System.Drawing.Imaging;
|
|
|
|
namespace Aga.Controls.Tree
|
|
{
|
|
[TypeConverter(typeof(TreeColumn.TreeColumnConverter)), DesignTimeVisible(false), ToolboxItem(false)]
|
|
public class TreeColumn : Component
|
|
{
|
|
private class TreeColumnConverter : ComponentConverter
|
|
{
|
|
public TreeColumnConverter()
|
|
: base(typeof(TreeColumn))
|
|
{
|
|
}
|
|
|
|
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private const int HeaderLeftMargin = 5;
|
|
private const int HeaderRightMargin = 5;
|
|
private const int SortOrderMarkMargin = 8;
|
|
|
|
private TextFormatFlags _headerFlags;
|
|
private TextFormatFlags _baseHeaderFlags = TextFormatFlags.NoPadding |
|
|
TextFormatFlags.EndEllipsis |
|
|
TextFormatFlags.VerticalCenter |
|
|
TextFormatFlags.PreserveGraphicsTranslateTransform;
|
|
|
|
#region Properties
|
|
|
|
private TreeColumnCollection _owner;
|
|
internal TreeColumnCollection Owner
|
|
{
|
|
get { return _owner; }
|
|
set { _owner = value; }
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public int Index
|
|
{
|
|
get
|
|
{
|
|
if (Owner != null)
|
|
return Owner.IndexOf(this);
|
|
else
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
private string _header;
|
|
[Localizable(true)]
|
|
public string Header
|
|
{
|
|
get { return _header; }
|
|
set
|
|
{
|
|
_header = value;
|
|
OnHeaderChanged();
|
|
}
|
|
}
|
|
|
|
private string _tooltipText;
|
|
[Localizable(true)]
|
|
public string TooltipText
|
|
{
|
|
get { return _tooltipText; }
|
|
set { _tooltipText = value; }
|
|
}
|
|
|
|
private int _width;
|
|
[DefaultValue(50), Localizable(true)]
|
|
public int Width
|
|
{
|
|
get
|
|
{
|
|
return _width;
|
|
}
|
|
set
|
|
{
|
|
if (_width != value)
|
|
{
|
|
_width = Math.Max(MinColumnWidth, value);
|
|
if (_maxColumnWidth > 0)
|
|
{
|
|
_width = Math.Min(_width, MaxColumnWidth);
|
|
}
|
|
OnWidthChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private int _minColumnWidth;
|
|
[DefaultValue(0)]
|
|
public int MinColumnWidth
|
|
{
|
|
get { return _minColumnWidth; }
|
|
set
|
|
{
|
|
if (value < 0)
|
|
throw new ArgumentOutOfRangeException("value");
|
|
|
|
_minColumnWidth = value;
|
|
Width = Math.Max(value, Width);
|
|
}
|
|
}
|
|
|
|
private int _maxColumnWidth;
|
|
[DefaultValue(0)]
|
|
public int MaxColumnWidth
|
|
{
|
|
get { return _maxColumnWidth; }
|
|
set
|
|
{
|
|
if (value < 0)
|
|
throw new ArgumentOutOfRangeException("value");
|
|
|
|
_maxColumnWidth = value;
|
|
if (value > 0)
|
|
Width = Math.Min(value, _width);
|
|
}
|
|
}
|
|
|
|
private bool _visible = true;
|
|
[DefaultValue(true)]
|
|
public bool IsVisible
|
|
{
|
|
get { return _visible; }
|
|
set
|
|
{
|
|
_visible = value;
|
|
OnIsVisibleChanged();
|
|
}
|
|
}
|
|
|
|
private HorizontalAlignment _textAlign = HorizontalAlignment.Left;
|
|
[DefaultValue(HorizontalAlignment.Left)]
|
|
public HorizontalAlignment TextAlign
|
|
{
|
|
get { return _textAlign; }
|
|
set
|
|
{
|
|
if (value != _textAlign)
|
|
{
|
|
_textAlign = value;
|
|
_headerFlags = _baseHeaderFlags | TextHelper.TranslateAligmentToFlag(value);
|
|
OnHeaderChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool _sortable = false;
|
|
[DefaultValue(false)]
|
|
public bool Sortable
|
|
{
|
|
get { return _sortable; }
|
|
set { _sortable = value; }
|
|
}
|
|
|
|
private SortOrder _sort_order = SortOrder.None;
|
|
public SortOrder SortOrder
|
|
{
|
|
get { return _sort_order; }
|
|
set
|
|
{
|
|
if (value == _sort_order)
|
|
return;
|
|
_sort_order = value;
|
|
OnSortOrderChanged();
|
|
}
|
|
}
|
|
|
|
public Size SortMarkSize
|
|
{
|
|
get
|
|
{
|
|
if (Application.RenderWithVisualStyles)
|
|
return new Size(9, 5);
|
|
else
|
|
return new Size(7, 4);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public TreeColumn():
|
|
this(string.Empty, 50)
|
|
{
|
|
}
|
|
|
|
public TreeColumn(string header, int width)
|
|
{
|
|
_header = header;
|
|
_width = width;
|
|
_headerFlags = _baseHeaderFlags | TextFormatFlags.Left;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
if (string.IsNullOrEmpty(Header))
|
|
return GetType().Name;
|
|
else
|
|
return Header;
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
#region Draw
|
|
|
|
private static VisualStyleRenderer _normalRenderer;
|
|
private static VisualStyleRenderer _pressedRenderer;
|
|
private static VisualStyleRenderer _hotRenderer;
|
|
|
|
private static void CreateRenderers()
|
|
{
|
|
if (Application.RenderWithVisualStyles && _normalRenderer == null)
|
|
{
|
|
_normalRenderer = new VisualStyleRenderer(VisualStyleElement.Header.Item.Normal);
|
|
_pressedRenderer = new VisualStyleRenderer(VisualStyleElement.Header.Item.Pressed);
|
|
_hotRenderer = new VisualStyleRenderer(VisualStyleElement.Header.Item.Hot);
|
|
}
|
|
}
|
|
|
|
internal Bitmap CreateGhostImage(Rectangle bounds, Font font)
|
|
{
|
|
Bitmap b = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
|
|
Graphics gr = Graphics.FromImage(b);
|
|
gr.FillRectangle(SystemBrushes.ControlDark, bounds);
|
|
DrawContent(gr, bounds, font);
|
|
BitmapHelper.SetAlphaChanelValue(b, 150);
|
|
return b;
|
|
}
|
|
|
|
internal void Draw(Graphics gr, Rectangle bounds, Font font, bool pressed, bool hot)
|
|
{
|
|
DrawBackground(gr, bounds, pressed, hot);
|
|
DrawContent(gr, bounds, font);
|
|
}
|
|
|
|
private void DrawContent(Graphics gr, Rectangle bounds, Font font)
|
|
{
|
|
if (TreeViewAdv.CustomColumnTextRenderFunc != null)
|
|
{
|
|
TreeViewAdv.CustomColumnTextRenderFunc(gr, bounds, font, Header);
|
|
}
|
|
else
|
|
{
|
|
Rectangle innerBounds = new Rectangle(bounds.X + HeaderLeftMargin, bounds.Y,
|
|
bounds.Width - HeaderLeftMargin - HeaderRightMargin,
|
|
bounds.Height);
|
|
|
|
if (SortOrder != SortOrder.None)
|
|
innerBounds.Width -= (SortMarkSize.Width + SortOrderMarkMargin);
|
|
|
|
Size maxTextSize = TextRenderer.MeasureText(gr, Header, font, innerBounds.Size, TextFormatFlags.NoPadding);
|
|
Size textSize = TextRenderer.MeasureText(gr, Header, font, innerBounds.Size, _baseHeaderFlags);
|
|
|
|
if (SortOrder != SortOrder.None)
|
|
{
|
|
int tw = Math.Min(textSize.Width, innerBounds.Size.Width);
|
|
|
|
int x = 0;
|
|
if (TextAlign == HorizontalAlignment.Left)
|
|
x = innerBounds.X + tw + SortOrderMarkMargin;
|
|
else if (TextAlign == HorizontalAlignment.Right)
|
|
x = innerBounds.Right + SortOrderMarkMargin;
|
|
else
|
|
x = innerBounds.X + tw + (innerBounds.Width - tw) / 2 + SortOrderMarkMargin;
|
|
DrawSortMark(gr, bounds, x);
|
|
}
|
|
|
|
if (textSize.Width < maxTextSize.Width)
|
|
TextRenderer.DrawText(gr, Header, font, innerBounds, SystemColors.ControlText, _baseHeaderFlags | TextFormatFlags.Left);
|
|
else
|
|
TextRenderer.DrawText(gr, Header, font, innerBounds, SystemColors.ControlText, _headerFlags);
|
|
}
|
|
}
|
|
|
|
private void DrawSortMark(Graphics gr, Rectangle bounds, int x)
|
|
{
|
|
int y = bounds.Y + bounds.Height / 2 - 2;
|
|
x = Math.Max(x, bounds.X + SortOrderMarkMargin);
|
|
|
|
int w2 = SortMarkSize.Width / 2;
|
|
if (SortOrder == SortOrder.Ascending)
|
|
{
|
|
Point[] points = new Point[] { new Point(x, y), new Point(x + SortMarkSize.Width, y), new Point(x + w2, y + SortMarkSize.Height) };
|
|
gr.FillPolygon(SystemBrushes.ControlDark, points);
|
|
}
|
|
else if (SortOrder == SortOrder.Descending)
|
|
{
|
|
Point[] points = new Point[] { new Point(x - 1, y + SortMarkSize.Height), new Point(x + SortMarkSize.Width, y + SortMarkSize.Height), new Point(x + w2, y - 1) };
|
|
gr.FillPolygon(SystemBrushes.ControlDark, points);
|
|
}
|
|
}
|
|
|
|
internal static void DrawDropMark(Graphics gr, Rectangle rect)
|
|
{
|
|
gr.FillRectangle(SystemBrushes.HotTrack, rect.X-1, rect.Y, 2, rect.Height);
|
|
}
|
|
|
|
internal static void DrawBackground(Graphics gr, Rectangle bounds, bool pressed, bool hot)
|
|
{
|
|
if (TreeViewAdv.CustomColumnBackgroundRenderFunc != null)
|
|
{
|
|
TreeViewAdv.CustomColumnBackgroundRenderFunc(gr, bounds, pressed, hot);
|
|
}
|
|
else
|
|
{
|
|
if (Application.RenderWithVisualStyles)
|
|
{
|
|
CreateRenderers();
|
|
if (pressed)
|
|
_pressedRenderer.DrawBackground(gr, bounds);
|
|
else if (hot)
|
|
_hotRenderer.DrawBackground(gr, bounds);
|
|
else
|
|
_normalRenderer.DrawBackground(gr, bounds);
|
|
}
|
|
else
|
|
{
|
|
gr.FillRectangle(SystemBrushes.Control, bounds);
|
|
Pen p1 = SystemPens.ControlLightLight;
|
|
Pen p2 = SystemPens.ControlDark;
|
|
Pen p3 = SystemPens.ControlDarkDark;
|
|
if (pressed)
|
|
gr.DrawRectangle(p2, bounds.X, bounds.Y, bounds.Width, bounds.Height);
|
|
else
|
|
{
|
|
gr.DrawLine(p1, bounds.X, bounds.Y, bounds.Right, bounds.Y);
|
|
gr.DrawLine(p3, bounds.X, bounds.Bottom, bounds.Right, bounds.Bottom);
|
|
gr.DrawLine(p3, bounds.Right - 1, bounds.Y, bounds.Right - 1, bounds.Bottom - 1);
|
|
gr.DrawLine(p1, bounds.Left, bounds.Y + 1, bounds.Left, bounds.Bottom - 2);
|
|
gr.DrawLine(p2, bounds.Right - 2, bounds.Y + 1, bounds.Right - 2, bounds.Bottom - 2);
|
|
gr.DrawLine(p2, bounds.X, bounds.Bottom - 1, bounds.Right - 2, bounds.Bottom - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
public event EventHandler HeaderChanged;
|
|
private void OnHeaderChanged()
|
|
{
|
|
if (HeaderChanged != null)
|
|
HeaderChanged(this, EventArgs.Empty);
|
|
}
|
|
|
|
public event EventHandler SortOrderChanged;
|
|
private void OnSortOrderChanged()
|
|
{
|
|
if (SortOrderChanged != null)
|
|
SortOrderChanged(this, EventArgs.Empty);
|
|
}
|
|
|
|
public event EventHandler IsVisibleChanged;
|
|
private void OnIsVisibleChanged()
|
|
{
|
|
if (IsVisibleChanged != null)
|
|
IsVisibleChanged(this, EventArgs.Empty);
|
|
}
|
|
|
|
public event EventHandler WidthChanged;
|
|
private void OnWidthChanged()
|
|
{
|
|
if (WidthChanged != null)
|
|
WidthChanged(this, EventArgs.Empty);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|