|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
ContentsIntroductionEveryone knows the navigation in Outlook with its neat bar, and there are many different implementations for it already existing. Some of them are very neat, but are rather inflexible on what kind of contents can be inserted into each group. What I needed was a possibility to show and hide several large grids quickly in all possible combinations. I could have used some sort of docking and/or MDI style to achieve this, but for several reasons, this was not good in my situation. Moreover, I think my solution is more convenient and fast than having to tackle with docking via drag and drop. This component has a look similar to that of a normal Outlook Bar (I admit I didn't stick to a certain style but invented my own based on the several implementations I have already seen). The big difference is that any control can be placed within each group, and that each group can not only be expanded or collapsed but also resized by the user. To make it look fancy, I also added animation effects for the expand/collapse process. Warning: Do not use this component if you just want to insert links/icons into the groups (like in Outlook). There are other components out there which are better in handling this. BackgroundTo use this component, you should just have a bit of experience working with Windows Forms. As there isn't much built-in designer support, it is necessary to build the contents of the bars at runtime. To add animations, I have used my own Using the codeAlthough there is no extensive designer support, the usage is straightforward. First, drag a All what is left to do is adding groups to the bar. This cannot be done with the designer. As a preparation, you could nevertheless already drag and adjust the controls you want to add onto your public MyForm() {
InitializeComponent();
_groupPaneBar.Add(new DataGrid(), "Bar 1",
null, false); //runtime created control
_groupPaneBar.Add(_panelWithLinks, "Bar 2",
null, true); //design time created control
}
Compile, start, and you can see two groups in the bar. SamplesAs I always do, I provided a sample application which should show most of the capabilities of this component. It's rather heavyweight, because I stuffed everything I could think of into it, and also used it for my final testing. Note that, because of the nesting of the bars and the huge content, several ArchitectureThe structure is rather simple. The whole component has only four classes of which only two are doing the relevant work. The other two are just specialized event classes. GroupPaneThis class represents a group within a bar. It handles the painting, event management, state holding, and animation. For those of you interested in programming self drawn controls, the private void DrawArrow(Graphics graphics, Rectangle rect,
Color color, bool isUp)
{
int arrowHeight = rect.Height - 8;
if (arrowHeight > 5)
arrowHeight = 5;
int halfLeftHeight = (rect.Height - arrowHeight) / 2;
int halfWidth = (rect.Width / 2) - 1;
using (SolidBrush brush = new SolidBrush(color))
{
int upwardsOffset = isUp ? 1 : -1;
int curLine = 0;
for (int i = (upwardsOffset < 0) ?
(arrowHeight - 1) : 0; (upwardsOffset < 0) ?
(i >= 0) : (i < arrowHeight); i += upwardsOffset)
{
graphics.FillRectangle(brush, (rect.X + halfWidth) - i,
(rect.Y + halfLeftHeight) +
curLine, (i * 2) + 1, 1);
curLine++;
}
}
}
private GraphicsPath CreateRoundedRectPath(Rectangle r, int radius)
{
GraphicsPath path = new GraphicsPath();
path.AddLine(r.Left + radius, r.Top,
(r.Left + r.Width) - (radius * 2), r.Top);
path.AddArc((r.Left + r.Width) - (radius * 2), r.Top,
radius * 2, radius * 2, 270f, 90f);
path.AddLine((int) (r.Left + r.Width),
(int) (r.Top + radius), (int) (r.Left + r.Width),
(int) ((r.Top + r.Height) - (radius * 2)));
path.AddArc((int) ((r.Left + r.Width) - (radius * 2)),
(int) ((r.Top + r.Height) - (radius * 2)),
(int) (radius * 2), (int) (radius * 2),
(float) 0f, (float) 90f);
path.AddLine((int) ((r.Left + r.Width) - (radius * 2)),
(int) (r.Top + r.Height),
(int) (r.Left + radius), (int) (r.Top + r.Height));
path.AddArc(r.Left, (r.Top + r.Height) - (radius * 2),
radius * 2, radius * 2, 90f, 90f);
path.AddLine(r.Left, (r.Top + r.Height) -
(radius * 2), r.Left, r.Top + radius);
path.AddArc(r.Left, r.Top, radius * 2, radius * 2, 180f, 90f);
path.CloseFigure();
return path;
}
private Color GetColor(Color color)
{
return GetColor(color, base.Enabled);
}
private Color GetColor(Color color, bool enabled)
{
if (enabled)
return color;
return ControlPaint.LightLight(color);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
e.Graphics.Clear(this.BackColor);
Rectangle headerRectangle = GetHeaderRectangle();
if (headerRectangle.Width == 0 || headerRectangle.Height == 0)
return;
//fill gradient backcolor of header
using (LinearGradientBrush brush =
new LinearGradientBrush(headerRectangle,
GetColor(_parent.HeaderColor1),
GetColor(_parent.HeaderColor2),
_parent.HeaderGradientMode))
{
e.Graphics.FillRectangle(brush, headerRectangle);
}
//draw surrounding borders
using (Pen pen = new Pen(GetColor(_parent.BorderColor),
_parent.BorderWidth))
{
//top
e.Graphics.DrawLine(pen, _parent.BorderWidth * 2,
(_parent.BorderWidth) / 2,
Width - _parent.BorderWidth * 2.5f,
(_parent.BorderWidth) / 2);
//topleft
e.Graphics.DrawArc(pen, _parent.BorderWidth / 2f,
_parent.BorderWidth / 2f,
_parent.BorderWidth * 4,
_parent.BorderWidth * 4, 180, 90);
//topright
e.Graphics.DrawArc(pen, Width - 1 - _parent.BorderWidth * 4.5f,
_parent.BorderWidth / 2f, _parent.BorderWidth * 4,
_parent.BorderWidth * 4, 270, 90);
//bottom
e.Graphics.DrawLine(pen, 1, Height - 2 *
_parent.BorderWidth, Width - 2,
Height - 2 * _parent.BorderWidth);
//left
e.Graphics.DrawLine(pen, (_parent.BorderWidth) / 2,
_parent.BorderWidth * 2f,
(_parent.BorderWidth) / 2, Height - 1.5f *
_parent.BorderWidth - 1);
//right
e.Graphics.DrawLine(pen, Width - 1 - _parent.BorderWidth / 2,
_parent.BorderWidth * 2,
Width - 1 - _parent.BorderWidth / 2,
Height - 1.5f * _parent.BorderWidth - 1);
//under header
e.Graphics.DrawLine(pen, _parent.BorderWidth / 2f,
_parent.BorderWidth + _parent.HeaderHeight,
Width - _parent.BorderWidth,
_parent.BorderWidth + _parent.HeaderHeight);
}
//draw expand/collapse button
int buttonSize = (int)(headerRectangle.Height -
_parent.BorderWidth - 5);
_buttonRect = new Rectangle(this.Width -
_parent.BorderWidth - 5 - buttonSize,
_parent.BorderWidth + 2, buttonSize, buttonSize);
using (Pen pen = new Pen(GetColor(_parent.BorderColor,
Enabled && _parent.CanExpandCollapse), 1))
{
e.Graphics.DrawPath(pen,
CreateRoundedRectPath(_buttonRect, 1));
}
if (_buttonHighlighted && Enabled)
{
//draw button highlighting
using (Pen pen =
new Pen(Color.FromArgb(_parent.ButtonHighlightAlpha,
_parent.ButtonHighlightColor), 4))
{
Rectangle highlightRect = _buttonRect;
highlightRect.Inflate(-2, -2);
e.Graphics.DrawPath(pen,
CreateRoundedRectPath(highlightRect, 3));
}
}
//draw expand/collapse arrow
Rectangle shapeRect = new Rectangle(_buttonRect.X + 1,
_buttonRect.Y + 1, _buttonRect.Width - 1,
_buttonRect.Height - 1);
if (_heightAnimator.IsRunning || _expanded)
DrawArrow(e.Graphics, shapeRect,
GetColor(_parent.ButtonArrowColor,
Enabled && _parent.CanExpandCollapse), true);
if (_heightAnimator.IsRunning || !_expanded)
DrawArrow(e.Graphics, shapeRect,
GetColor(_parent.ButtonArrowColor,
Enabled && _parent.CanExpandCollapse), false);
if (_image != null && _parent.ImagesEnabled)
//draw image
{
int x = _parent.BorderWidth * 3;
int y = (int)(_parent.BorderWidth +
_parent.HeaderHeight / 2f - _image.Height / 2f);
if (Enabled)
e.Graphics.DrawImageUnscaled(_image, x, y);
else
ControlPaint.DrawImageDisabled(e.Graphics, _image, x, y,
GetColor(_parent.HeaderColor1));
}
if (_text != null)
{
//draw text
int textX = _parent.BorderWidth * 3 +
(_image == null ? 0 : _image.Width);
Rectangle textRect = new Rectangle(textX, _parent.BorderWidth,
_buttonRect.Left - textX - _parent.BorderWidth,
_parent.HeaderHeight - _parent.BorderWidth);
if (Enabled)
{
using (SolidBrush brush = new SolidBrush(base.ForeColor))
{
e.Graphics.DrawString(_text, base.Font, brush,
textRect, _parent.GetStringFormat());
}
}
else
{
ControlPaint.DrawStringDisabled(e.Graphics, _text, base.Font,
GetColor(base.ForeColor), textRect,
_parent.GetStringFormat());
}
}
}
It uses many different kinds of painting operations, and makes sure that the The following are the most important members of the public interface:
EventsThe GroupPaneBarA Group organizationTo create /// <summary>
/// Adds a new <see cref="GroupPane"/> to the end of the list.
/// </summary>
/// <param name="control">Element which should
/// initially beend placed in the new group.</param>
/// <param name="text">Initial text of the new group.</param>
/// <param name="image">Initial image of the new group.</param>
/// <param name="adjustGroupPaneHeightToControlheight">
/// Sets whether the expanded height of
/// the resulting group pane should match the height
/// of the given control.</param>
/// <returns>The newly created <see cref="GroupPane"/>.</returns>
public GroupPane Add(Control control, string text, Image image,
bool adjustGroupPaneHeightToControlheight);
The events Visual propertiesExcept the Expanding and collapsingBesides the ToDo's
History
|
||||||||||||||||||||||