private void OnTreeViewDrawNode(object sender, DrawTreeNodeEventArgs e)
{
// We cannot do anything without a valid node
if (e.Node == null)
return;
// Update our content object with values from the node
UpdateContentFromNode(e.Node);
// Do we need an image?
if (ImageList != null)
{
_layoutImageStack.Visible = true;
_layoutImage.SeparatorSize = ImageList.ImageSize;
}
else
_layoutImageStack.Visible = false;
// Work out if we need to draw a state image
Image drawStateImage = null;
if (StateImageList != null)
{
try
{
// If showing check boxes then used fixed entries from the state image list
if (CheckBoxes)
{
if (e.Node.Checked)
drawStateImage = StateImageList.Images[1];
else
drawStateImage = StateImageList.Images[0];
}
else
{
// Check node values before tree level values
if (!string.IsNullOrEmpty(e.Node.StateImageKey))
drawStateImage = StateImageList.Images[e.Node.StateImageKey];
else if ((e.Node.StateImageIndex >= 0) && (e.Node.StateImageIndex < StateImageList.Images.Count))
drawStateImage = StateImageList.Images[e.Node.StateImageIndex];
}
}
catch
{
}
}
_layoutImageCenterState.Visible = (drawStateImage != null);
// Do we need the check box?
_layoutCheckBox.Visible = (StateImageList == null) && CheckBoxes;
if (_layoutCheckBox.Visible)
_drawCheckBox.CheckState = e.Node.Checked ? CheckState.Checked : CheckState.Unchecked;
// By default the button is in the normal state
PaletteState buttonState = PaletteState.Normal;
// Is this item disabled
if ((e.State & TreeNodeStates.Grayed) == TreeNodeStates.Grayed)
buttonState = PaletteState.Disabled;
else
{
// If selected then show as a checked item
if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
{
_drawButton.Checked = true;
if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
buttonState = PaletteState.CheckedTracking;
else
buttonState = PaletteState.CheckedNormal;
}
else
{
_drawButton.Checked = false;
if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
buttonState = PaletteState.Tracking;
else
buttonState = PaletteState.Normal;
}
// Do we need to show item as having the focus
bool hasFocus = false;
if ((e.State & TreeNodeStates.Focused) == TreeNodeStates.Focused)
hasFocus = true;
_overrideNormal.Apply = hasFocus;
_overrideTracking.Apply = hasFocus;
_overridePressed.Apply = hasFocus;
_overrideCheckedTracking.Apply = hasFocus;
_overrideCheckedNormal.Apply = hasFocus;
_overrideCheckedPressed.Apply = hasFocus;
}
// Update the view with the calculated state
_drawButton.ElementState = buttonState;
// Grab the raw device context for the graphics instance
IntPtr hdc = e.Graphics.GetHdc();
try
{
Rectangle bounds = e.Bounds;
int indent = _treeView.Indent;
// Create indent rectangle and adjust bounds for remainder
int nodeIndent = NodeIndent(e.Node) + 2;
Rectangle indentBounds = new Rectangle(bounds.X + nodeIndent - indent, bounds.Y, indent, bounds.Height);
bounds.X += nodeIndent;
bounds.Width -= nodeIndent;
// Create bitmap that all drawing occurs onto, then we can blit it later to remove flicker
IntPtr hBitmap = PI.CreateCompatibleBitmap(hdc, bounds.Right, bounds.Bottom);
// If we managed to get a compatible bitmap
if (hBitmap != IntPtr.Zero)
{
try
{
// Must use the screen device context for the bitmap when drawing into the
// bitmap otherwise the Opacity and RightToLeftLayout will not work correctly.
PI.SelectObject(_screenDC, hBitmap);
// Easier to draw using a graphics instance than a DC!
using (Graphics g = Graphics.FromHdc(_screenDC))
{
Size prefSize = Size.Empty;
using (ViewLayoutContext context = new ViewLayoutContext(this, Renderer))
{
context.DisplayRectangle = e.Bounds;
_treeView.ViewDrawPanel.Layout(context);
context.DisplayRectangle = bounds;
// If no using full row selection, then layout using only required width
prefSize = _layoutDocker.GetPreferredSize(context);
if (!FullRowSelect)
{
if (prefSize.Width < bounds.Width)
bounds.Width = prefSize.Width;
}
// Always ensure we have enough space for drawing all the elements
if (bounds.Width < prefSize.Width)
bounds.Width = prefSize.Width;
context.DisplayRectangle = bounds;
_layoutDocker.Layout(context);
}
using (RenderContext context = new RenderContext(this, g, e.Bounds, Renderer))
_treeView.ViewDrawPanel.Render(context);
// Do we have a indent area for drawing plus/minus/lines?
if (indentBounds.X >= 0)
{
// Do we draw lines between nodes?
if (ShowLines && (Redirector.GetMetricBool(PaletteState.Normal, PaletteMetricBool.TreeViewLines) != InheritBool.False))
{
// Find center points
int hCenter = indentBounds.X + (indentBounds.Width / 2) - 1;
int vCenter = indentBounds.Y + (indentBounds.Height / 2);
vCenter -= (vCenter + 1) % 2;
// Default to showing full line height
int top = indentBounds.Y;
top -= (top + 1) % 2;
int bottom = indentBounds.Bottom;
// If the first root node then do not show top half of line
if ((e.Node.Parent == null) && (e.Node.PrevNode == null))
{
top = vCenter;
}
// If the last node in collection then do not show bottom half of line
if (e.Node.NextNode == null)
bottom = vCenter;
// Draw the horizontal and vertical lines
Color lineColor = Redirector.GetContentShortTextColor1(PaletteContentStyle.InputControlStandalone, PaletteState.Normal);
using (Pen linePen = new Pen(lineColor))
{
linePen.DashStyle = DashStyle.Dot;
linePen.DashOffset = indent % 2;
g.DrawLine(linePen, hCenter, top, hCenter, bottom);
g.DrawLine(linePen, hCenter - 1, vCenter - 1, indentBounds.Right, vCenter - 1);
hCenter -= indent;
// Draw the vertical lines for previous node levels
while (hCenter >= 0)
{
int begin = indentBounds.Y;
begin -= (begin + 1) % 2;
g.DrawLine(linePen, hCenter, begin, hCenter, indentBounds.Bottom);
hCenter -= indent;
}
}
}
// Do we draw any plus/minus images in indent bounds?
if (ShowPlusMinus && (e.Node.Nodes.Count > 0))
{
Image drawImage = _redirectImages.GetTreeViewImage(e.Node.IsExpanded);
if (drawImage != null)
g.DrawImage(drawImage, new Rectangle(indentBounds.X + (indentBounds.Width - drawImage.Width) / 2 - 1,
indentBounds.Y + (indentBounds.Height - drawImage.Height) / 2,
drawImage.Width, drawImage.Height));
}
}
using (RenderContext context = new RenderContext(this, g, bounds, Renderer))
_layoutDocker.Render(context);
// Do we draw an image for the node?
if (ImageList != null)
{
Image drawImage = null;
int imageCount = ImageList.Images.Count;
try
{
if (e.Node.IsSelected)
{
// Check node values before tree level values
if (!string.IsNullOrEmpty(e.Node.SelectedImageKey))
drawImage = ImageList.Images[e.Node.SelectedImageKey];
else if ((e.Node.SelectedImageIndex >= 0) && (e.Node.SelectedImageIndex < imageCount))
drawImage = ImageList.Images[e.Node.SelectedImageIndex];
else if (!string.IsNullOrEmpty(SelectedImageKey))
drawImage = ImageList.Images[SelectedImageKey];
else if ((SelectedImageIndex >= 0) && (SelectedImageIndex < imageCount))
drawImage = ImageList.Images[SelectedImageIndex];
}
else
{
// Check node values before tree level values
if (!string.IsNullOrEmpty(e.Node.ImageKey))
drawImage = ImageList.Images[e.Node.ImageKey];
else if ((e.Node.ImageIndex >= 0) && (e.Node.ImageIndex < imageCount))
drawImage = ImageList.Images[e.Node.ImageIndex];
else if (!string.IsNullOrEmpty(ImageKey))
drawImage = ImageList.Images[ImageKey];
else if ((ImageIndex >= 0) && (ImageIndex < imageCount))
drawImage = ImageList.Images[ImageIndex];
}
if (drawImage != null)
g.DrawImage(drawImage, _layoutImage.ClientRectangle);
}
catch
{
}
}
// Draw a node state image?
if (_layoutImageCenterState.Visible)
{
if (drawStateImage != null)
g.DrawImage(drawStateImage, _layoutImageState.ClientRectangle);
}
// Now blit from the bitmap from the screen to the real dc
PI.BitBlt(hdc, e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height, _screenDC, e.Bounds.X, e.Bounds.Y, PI.SRCCOPY);
}
}
finally
{
// Delete the temporary bitmap
PI.DeleteObject(hBitmap);
}
}
}
finally
{
// Must reserve the GetHdc() call before
e.Graphics.ReleaseHdc();
}
}