vbAccelerator - Contents of code file: ListBar.csusing System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Diagnostics;
using vbAccelerator.Components.Drawing;
namespace vbAccelerator.Components.ListBarControl
{
#region Enumerations
/// <summary>
/// Enumeration specifying the view to use for the items within
/// a <see cref="ListBarGroup"/>.
/// </summary>
[Description("Enumeration specifying the view to use for the items within a
group.")]
public enum ListBarGroupView
{
/// <summary>
/// The ListBar will display using large icons, with the text underneath.
/// </summary>
[Description("The ListBar will display using large icons, with the text
underneath.")]
LargeIcons,
/// <summary>
/// The ListBar will display using small icons, with text to the left.
/// </summary>
[Description("The ListBar will display using small icons, with text to
the left.")]
SmallIcons,
/// <summary>
/// The ListBar will display using large icons with no text.
/// </summary>
[Description("The ListBar will display large icons with no text.")]
LargeIconsOnly,
/// <summary>
/// The ListBar will display using small icons with no text.
/// </summary>
[Description("The ListBar will display small icons with no text.")]
SmallIconsOnly
}
/// <summary>
/// Enumeration specifying how the <see cref="ListBar"/> control will draw.
/// </summary>
[Description("Enumeration specifying the ListBar control drawing style.")]
public enum ListBarDrawStyle
{
/// <summary>
/// The ListBar will draw using the style of the original Office
/// releases.
/// </summary>
[Description("The ListBar will draw using the style of the original
Office releases.")]
ListBarDrawStyleNormal,
/// <summary>
/// The ListBar will draw using the Office XP style.
/// </summary>
[Description("The ListBar will draw using the Office XP style.")]
ListBarDrawStyleOfficeXP,
/// <summary>
/// The ListBar will draw using the Office 2003 style
/// (not implemented yet).
/// </summary>
[Description("The ListBar will draw using the Office 2003 style (not
implemented yet).")]
ListBarDrawStyleOffice2003
}
#endregion
#region Event argument classes
/// <summary>
/// Provides details about an item which will undergo
/// an edit operation.
/// </summary>
public class ListBarLabelEditEventArgs : LabelEditEventArgs
{
private object labelEditObject = null;
/// <summary>
/// Returns the object for which label editing has
/// been requested. Can either be a <see cref="ListBarItem"/> or
/// a <see cref="ListBarGroup"/> (or a subclass of either).
/// </summary>
[Description("Gets the object for which label editing has been requested.
Either a ListBarItem or a ListBarGroup (or a subclass)")]
public object LabelEditObject
{
get
{
return this.labelEditObject;
}
}
/// <summary>
/// Constructs a new instance of this object
/// given the item, label and object.
/// </summary>
/// <param name="item">The index of the item being edited.</param>
/// <param name="label">The label of the item being edited.</param>
/// <param name="labelEditObject">The object being edited.</param>
[Description("Constructs a new instance of this object.")]
public ListBarLabelEditEventArgs(
int item,
string label,
object labelEditObject
) : base(item, label)
{
this.labelEditObject = labelEditObject;
}
}
/// <summary>
/// Provides event arguments for the BeforeSelectedGroupChanged event
/// raised by the control. This object contains the group that
/// would be selected and provides the opportunity to cancel the
/// group selection.
/// </summary>
public class BeforeGroupChangedEventArgs : EventArgs
{
/// <summary>
/// The ListBarGroup that would be selected.
/// </summary>
private ListBarGroup group;
/// <summary>
/// Whether to cancel the operation or not.
/// </summary>
private bool cancel = false;
/// <summary>
/// Gets the group that will be selected.
/// </summary>
[Description("Gets the group that will be selected.")]
public ListBarGroup Group
{
get
{
return this.group;
}
}
/// <summary>
/// Gets/sets whether the group selection should be cancelled
/// or not. By default the group selection is not cancelled.
/// </summary>
[Description("Gets/sets whether the group selection should be
cancelled.")]
public bool Cancel
{
get
{
return this.cancel;
}
set
{
this.cancel = value;
}
}
/// <summary>
/// Constructs a new instance of this object.
/// Called
/// by the <see cref="ListBar"/> control before firing a
/// <c>BeforeSelectedGroupChanged</c> event.
/// </summary>
/// <param name="group">The group that will be selected</param>
[Description("Constructs a new instance of this object.")]
public BeforeGroupChangedEventArgs(
ListBarGroup group
)
{
this.group = group;
}
}
/// <summary>
/// This class is used with the BeforeItemClicked event and provides
/// the item which is about to be clicked and the option to prevent
/// the item being clicked by setting the Cancel property.
/// </summary>
public class BeforeItemClickedEventArgs : EventArgs
{
/// <summary>
/// The ListBarItem which is about to be clicked.
/// </summary>
private ListBarItem item = null;
/// <summary>
/// Whether the click should be cancelled or not.
/// </summary>
private bool cancel = false;
/// <summary>
/// Gets/sets whether the click should be cancelled or not.
/// </summary>
[Description("Gets/sets whether the click should be cancelled or not.")]
public bool Cancel
{
get
{
return this.cancel;
}
set
{
this.cancel = value;
}
}
/// <summary>
/// Gets the ListBarItem that is about to be clicked.
/// </summary>
[Description("Gets the ListBarItem that is about to be clicked.")]
public ListBarItem Item
{
get
{
return this.item;
}
}
/// <summary>
/// Constructor for this object. Called
/// by the <see cref="ListBar"/> control before firing a
/// <see cref="BeforeItemClickedEventHandler"/> event.
/// </summary>
/// <param name="item">The item that's about to be clicked.</param>
[Description("Constructs a new instance of this object.")]
public BeforeItemClickedEventArgs(
ListBarItem item
)
{
this.item = item;
}
}
/// <summary>
/// This class is provides details of which item has been clicked
/// and the mouse details of the click when the <c>ItemClicked</c> event
/// is raised from a <c>ListBar</c>.
/// <seealso cref="ListBar.ItemClicked"/>
/// </summary>
public class ItemClickedEventArgs : ObjectClickedEventArgs
{
/// <summary>
/// The ListBarIem that has been clicked.
/// </summary>
private ListBarItem item = null;
/// <summary>
/// Gets the <see cref="ListBarItem"/> that has been clicked.
/// </summary>
[Description("Gets the ListBarItem that has been clicked.")]
public ListBarItem Item
{
get
{
return this.item;
}
}
/// <summary>
/// Constructs a new instance of this object. Called by the <see
cref="ListBar"/>
/// control when firing an <c>ItemClicked</c> event.
/// </summary>
/// <param name="item">The item that has been clicked</param>
/// <param name="location">The mouse location relative to the
/// control for the click.</param>
/// <param name="mouseButton">The mouse button used to click
/// the item.</param>
[Description("Constructs a new instance of this object")]
public ItemClickedEventArgs(
ListBarItem item,
Point location,
MouseButtons mouseButton
) : base(location, mouseButton)
{
this.item = item;
}
}
/// <summary>
/// This class is provides details of which item has been clicked
/// and the mouse details of the click when the <c>GroupClicked</c> event
/// is raised from a <see cref="ListBar" /> control.
/// </summary>
public class GroupClickedEventArgs : ObjectClickedEventArgs
{
/// <summary>
/// The ListBarGroup that has been clicked.
/// </summary>
private ListBarGroup group = null;
/// <summary>
/// Gets the <see cref="ListBarGroup"/> that has been clicked.
/// </summary>
[Description("Gets the ListBarGroup that has been clicked.")]
public ListBarGroup Group
{
get
{
return this.group;
}
}
/// <summary>
/// Constructs a new instance of this object. Called by the <see
cref="ListBar"/>
/// control when firing a <c>GroupClicked</c> event.
/// </summary>
/// <param name="group">The <see cref="ListBarGroup"/> that has been
clicked</param>
/// <param name="location">The mouse location relative to the
/// control for the click.</param>
/// <param name="mouseButton">The mouse button used to click
/// the item.</param>
[Description("Constructs a new instance of this object.")]
public GroupClickedEventArgs(
ListBarGroup group,
Point location,
MouseButtons mouseButton
) : base(location, mouseButton)
{
this.group = group;
}
}
/// <summary>
/// An abstract class used as the bases for the <c>ItemClicked</c>
/// and <c>GroupClicked</c> events of the <see cref="ListBar"/> control.
/// This class stores details of the mouse location and button.
/// </summary>
public abstract class ObjectClickedEventArgs : EventArgs
{
/// <summary>
/// The location of the mouse when the item was clicked.
/// </summary>
private Point location;
/// <summary>
/// The mouse button that was used.
/// </summary>
private MouseButtons mouseButton = MouseButtons.Left;
/// <summary>
/// The Location of the mouse, relative to the control,
/// when the item was clicked.
/// </summary>
[Description("The location of the mouse relative to the control when the
item was clicked.")]
public Point Location
{
get
{
return location;
}
}
/// <summary>
/// The MouseButton used to click the item.
/// </summary>
[Description("The mouse button used to click this item.")]
public MouseButtons MouseButton
{
get
{
return this.mouseButton;
}
}
/// <summary>
/// When used in a subclass, constructs a new instance of the class with
the specified
/// mouse location and button.
/// </summary>
/// <param name="location">The location of the mouse.</param>
/// <param name="mouseButton">The button which was pressed.</param>
[Description("When used in a subclass, constructs a new instance of this
class.")]
public ObjectClickedEventArgs(
Point location,
MouseButtons mouseButton
)
{
this.location = location;
this.mouseButton = mouseButton;
}
}
#endregion
#region Event delegates
/// <summary>
/// Represents the method that handles the BeforeSelectedGroupChanged event
/// of a ListBar control.
/// </summary>
public delegate void BeforeGroupChangedEventHandler(
object sender,
BeforeGroupChangedEventArgs e);
/// <summary>
/// Represents the method that handles the BeforeItemClicked event
/// of a ListBar control.
/// </summary>
public delegate void BeforeItemClickedEventHandler(
object sender,
BeforeItemClickedEventArgs e);
/// <summary>
/// Represents the method that handles the ItemClicked event of a
/// ListBar control.
/// </summary>
public delegate void ItemClickedEventHandler(
object sender,
ItemClickedEventArgs e);
/// <summary>
/// Represents the method that handles the GroupClicked event of a
/// ListBar control.
/// </summary>
public delegate void GroupClickedEventHandler(
object sender,
GroupClickedEventArgs e);
/// <summary>
/// Represents the method that handles the BeforeLabelEdit and AfterLabelEdit
/// events of a ListBar control.
/// </summary>
public delegate void ListBarLabelEditEventHandler(
object sender,
ListBarLabelEditEventArgs e);
#endregion
#region ListBar Control class
/// <summary>
/// An implementation of a Microsoft Outlook Style ListBar control.
/// The control provides all the features needed to implement a replica
/// of the Outlook style control and is also designed to allow the same
/// functionality to be used in overriden controls in which the
/// individual sizing and appearance of each of the UI components can be
/// customised.
///
/// The <c>ListBar</c> control is modelled as an extension to
/// the <c>System.Windows.Forms.UserControl</c> class. Bars
/// are configured using <see cref="ListBarGroup" /> objects which are
/// collected in the <see cref="ListBarGroupCollection" /> object
/// accessible through the control's <see cref="Groups" /> accessor.
/// Each <see cref="ListBarGroup" /> in turn contains a
/// <see cref="ListBarItemCollection" /> of <see cref="ListBarItem" />
objects
/// which represent the buttons within a group.
/// </summary>
///
/// <remarks>
/// Copyright © 2003 Steve McMahon for vbAccelerator.com.
/// vbAccelerator is a Trade Mark of vbAccelerator Ltd. All Rights
/// Reserved. Please visit http://vbaccelerator.com/ for more
/// on this and other VB and .NET Framework code. Comments to
/// mailto:steve@vbaccelerator.com.
/// </remarks>
///
public class ListBar : System.Windows.Forms.UserControl
{
#region Member Variables
/// <summary>
/// Reference to the collection of groups contained within the ListBar
control.
/// </summary>
private ListBarGroupCollection groups = null;
/// <summary>
/// Reference to an external ToolTip object.
/// </summary>
private ToolTip toolTip = null;
/// <summary>
/// Reference to an external Image List for drawing the large icon view.
/// </summary>
private ImageList largeImageList = null;
/// <summary>
/// Reference to an external Image List for drawing the small icon view.
/// </summary>
private ImageList smallImageList = null;
/// <summary>
/// A timer for controlling scrolling when the scroll buttons are held
/// down.
/// </summary>
private Timer buttonPressed = new Timer();
/// <summary>
/// Contains a reference to the active scroll button when one is pressed
/// and the mouse is over it.
/// </summary>
private ListBarScrollButton activeButton = null;
/// <summary>
/// The last time a scroll occurred during a drag-drop operation. Used
/// to control the speed of scrolling during drag-drop.
/// </summary>
private DateTime lastScrollTime = DateTime.Now;
/// <summary>
/// Drawing style fo the control.
/// </summary>
private ListBarDrawStyle drawStyle =
ListBarDrawStyle.ListBarDrawStyleOfficeXP;
/// <summary>
/// Last width the control was drawn at. Used to control resizing.
/// </summary>
private int lastWidth = 0;
/// <summary>
/// Last height the control was drawn at. Used to control resizing.
/// </summary>
private int lastHeight = 0;
/// <summary>
/// Flag to control whether redrawing occurs or not
/// during updating:
/// </summary>
private bool redraw = true;
/// <summary>
/// Up scroll button reference.
/// </summary>
protected ListBarScrollButton btnUp;
/// <summary>
/// Down scroll buttons reference.
/// </summary>
protected ListBarScrollButton btnDown;
/// <summary>
/// The rectangle containing the "ListView" portion of the control.
/// </summary>
private Rectangle rcListView;
/// <summary>
/// The object that the mouse is currently over, if any.
/// </summary>
private IMouseObject mouseTrack = null;
/// <summary>
/// The object that the mouse is currently down on, if any.
/// </summary>
private IMouseObject mouseDown = null;
/// <summary>
/// Whether items are selected on MouseDown or
/// MouseUp.
/// </summary>
private bool selectOnMouseDown = false;
/// <summary>
/// Whether items can be dragged or not
/// </summary>
private bool allowDragItems = true;
/// <summary>
/// Whether groups can be dragged or not
/// </summary>
private bool allowDragGroups = true;
/// <summary>
/// During drag-drop, the insert point, if any.
/// </summary>
private ListBarDragDropInsertPoint dragInsertPoint = null;
/// <summary>
/// The object that was last hovered over during
/// drag-drop, if any:
/// </summary>
private IMouseObject dragHoverOver = null;
/// <summary>
/// The time at which hovering started over the object
/// which is currently being hovered over:
/// </summary>
private DateTime dragHoverOverStartTime = DateTime.Now;
/// <summary>
/// The ListBarItem currently being edited, if any
/// </summary>
private ListBarItem editItem = null;
/// <summary>
/// The ListBarGroup currently being edited, if any
/// </summary>
private ListBarGroup editGroup = null;
/// <summary>
/// Are we scrolling a new group into view or not?
/// </summary>
private bool scrollingGroup = false;
/// <summary>
/// The index of the group which is currently selected
/// when scrolling a new group into view:
/// </summary>
protected int indexCurrent = -1;
/// <summary>
/// The index of the newly selected group which will replace
/// the selected index when scrolling a new group into view:
/// </summary>
protected int indexNew = -1;
/// <summary>
/// The Text Box used for editing an item's caption.
/// </summary>
private System.Windows.Forms.TextBox txtEdit;
/// <summary>
/// A class to determine when the TextBox used for
/// editing should be cancelled:
/// </summary>
private PopupCancelNotifier popupCancel;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
#endregion
#region Events
/// <summary>
/// Raised before the selected group in the ListBar control is changed.
Allows
/// the group selection to be cancelled.
/// </summary>
[Description("Raised before the selected group in the ListBar control is
changed.")]
public event BeforeGroupChangedEventHandler BeforeSelectedGroupChanged;
/// <summary>
/// Raised when the selected group in a ListBar control has been
/// changed.
/// </summary>
[Description("Raised once the selected group in the ListBar control has
been changed.")]
public event System.EventHandler SelectedGroupChanged;
/// <summary>
/// Raised before an item in a ListBar control is clicked. Allows
/// the item selection to be cancelled.
/// </summary>
[Description("Raised before an item in the ListBar control is clicked.")]
public event BeforeItemClickedEventHandler BeforeItemClicked;
/// <summary>
/// Raised when an item has been clicked in the ListBar control.
/// </summary>
[Description("Raised once an item in the ListBar control has been
clicked.")]
public event ItemClickedEventHandler ItemClicked;
/// <summary>
/// Raised when an item has been double clicked in the ListBar control.
/// </summary>
[Description("Raised when an item has been double clicked in the ListBar
control.")]
public event ItemClickedEventHandler ItemDoubleClicked;
/// <summary>
/// Raised when a group has been clicked in the ListBar control.
/// </summary>
[Description("Raised when a group has been clicked in the ListBar
control.")]
public event GroupClickedEventHandler GroupClicked;
/// <summary>
/// Raised before an item's label is about to be edited in the ListBar
/// control. Allows the label edit to be cancelled.
/// </summary>
[Description("Raised before an item's label is about to be edited in the
ListBar control.")]
public event ListBarLabelEditEventHandler BeforeLabelEdit;
/// <summary>
/// Raised after an item's label has been edited in the ListBar control.
/// Allows the new caption to be checked and the edit cancelled.
/// </summary>
[Description("Raised after an item's label has been edited but before the
change is committed.")]
public event ListBarLabelEditEventHandler AfterLabelEdit;
#endregion
#region Constructor and Dispose/Finalise
/// <summary>
/// Creates a new instance of a ListBar control.
/// </summary>
public ListBar()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
// Set up the control:
this.SetStyle(
ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint |
ControlStyles.DoubleBuffer |
ControlStyles.SupportsTransparentBackColor,
true);
// Initialisation:
groups = CreateListBarGroupCollection();
btnUp =
CreateListBarScrollButton(ListBarScrollButton.ListBarScrollButtonType.
Up);
btnDown =
CreateListBarScrollButton(ListBarScrollButton.ListBarScrollButtonType.
Down);
// Scroll timer:
buttonPressed.Interval = 350;
buttonPressed.Enabled = false;
buttonPressed.Tick += new EventHandler(buttonPressed_Tick);
// Text box:
txtEdit.KeyDown += new KeyEventHandler(txtEdit_KeyDown);
popupCancel = new PopupCancelNotifier();
popupCancel.PopupCancel += new
PopupCancelEventHandler(popupCancel_PopupCancel);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#endregion
#region Responding to events
/// <summary>
/// Controls scrolling when the mouse is over and down on a scroll
/// bar button.
/// </summary>
/// <param name="sender">The object which raised this event.</param>
/// <param name="e">Arguments associated with this event.</param>
private void buttonPressed_Tick(object sender, System.EventArgs e)
{
// check if the mouse is still over a scroll button
// that's been pressed:
if (activeButton != null)
{
// shorten the interval for the next scroll down
// to 75ms:
buttonPressed.Interval = 75;
// Check if mouse in button:
Point pos = Cursor.Position;
pos = this.PointToClient(pos);
if (activeButton.HitTest(pos))
{
// perform the scrolling:
Scroll(activeButton, true);
}
}
}
/// <summary>
/// Scroll the control for the selected button.
/// </summary>
/// <param name="button">Button to scroll for.</param>
/// <param name="fromTimer">Whether request to scroll from a
/// scroll button timer event.</param>
private void Scroll(ListBarScrollButton button, bool fromTimer)
{
int direction =
(button.ButtonType ==
ListBarScrollButton.ListBarScrollButtonType.Up ? 1 : -1);
Scroll(direction, fromTimer);
}
/// <summary>
/// Scroll the control for the selected button.
/// </summary>
/// <param name="button">Button to scroll for.</param>
private void Scroll(ListBarScrollButton button)
{
Scroll(button, false);
}
/// <summary>
/// Scroll the control in the specified direction.
/// </summary>
/// <param name="direction">The direction to move in. Note that this
follows
/// the direction of movement of an item: +1 scrolls up, -1 scrolls
down.</param>
private void Scroll(int direction)
{
Scroll(direction, false);
}
/// <summary>
/// Scroll the control in the specified direction.
/// </summary>
/// <param name="direction">The direction to move in. Note that this
follows
/// the direction of movement of an item: +1 scrolls up, -1 scrolls
down.</param>
/// <param name="fromTimer">Whether request to scroll from a
/// scroll button timer event.</param>
private void Scroll(int direction, bool fromTimer)
{
// get the distance we must scroll to move one entire
// item:
ListBarGroup selGroup = SelectedGroup;
int endScrollOffset = selGroup.ScrollOffset +
(direction * selGroup.Items[0].Height);
if (endScrollOffset > 0)
{
endScrollOffset = 0;
}
// Get the invalidation rectangle:
Rectangle rcInvalid = new Rectangle(
new Point(1, selGroup.ButtonLocation.X + selGroup.ButtonHeight),
new Size(this.Width - 2,
((groups.IndexOf(selGroup) == groups.Count - 1) ?
this.Height - (selGroup.ButtonLocation.Y +
selGroup.ButtonHeight) :
groups[groups.IndexOf(selGroup) + 1].ButtonLocation.Y)));
// Starting from the current point, scroll the selected
// bar to the new point in ever increasing steps:
int step = direction;
if (fromTimer)
{
step *= selGroup.Items[0].Height / 4;
}
while (selGroup.ScrollOffset != endScrollOffset)
{
// determine the new scroll offset:
int newOffset = selGroup.ScrollOffset + step;
if (direction < 0)
{
if (newOffset < endScrollOffset)
{
newOffset = endScrollOffset;
}
}
else
{
if (newOffset > endScrollOffset)
{
newOffset = endScrollOffset;
}
}
selGroup.ScrollOffset = newOffset;
// refresh the display:
Invalidate();
this.Update();
// Make the next step larger.
step *= 2;
}
// Ensure that everything is shown in the right place
DoResize();
}
/// <summary>
/// Raises the Resize event and performs internal
/// sizing of the objects in the control.
/// </summary>
/// <param name="e"></param>
protected override void OnResize(EventArgs e)
{
DoResize();
base.OnResize(e);
}
/// <summary>
/// Raises the SizeChanged event for this control
/// and internally sizes the display.
/// </summary>
/// <param name="e">Event arguments associated with this
/// event.</param>
protected override void OnSizeChanged(EventArgs e)
{
DoResize();
this.Invalidate();
base.OnSizeChanged(e);
}
private ListBarGroup ensureSelection()
{
ListBarGroup selectedGroup = SelectedGroup;
if ((selectedGroup == null) || (!selectedGroup.Visible))
{
selectedGroup = null;
if (groups.Count > 0)
{
for (int i = 0; i < groups.Count; i++)
{
if ((groups[i].Visible) && (selectedGroup == null))
{
groups[i].Selected = true;
selectedGroup = groups[i];
}
else
{
if (groups[i].Selected)
{
groups[i].Selected = false;
}
}
}
}
}
return selectedGroup;
}
/// <summary>
/// Called by the control's internal sizing mechanism.
/// Returns the client size excluding the border of the
/// control.
/// </summary>
/// <returns>A <c>Rectangle</c> providing the area to
/// draw the control into.</returns>
protected virtual Rectangle GetClientRectangleExcludingBorder()
{
Rectangle rcClient = new Rectangle(
this.ClientRectangle.Left + 1,
this.ClientRectangle.Top + 1,
this.ClientRectangle.Width - 2,
this.ClientRectangle.Height - 2);
return rcClient;
}
/// <summary>
/// Called by the control's internal sizing mechanism.
/// Returns the rectangle for a scroll button.
/// </summary>
/// <param name="buttonType">The scroll button to
/// get the rectangle for.</param>
/// <param name="selectedGroup">The Selected Group in the control.</param>
/// <param name="internalGroupHeight">The internal height of the
/// selected group</param>
/// <returns>The Rectangle for the scroll button.</returns>
protected virtual Rectangle GetScrollButtonRectangle(
ListBarScrollButton.ListBarScrollButtonType buttonType,
ListBarGroup selectedGroup,
int internalGroupHeight
)
{
Rectangle buttonRect;
if (buttonType == ListBarScrollButton.ListBarScrollButtonType.Up)
{
buttonRect = new Rectangle(
new Point(
((this.RightToLeft == RightToLeft.Yes) ?
2 :
this.Width - 2 - btnUp.Rectangle.Width),
selectedGroup.ButtonLocation.Y + selectedGroup.ButtonHeight + 2),
btnUp.Rectangle.Size);
}
else
{
buttonRect = new Rectangle(
new Point(
((this.RightToLeft == RightToLeft.Yes) ?
2 :
this.Width - 2 - btnUp.Rectangle.Width),
selectedGroup.ButtonLocation.Y + selectedGroup.ButtonHeight +
internalGroupHeight - 2 - btnDown.Rectangle.Height),
btnDown.Rectangle.Size);
}
return buttonRect;
}
private void DoResize()
{
if (this.redraw)
{
if (this.groups.Count > 0)
{
ListBarGroup selectedGroup = ensureSelection();
if (selectedGroup != null)
{
Rectangle rcClient = GetClientRectangleExcludingBorder();
rcListView = new Rectangle(rcClient.Location, rcClient.Size);
int lastVisibleGroup = 0;
int firstVisibleGroup = groups.Count - 1;
int nextVisibleGroup = firstVisibleGroup;
for (int i = 0; i <= groups.IndexOf(selectedGroup); i++)
{
ListBarGroup group = groups[i];
if (group.Visible)
{
int buttonWidth = GetGroupButtonWidth(group);
group.SetLocationAndWidth(
new Point(rcClient.Left, rcListView.Top),
buttonWidth);
rcListView.Y += group.ButtonHeight;
rcListView.Height -= group.ButtonHeight;
if (i > lastVisibleGroup)
{
lastVisibleGroup = i;
}
if (i < firstVisibleGroup)
{
firstVisibleGroup = i;
}
}
}
int bottom = rcClient.Bottom;
for (int i = groups.Count - 1; i >
groups.IndexOf(selectedGroup); i--)
{
ListBarGroup group = groups[i];
if (group.Visible)
{
int buttonWidth = GetGroupButtonWidth(group);
bottom -= group.ButtonHeight;
rcListView.Height -= group.ButtonHeight;
group.SetLocationAndWidth(
new Point(rcClient.Left, bottom),
buttonWidth);
if (i > lastVisibleGroup)
{
lastVisibleGroup = i;
}
if (i < nextVisibleGroup)
{
nextVisibleGroup = i;
}
}
}
int size = selectedGroup.Items.Height;
int height = selectedGroup.ButtonLocation.Y +
selectedGroup.ButtonHeight;
if (groups.IndexOf(selectedGroup) == lastVisibleGroup)
{
height = this.ClientRectangle.Height - height;
}
else
{
height = groups[nextVisibleGroup].ButtonLocation.Y -
height;
}
bool needUp = false;
bool needDown = false;
needUp = (selectedGroup.ScrollOffset < 0);
needDown = ((size + selectedGroup.ScrollOffset) > height);
Rectangle btnUpRect = GetScrollButtonRectangle(
ListBarScrollButton.ListBarScrollButtonType.Up,
selectedGroup,
height);
btnUp.SetRectangle(btnUpRect);
btnUp.Visible = needUp;
if (!needUp)
{
if (this.activeButton != null)
{
if (this.activeButton.Equals(btnUp))
{
buttonPressed.Enabled = false;
}
}
}
Rectangle btnDownRect = GetScrollButtonRectangle(
ListBarScrollButton.ListBarScrollButtonType.Down,
selectedGroup, height);
btnDown.SetRectangle(btnDownRect);
btnDown.Visible = needDown;
if (!needDown)
{
if (this.activeButton != null)
{
if (this.activeButton.Equals(btnDown))
{
buttonPressed.Enabled = false;
}
}
}
}
else
{
btnUp.Visible = false;
btnDown.Visible = false;
}
if (this.Width != lastWidth)
{
lastWidth = this.Width;
}
if (this.Height != lastHeight)
{
lastHeight = this.Height;
}
}
}
}
/// <summary>
/// Raises the Paint event and performs internal drawing of the
/// control.
/// </summary>
/// <param name="e">A PaintEventArgs object with details about the
/// paint event that must be performed.</param>
protected override void OnPaint ( System.Windows.Forms.PaintEventArgs e )
{
if (scrollingGroup)
{
RenderScrollNewGroup(e);
}
else
{
Render(e);
}
base.OnPaint(e);
}
/// <summary>
/// Raises the double click event and performs internal double-click
/// processing for the control.
/// </summary>
/// <param name="e"><see cref="EventArgs"/> associated with this
/// double-click event.</param>
protected override void OnDoubleClick( EventArgs e)
{
base.OnDoubleClick(e);
Point pt = this.PointToClient(Cursor.Position);
IMouseObject obj = HitTest(pt, false);
if (obj != null)
{
if (typeof(ListBarItem).IsAssignableFrom(obj.GetType()))
{
ListBarItem item = (ListBarItem)obj;
MouseButtons button = MouseButtons.Left; // TODO should use
GetAsyncKeyState or whatever the Framework equivalent is
ItemClickedEventArgs ice = new ItemClickedEventArgs(
item, pt, button);
OnItemDoubleClicked(ice);
}
}
}
/// <summary>
/// Raises the <see cref="ItemDoubleClicked"/> event for an item.
/// </summary>
/// <param name="e">The <see cref="ItemClickedEventArgs"/> details
/// associated with the double click event.</param>
protected virtual void OnItemDoubleClicked(ItemClickedEventArgs e)
{
if (this.ItemDoubleClicked != null)
{
this.ItemDoubleClicked(this, e);
}
}
/// <summary>
/// Raises the MouseDown event and performs internal mouse-down
/// processing for the control.
/// </summary>
/// <param name="e">A MouseEventArgs object with details about the
/// mouse event that has occurred.</param>
protected override void OnMouseDown( System.Windows.Forms.MouseEventArgs
e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
if (mouseTrack != null)
{
mouseDown = mouseTrack;
mouseDown.MouseDown = true;
mouseDown.MouseDownPoint = new Point(e.X, e.Y);
// Check whether a scroll button has been pressed.
// If it has, then start a timer to auto-scroll
// more.
if
(typeof(ListBarScrollButton).IsAssignableFrom(mouseTrack.GetType
()))
{
// Set the active scrolling button:
activeButton = (ListBarScrollButton)mouseTrack;
// perform the initial scroll:
Scroll(activeButton);
// initialise the timer:
buttonPressed.Interval = 350;
buttonPressed.Enabled = true;
}
else if
(typeof(ListBarItem).IsAssignableFrom(mouseTrack.GetType()))
{
if (this.selectOnMouseDown)
{
MouseSelectItem((ListBarItem)mouseTrack, e);
}
}
// Redraw the control:
Invalidate();
}
}
}
/// <summary>
/// Raises the MouseMove event and performs mouse move processing
/// for the control.
/// </summary>
/// <param name="e">A MouseEventArgs object describing the mouse
/// move event that has occurred.</param>
protected override void OnMouseMove( System.Windows.Forms.MouseEventArgs
e)
{
base.OnMouseMove(e);
// no motion during item editing
if (editItem != null)
{
return;
}
// detect if the mouse is over anything:
IMouseObject newMouseOver = HitTest(new Point(e.X, e.Y));
if (newMouseOver == null)
{
if (mouseTrack != null)
{
mouseTrack.MouseOver = false;
mouseTrack = null;
this.Cursor = Cursors.Default;
Invalidate();
}
if (this.toolTip != null)
{
this.toolTip.SetToolTip(this, "");
}
}
else
{
bool noChange = false;
if (mouseTrack != null)
{
if (mouseTrack == newMouseOver)
{
// We're not tracking a new item.
noChange = true;
// However, if we mouse-downed on an item, then we
// should check if the new mouse position is sufficiently
// far from the original position that a drag operation
// is in order:
if (this.allowDragItems)
{
if
(typeof(ListBarItem).IsAssignableFrom(mouseTrack.GetType()
))
{
if (mouseTrack.MouseDown)
{
int hysteresis = (SelectedGroup.View ==
ListBarGroupView.LargeIcons ? 4 : 2);
if ((Math.Abs(mouseTrack.MouseDownPoint.X - e.X) >
hysteresis) ||
(Math.Abs(mouseTrack.MouseDownPoint.Y - e.Y) >
hysteresis))
{
// time to start dragging:
ListBarItem dragItem = (ListBarItem)mouseTrack;
this.DoDragDrop(dragItem, DragDropEffects.Move);
InternalDragDropComplete(dragItem, true);
EnsureItemVisible(dragItem);
return;
}
}
}
}
if (this.allowDragGroups)
{
if
(typeof(ListBarGroup).IsAssignableFrom(mouseTrack.GetType(
)))
{
if (mouseTrack.MouseDown)
{
if ((Math.Abs(mouseTrack.MouseDownPoint.X - e.X) >
4) ||
(Math.Abs(mouseTrack.MouseDownPoint.Y - e.Y) > 4))
{
// time to start dragging:
ListBarGroup dragGroup = (ListBarGroup)mouseTrack;
this.DoDragDrop(dragGroup, DragDropEffects.Move);
//InternalDragDropComplete(dragGroup);
dragGroup.MouseOver = false;
dragGroup.MouseDown = false;
return;
}
}
}
}
}
else
{
mouseTrack.MouseOver = false;
}
}
if (!noChange)
{
mouseTrack = newMouseOver;
if (this.toolTip != null)
{
this.toolTip.SetToolTip(this, mouseTrack.ToolTipText);
}
mouseTrack.MouseOver = true;
if (typeof(ListBarGroup).IsAssignableFrom(mouseTrack.GetType()))
{
this.Cursor = Cursors.Hand;
}
else
{
this.Cursor = Cursors.Default;
}
Invalidate();
}
}
}
/// <summary>
/// Raises the MouseUp event and performs mouse up processing
/// for the control.
/// </summary>
/// <param name="e">A MouseEventArgs object describing the mouse
/// move event that has occurred.</param>
protected override void OnMouseUp( System.Windows.Forms.MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button == MouseButtons.Left)
{
if (mouseTrack != null)
{
if (mouseTrack.Equals(mouseDown))
{
if
(typeof(ListBarGroup).IsAssignableFrom(mouseTrack.GetType()))
{
BeforeGroupChangedEventArgs bgc = new
BeforeGroupChangedEventArgs(
(ListBarGroup)mouseTrack);
OnBeforeGroupChanged(ref bgc);
if (!bgc.Cancel)
{
// group clicked. Select the new group:
SelectGroup((ListBarGroup)mouseTrack);
OnSelectedGroupChanged(new System.EventArgs());
GroupClickedEventArgs gce = new GroupClickedEventArgs(
(ListBarGroup)mouseTrack,
new Point(e.X, e.Y),
e.Button);
OnGroupClicked(gce);
}
}
else if
(typeof(ListBarScrollButton).IsAssignableFrom(mouseTrack.GetT
ype()))
{
// don't need to do anything here, except be sure
// we reset the active scroll button & timer later
}
else
{
if (activeButton == null)
{
if (!this.selectOnMouseDown)
{
MouseSelectItem((ListBarItem)mouseTrack, e);
}
}
}
}
}
// no more scrolling
activeButton = null;
buttonPressed.Enabled = false;
if (mouseDown != null)
{
mouseDown.MouseDown = false;
mouseDown.MouseOver = false;
}
if (mouseTrack != null)
{
mouseTrack.MouseOver = false;
}
Invalidate();
}
else if (e.Button == MouseButtons.Right)
{
if (mouseTrack != null)
{
// Right click?
if (typeof(ListBarGroup).IsAssignableFrom(mouseTrack.GetType()))
{
GroupClickedEventArgs gce = new GroupClickedEventArgs(
(ListBarGroup)mouseTrack,
new Point(e.X, e.Y),
e.Button);
OnGroupClicked(gce);
}
else if
(typeof(ListBarItem).IsAssignableFrom(mouseTrack.GetType()))
{
ItemClickedEventArgs ic = new ItemClickedEventArgs(
(ListBarItem)mouseTrack,
new Point(e.X, e.Y),
e.Button );
OnItemClicked(ic);
}
else
{
// no action currently
}
}
else
{
// group right click:
GroupClickedEventArgs gce = new GroupClickedEventArgs(
SelectedGroup,
new Point(e.X, e.Y),
e.Button);
OnGroupClicked(gce);
}
}
}
/// <summary>
/// Raises the MouseLeave event and performs internal mouse
/// track processing for the control.
/// </summary>
/// <param name="e">Event arguments associated with this event.</param>
protected override void OnMouseLeave( System.EventArgs e)
{
base.OnMouseLeave(e);
if (mouseTrack != null)
{
mouseTrack.MouseOver = false;
mouseTrack = null;
this.Cursor = Cursors.Default;
Invalidate();
}
}
/// <summary>
/// Raises the MouseWheel event and performs mouse wheel
/// processing for the control.
/// </summary>
/// <param name="e">A MouseEventArgs object describing the mouse
/// move event that has occurred.</param>
protected override void OnMouseWheel (
System.Windows.Forms.MouseEventArgs e )
{
base.OnMouseWheel(e);
if ((e.Delta > 0) && (btnUp.Visible))
{
Scroll(1);
}
else if ((e.Delta < 0) && (btnDown.Visible))
{
Scroll(-1);
}
}
private object GetBestDragDropFormat(DragEventArgs e)
{
object ret = null;
object defaultFormat = null;
foreach (string format in e.Data.GetFormats() )
{
object thisFormatData = e.Data.GetData(format);
if (defaultFormat == null)
{
defaultFormat = thisFormatData;
}
if (typeof(ListBarItem).IsAssignableFrom(thisFormatData.GetType()))
{
ret = thisFormatData;
break;
}
else if
(typeof(ListBarItem).IsAssignableFrom(thisFormatData.GetType()))
{
ret = thisFormatData;
break;
}
}
if (ret == null)
{
ret = defaultFormat;
}
return ret;
}
private object GetTypeOrSubClassFromData(DragEventArgs e, Type dataType)
{
object ret = null;
foreach (string format in e.Data.GetFormats() )
{
if (dataType.IsAssignableFrom(e.Data.GetData(format).GetType()))
{
ret = e.Data.GetData(format);
break;
}
}
return ret;
}
private bool PerformAutoDrag(DragEventArgs e)
{
bool ret = false;
if ((this.allowDragItems) || (this.allowDragGroups))
{
foreach (string format in e.Data.GetFormats() )
{
Type dataType = e.Data.GetData(format).GetType();
if (typeof(ListBarItem).IsAssignableFrom(dataType))
{
ret = true;
break;
}
else if (typeof(ListBarGroup).IsAssignableFrom(dataType))
{
ret = true;
break;
}
}
}
return ret;
}
/// <summary>
/// Raises the DragOver event and performs internal processing of
/// drag-drop to show the insertion point and navigate through
/// the items in the control.
/// </summary>
/// <param name="e">A DragEventArgs object describing the drag
/// over being performed.</param>
protected override void OnDragOver(DragEventArgs e)
{
// perform the base operation:
base.OnDragOver(e);
if (groups.Count > 0)
{
if (e.Effect != DragDropEffects.None)
{
this.InternalDragOverProcess(e, true);
}
else if (this.PerformAutoDrag(e))
{
this.InternalDragOverProcess(e, false);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnDragDrop(DragEventArgs e)
{
// perform the base operation:
base.OnDragDrop(e);
if (groups.Count > 0)
{
object obj = GetBestDragDropFormat(e);
if (e.Effect != DragDropEffects.None)
{
bool move = (e.Effect == DragDropEffects.Move);
this.InternalDragDropComplete(obj, move);
}
else if (this.PerformAutoDrag(e))
{
this.InternalDragDropComplete(obj, true);
}
}
}
/// <summary>
/// Raises the BeforeSelectedGroupChanged event. This event enables
/// the user to prevent a group selection.
/// </summary>
/// <param name="e">The BeforeGroupChangedEventArgs object associated
/// with this event.</param>
protected virtual void OnBeforeGroupChanged(ref
BeforeGroupChangedEventArgs e)
{
if (this.BeforeSelectedGroupChanged != null)
{
this.BeforeSelectedGroupChanged(this, e);
}
}
/// <summary>
/// Raises the BeforeItemClicked event. This event enables
/// the user to prevent an item from being selected.
/// </summary>
/// <param name="e">The BeforeItemClickedEventArgs object associated
/// with this event.</param>
protected virtual void OnBeforeItemClicked(ref BeforeItemClickedEventArgs
e)
{
e.Cancel = (!e.Item.Enabled);
if (this.BeforeItemClicked != null)
{
this.BeforeItemClicked(this, e);
}
}
/// <summary>
/// Raises the <c>ItemClicked</c> event.
/// </summary>
/// <param name="e">The <c>ItemClickedEventArgs</c> object associated
/// with this event.</param>
protected virtual void OnItemClicked(ItemClickedEventArgs e)
{
if (this.ItemClicked != null)
{
this.ItemClicked(this, e);
}
}
/// <summary>
/// Raises the <c>GroupClicked</c> event.
/// </summary>
/// <param name="e">The <c>GroupClickedEventArgs</c> object
/// associated with this event.</param>
protected virtual void OnGroupClicked(GroupClickedEventArgs e)
{
if (this.GroupClicked != null)
{
this.GroupClicked(this, e);
}
}
/// <summary>
/// Raises the BeforeLabelEdit event for an item in the control.
/// </summary>
/// <param name="e">The LabelEditEventArgs describing the item
/// that is about to be edited and allowing the edit action
/// to be cancelled.</param>
protected virtual void OnBeforeLabelEdit ( ListBarLabelEditEventArgs e )
{
if (BeforeLabelEdit != null)
{
this.BeforeLabelEdit(this, e);
}
}
/// <summary>
/// Raises the AfterLabelEdit event for an item in the control.
/// </summary>
/// <param name="e">The AfterEditEventArgs describing the item
/// that has just been edited and allowing the edit action
/// to be cancelled or the new caption to be changed.</param>
protected virtual void OnAfterLabelEdit ( ListBarLabelEditEventArgs e )
{
if (AfterLabelEdit != null)
{
this.AfterLabelEdit(this, e);
}
}
/// <summary>
/// Raises the <c>SelectedGroupChanged</c> event.
/// </summary>
/// <param name="e">An EventArgs object associated with the event.</param>
protected virtual void OnSelectedGroupChanged ( System.EventArgs e )
{
if (SelectedGroupChanged != null)
{
SelectedGroupChanged(this, e);
}
}
private void txtEdit_TextChanged(object sender, System.EventArgs e)
{
if (editItem != null)
{
setTextBoxSize(editItem);
}
}
private void txtEdit_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyData)
{
case Keys.Return:
// end editing:
EndTextEdit(true);
break;
case Keys.Escape:
// cancel editing:
EndTextEdit(false);
break;
}
}
private void popupCancel_PopupCancel(object sender, EventArgs e)
{
EndTextEdit(true);
}
#endregion
#region Internal implementation
private void EndTextEdit(bool commit)
{
if (this.editItem != null)
{
ListBarItem editedItem = this.editItem;
this.editItem = null;
if ((commit) && (editedItem != null))
{
ListBarGroup selectedGroup = SelectedGroup;
ListBarLabelEditEventArgs lea = new ListBarLabelEditEventArgs(
selectedGroup.Items.IndexOf(editItem), txtEdit.Text,
editedItem);
OnAfterLabelEdit(lea);
if (!lea.CancelEdit )
{
if (editedItem != null) // may be shutting down...
{
editedItem.Caption = lea.Label;
}
}
}
}
else if (this.editGroup != null)
{
ListBarGroup editedGroup = this.editGroup;
this.editGroup = null;
if ((commit) && (editedGroup != null))
{
ListBarLabelEditEventArgs lea = new ListBarLabelEditEventArgs(
this.Groups.IndexOf(editedGroup), txtEdit.Text, editedGroup);
OnAfterLabelEdit(lea);
if (!lea.CancelEdit)
{
if (editedGroup != null)
{
editedGroup.Caption = lea.Label;
}
}
}
}
txtEdit.Visible = false;
Invalidate();
}
private void InternalDragDropComplete(
object dragItem,
bool move
)
{
ListBarItem listBarDragItem = null;
if (typeof(ListBarItem).IsAssignableFrom(dragItem.GetType()))
{
listBarDragItem = (ListBarItem)dragItem;
listBarDragItem.MouseOver = false;
listBarDragItem.MouseDown = false;
}
if (dragInsertPoint != null)
{
ListBarGroup groupTo = SelectedGroup;
if (groupTo != null) // cannot happen...
{
ListBarGroup groupFrom = null;
if (listBarDragItem != null)
{
// Check which bar we've come from
// (it may be none, we may have come
// from another control):
foreach (ListBarGroup group in groups)
{
if (group.Items.Contains(listBarDragItem))
{
groupFrom = group;
break;
}
}
}
if (groupFrom != null) // Dragged from this control
{
// moving to a new group:
if (move)
{
if (dragInsertPoint.ItemAfter != null)
{
if (dragInsertPoint.ItemAfter.Equals(listBarDragItem))
{
listBarDragItem = null;
}
}
else if (dragInsertPoint.ItemBefore != null)
{
if (dragInsertPoint.ItemBefore.Equals(listBarDragItem))
{
listBarDragItem = null;
}
}
if (listBarDragItem != null)
{
groupFrom.Items.Remove(listBarDragItem);
}
}
else
{
// Clone a new item to add:
ListBarItem newItem = new ListBarItem(
listBarDragItem.Caption, listBarDragItem.IconIndex,
listBarDragItem.ToolTipText,
listBarDragItem.Tag);
listBarDragItem = newItem;
}
}
else
{
// add a new item which represents what's been dragged
if (listBarDragItem != null)
{
// there's an issue with which image to pick here
listBarDragItem = new ListBarItem(
listBarDragItem.Caption, listBarDragItem.IconIndex,
listBarDragItem.ToolTipText, listBarDragItem.Tag);
}
else
{
// Create a new item
listBarDragItem = new ListBarItem(
dragItem.ToString());
((ListBarItem)dragItem).Tag = dragItem;
}
}
if (listBarDragItem != null)
{
if (dragInsertPoint.ItemAfter != null)
{
groupTo.Items.InsertAfter(dragInsertPoint.ItemAfter,
listBarDragItem);
}
else
{
groupTo.Items.InsertAfter(dragInsertPoint.ItemAfter,
listBarDragItem);
}
}
}
}
dragInsertPoint = null;
Invalidate();
}
private void SelectGroup(ListBarGroup group)
{
// first work out the scrolling logic:
ListBarGroup selGroup = SelectedGroup;
if (selGroup != group)
{
// Which groups are we moving between?
this.indexNew = this.groups.IndexOf(group);
this.indexCurrent = this.groups.IndexOf(selGroup);
// Scrolling the new group into view:
if (this.redraw)
{
this.scrollingGroup = true;
if (this.indexNew > this.indexCurrent)
{
// the new index is below the current one.
// Scroll buttons from indexCurrent + 1 to indexNew
// upwards
int newIndexTargetPos = selGroup.ButtonLocation.Y +
selGroup.ButtonHeight;
for (int i = this.indexCurrent + 1; i <= this.indexNew - 1;
i++)
{
if (this.groups[i].Visible)
{
newIndexTargetPos += this.groups[i].ButtonHeight;
}
}
bool finished = false;
int currentPos = group.ButtonLocation.Y;
int step = -1;
while (!finished)
{
currentPos += step;
if (currentPos <= newIndexTargetPos)
{
step += (newIndexTargetPos - currentPos);
currentPos = newIndexTargetPos;
finished = true;
}
for (int i = this.indexCurrent + 1; i <= this.indexNew;
i++)
{
ListBarGroup workGroup = this.groups[i];
if (workGroup.Visible)
{
Point newLocation = workGroup.ButtonLocation;
newLocation.Y += step;
workGroup.SetLocationAndWidth(
newLocation, workGroup.ButtonWidth);
}
}
this.Invalidate();
this.Update();
step *= 2;
}
}
else
{
// the new index is above the current one.
// scroll buttons from indexNew + 1 to indexCurrent
// downwards
int lastIndex = indexCurrent;
int nextIndex = this.Groups.Count -1;
for (int i = indexCurrent + 1; i < this.Groups.Count; i++)
{
if (i > lastIndex)
{
lastIndex = i;
}
if (i < nextIndex)
{
nextIndex = i;
}
}
int currentTargetPos = (indexCurrent == lastIndex ?
this.ClientRectangle.Height :
this.groups[nextIndex].ButtonLocation.Y);
bool finished = false;
int currentPos = selGroup.ButtonLocation.Y;
int step = 1;
while (!finished)
{
currentPos += step;
if (currentPos >= currentTargetPos)
{
step -= (currentPos - currentTargetPos);
currentPos = currentTargetPos;
finished = true;
}
for (int i = indexNew + 1; i <= indexCurrent; i++)
{
ListBarGroup workGroup = this.groups[i];
if (workGroup.Visible)
{
Point newLocation = workGroup.ButtonLocation;
newLocation.Y += step;
workGroup.SetLocationAndWidth(newLocation,
workGroup.ButtonWidth);
}
}
this.Invalidate();
this.Update();
step *= 2;
}
}
this.scrollingGroup = false;
}
selGroup.Selected = false;
group.Selected = true;
DoResize();
}
}
/// <summary>
/// Selects an item in response to a mouse event.
/// </summary>
/// <param name="item">Item to be selected.</param>
/// <param name="e"><see cref="System.Windows.Forms.MouseEventArgs"/>
/// details associated with the mouse event.</param>
private void MouseSelectItem(ListBarItem item, MouseEventArgs e)
{
BeforeItemClickedEventArgs bic = new BeforeItemClickedEventArgs(
(ListBarItem)mouseTrack);
OnBeforeItemClicked(ref bic);
if (!bic.Cancel)
{
// item clicked:
SelectItem((ListBarItem)mouseTrack);
ItemClickedEventArgs ic = new ItemClickedEventArgs(
(ListBarItem)mouseTrack,
new Point(e.X, e.Y),
e.Button );
OnItemClicked(ic);
}
}
/// <summary>
/// Selects an item in the selected bar and makes
/// it visible.
/// </summary>
/// <param name="item">The item to select.</param>
private void SelectItem(ListBarItem item)
{
BeginUpdate();
foreach (ListBarItem otherItem in SelectedGroup.Items)
{
otherItem.Selected = false;
}
item.Selected = true;
EndUpdate();
EnsureItemVisible(item);
Invalidate();
}
/// <summary>
/// Starts editing the specified ListBarGroup. Note this
/// method is called from the StartEdit method of a ListBarGroup.
/// </summary>
/// <param name="group">The group to start editing.</param>
protected internal void StartGroupEdit(ListBarGroup group)
{
// Fire the BeforeLabelEdit event:
ListBarLabelEditEventArgs e = new ListBarLabelEditEventArgs(
this.groups.IndexOf(group), group.Caption, group);
OnBeforeLabelEdit(e);
if (!e.CancelEdit)
{
editGroup = group;
// Focus the control:
this.Focus();
// Set the edit text:
txtEdit.Text = group.Caption;
txtEdit.Font = (group.Font == null ? this.Font : group.Font);
txtEdit.Location = group.ButtonLocation;
txtEdit.Size = new Size(group.ButtonWidth, group.ButtonHeight);
txtEdit.Visible = true;
txtEdit.BringToFront();
txtEdit.Focus();
popupCancel.StartTracking(txtEdit);
}
}
/// <summary>
/// Starts editing the specified <c>ListBarItem</c>. Note this
/// method is called from the <c>StartEdit</c> method of a
/// <c>ListBarItem</c>.
/// <seealso cref="ListBarItem.StartEdit"/>
/// </summary>
/// <param name="item">The item to start editing.</param>
protected internal void StartItemEdit(ListBarItem item)
{
// Get rectangle of item relative to control:
ListBarGroup selectedGroup = SelectedGroup;
// Check whether item is part of the selected
// control:
if (selectedGroup.Items.Contains(item))
{
// Fire the BeforeLabelEdit event:
ListBarLabelEditEventArgs e = new ListBarLabelEditEventArgs(
selectedGroup.Items.IndexOf(item), item.Caption, item);
OnBeforeLabelEdit(e);
if (!e.CancelEdit)
{
editItem = item;
// Make sure we can see it:
EnsureItemVisible(item);
// Focus the control:
this.Focus();
// Set the edit text:
txtEdit.Text = item.Caption;
txtEdit.Font = (item.Font == null ? this.Font : item.Font);
setTextBoxSize(editItem);
int top = item.TextRectangle.Top;
txtEdit.Top = top;
txtEdit.Visible = true;
txtEdit.BringToFront();
txtEdit.Focus();
popupCancel.StartTracking(txtEdit);
}
}
else
{
throw new InvalidOperationException(
"Editing is only possible on items belonging to the
SelectedGroup in the control.");
}
}
private void setTextBoxSize(ListBarItem editItem)
{
ListBarGroup selectedGroup = SelectedGroup;
if (selectedGroup != null)
{
string text = txtEdit.Text;
if (text.Length == 0)
{
text = "Xg";
}
int maxWidth = 0;
if (selectedGroup.View == ListBarGroupView.SmallIcons)
{
maxWidth = this.ClientRectangle.Width -
editItem.TextRectangle.Left - 1;
}
else
{
maxWidth = this.ClientRectangle.Width - 2;
}
Graphics gfx = Graphics.FromHwnd(txtEdit.Handle);
StringFormat fmt = new StringFormat(StringFormatFlags.LineLimit |
(txtEdit.RightToLeft == RightToLeft.Yes ?
StringFormatFlags.DirectionRightToLeft : 0));
fmt.Alignment = StringAlignment.Center;
SizeF textSize = gfx.MeasureString(text, txtEdit.Font, maxWidth -
6, fmt);
fmt.Dispose();
gfx.Dispose();
if (textSize.Width < 24)
{
textSize.Width = 24;
}
textSize.Height += 2.0F;
txtEdit.Size = new Size((int)textSize.Width + 6,
(int)textSize.Height + 4);
if (selectedGroup.View == ListBarGroupView.SmallIcons)
{
txtEdit.Left = editItem.TextRectangle.Left + 1;
}
else
{
txtEdit.Left = 1 + (maxWidth - (int)textSize.Width) / 2;
}
}
}
/// <summary>
/// Brings the specified <c>ListBarItem</c> into view if it is not already
/// visible. The <c>ListBarItem</c> must be in the selected group.
/// <seealso cref="ListBarItem"/>
/// <seealso cref="ListBar.SelectedGroup"/>
/// </summary>
/// <param name="item">Item to bring into view.</param>
protected internal void EnsureItemVisible(ListBarItem item)
{
// Get rectangle of item relative to control:
ListBarGroup selectedGroup = SelectedGroup;
// Check whether item is part of the selected
// group:
if (selectedGroup.Items.Contains(item))
{
Rectangle rcVisible = new Rectangle(
selectedGroup.ButtonLocation,
new Size(this.ClientRectangle.Width, 0)
);
ListBarGroup nextGroup = null;
for (int i = this.groups.IndexOf(selectedGroup) + 1; i <
this.groups.Count; i++)
{
if (this.groups[i].Visible)
{
nextGroup = this.groups[i];
break;
}
}
if (nextGroup == null)
{
rcVisible.Height = this.ClientRectangle.Height -
(selectedGroup.ButtonLocation.Y + selectedGroup.ButtonHeight);
}
else
{
rcVisible.Height = nextGroup.ButtonLocation.Y - rcVisible.Top;
}
bool invisible = true;
bool notFirstTime = false;
while (invisible)
{
Rectangle rcItem = new Rectangle(item.Location,
new Size(this.ClientRectangle.Width, item.Height));
rcItem.Offset(0,
selectedGroup.ButtonLocation.Y + selectedGroup.ButtonHeight +
selectedGroup.ScrollOffset);
// Check if the item is too low:
if (rcItem.Bottom > rcVisible.Bottom )
{
// need to scroll down until it can be seen:
Scroll(-1, notFirstTime);
}
else if (rcItem.Top < rcVisible.Top)
{
// need to scroll up until it can be seen:
Scroll(1, notFirstTime);
}
else
{
invisible = false;
}
notFirstTime = true;
}
}
}
/// <summary>
/// Checks if there is an object which interacts with
/// the mouse in the control under the specified point.
/// </summary>
/// <param name="pt">The point to test.</param>
/// <returns>If there is a mouse object under the point
/// then its IMouseObject interface, otherwise null.</returns>
private IMouseObject HitTest(Point pt)
{
return HitTest(pt, false);
}
/// <summary>
/// Checks if there is an object which interacts with
/// the mouse in the control under the specified point.
/// </summary>
/// <param name="pt">The point to test.</param>
/// <returns>If there is a mouse object under the point
/// then its IMouseObject interface, otherwise null.</returns>
/// <param name="forDragDrop">Whether the hit testing is
/// being performed for a drag-drop operation or not. During
/// drag-drop, the hittest rectangle is relaxed so it includes
/// the entire rectangle and not just the icon and text.
/// </param>
private IMouseObject HitTest(Point pt, bool forDragDrop)
{
// Default return value:
IMouseObject mouseObject = null;
ListBarGroup selectedGroup = SelectedGroup;
// Over a scroll button?
if (btnUp.HitTest(pt))
{
// over the scroll up button:
mouseObject = btnUp;
}
else if (btnDown.HitTest(pt))
{
// over the scroll down button:
mouseObject = btnDown;
}
else
{
if ((forDragDrop) && (selectedGroup != null))
{
// we test for any point with 6 pixels of
// the scroll bars if the scroll buttons are on:
if (btnUp.Visible)
{
Rectangle scrollTest = new Rectangle(
selectedGroup.ButtonLocation.X,
selectedGroup.ButtonLocation.Y +
selectedGroup.ButtonHeight,
this.ClientRectangle.Width, 6);
if (scrollTest.Contains(pt))
{
mouseObject = btnUp;
}
}
if (btnDown.Visible)
{
ListBarGroup nextGroup = null;
for (int i = this.groups.IndexOf(selectedGroup) + 1; i <
this.groups.Count; i++)
{
if (this.groups[i].Visible)
{
nextGroup = this.groups[i];
break;
}
}
if (nextGroup != null)
{
Rectangle scrollTest = new Rectangle(
nextGroup.ButtonLocation.X, nextGroup.ButtonLocation.Y
- 6,
this.ClientRectangle.Width, 6);
if (scrollTest.Contains(pt))
{
mouseObject = btnDown;
}
}
}
}
}
// Check whether we're over any group buttons:
if (mouseObject == null)
{
foreach (ListBarGroup group in this.groups)
{
if (group.Visible)
{
Rectangle buttonRectangle = new Rectangle(
group.ButtonLocation, new Size(group.ButtonWidth,
group.ButtonHeight));
if (buttonRectangle.Contains(pt))
{
// over a group:
mouseObject = group;
break;
}
}
}
}
// Otherwise check whether we're over any list bar buttons:
if (mouseObject == null)
{
// Is there a selected ListBar Group?
if (selectedGroup != null)
{
// Check each item in this group:
foreach (ListBarItem item in selectedGroup.Items)
{
Rectangle rcTest;
if (forDragDrop)
{
// For drag drop the entire rectangle of the item
// is taken into account:
rcTest = new Rectangle(item.Location,
new Size(item.Width, item.Height));
rcTest.Offset(0, selectedGroup.ScrollOffset +
selectedGroup.ButtonLocation.Y +
selectedGroup.ButtonHeight);
if (rcTest.Contains(pt))
{
mouseObject = item;
break;
}
}
else
{
// Get the icon rectangle of the item within the group:
rcTest = item.IconRectangle;
// Check if the point is there:
if (rcTest.Contains(pt))
{
// We're over an item:
mouseObject = item;
break;
}
// Otherwise try the text rectangle:
rcTest = item.TextRectangle;
if (rcTest.Contains(pt))
{
// We're over an item:
mouseObject = item;
break;
}
}
}
}
}
// Return the object the mouse is over if any
return mouseObject;
}
/// <summary>
/// Internal notification from a ListBarGroup that it has
/// been changed.
/// </summary>
/// <param name="group">The ListBarGroup which has been
/// changed, or null the group has been removed.</param>
/// <param name="addRemove">Whether the effect of the
/// change will require the control to re-measured.</param>
protected internal void GroupChanged(ListBarGroup group, bool addRemove)
{
// if we have changed the number of groups,
// we need to redraw the entire control,
// otherwise we just redraw this group
if (addRemove)
{
if (group != null)
{
group.SetButtonHeight(this.Font);
}
DoResize();
PostResizeBarChanged();
}
Invalidate();
}
/// <summary>
/// Internal notification from a ListBarItem that it has been
/// changed.
/// </summary>
/// <param name="item">The ListBarItem which has been changed,
/// or null if the item has been removed.</param>
/// <param name="addRemove">Whether the effect of the control
/// will require the bar's contents to be remeasured.</param>
protected internal void ItemChanged(ListBarItem item, bool addRemove)
{
ListBarGroup selGroup = SelectedGroup;
ListBarGroup owningGroup = null;
if (item != null)
{
// Which bar does it belong to
foreach (ListBarGroup group in this.groups)
{
if (group.Items.Contains(item))
{
owningGroup = group;
break;
}
}
if (owningGroup != null)
{
Size imageSize = new Size(32, 32);
if ((owningGroup.View == ListBarGroupView.LargeIcons) ||
(owningGroup.View == ListBarGroupView.LargeIconsOnly))
{
if (this.largeImageList != null)
{
imageSize = this.largeImageList.ImageSize;
}
}
else
{
if (smallImageList != null)
{
imageSize = this.smallImageList.ImageSize;
}
else
{
imageSize = new Size(16, 16);
}
}
// Tell the item to size itself
item.SetSize(owningGroup.View, base.Font, imageSize);
}
else
{
selGroup.SetLocationAndWidth(selGroup.ButtonLocation,
selGroup.ButtonWidth);
}
}
if (selGroup != null)
{
if (item == null)
{
// need to assume it does
if (addRemove)
{
DoResize();
PostResizeBarChanged();
}
Invalidate();
}
else
{
if (selGroup.Items.Contains(item))
{
// yes it does. We need to modify the
// display:
if (addRemove)
{
DoResize();
PostResizeBarChanged();
}
Invalidate();
}
else
{
if (owningGroup == null)
{
if (addRemove)
{
DoResize();
PostResizeBarChanged();
}
Invalidate();
}
}
}
}
}
/// <summary>
/// Ensures the scroll bar isn't irrelevantly
/// begin displayed.
/// </summary>
private void PostResizeBarChanged()
{
// if the selected bar is scrolled,then we need
// to check in the new arrangement that there isn't
// an unused space below the last item in the bar.
// if there is we should check if it is possible
// to scroll up by one or more items whilst still
// ensuring the last item currently visible in the
// view does not become any less visible.
ListBarGroup selectedGroup = SelectedGroup;
if (selectedGroup != null)
{
if (selectedGroup.Items.Count > 0)
{
if (selectedGroup.ScrollOffset != 0)
{
bool finished = false;
ListBarGroup nextGroup = null;
for (int i = this.groups.IndexOf(selectedGroup) + 1; i <
this.groups.Count; i++)
{
if (this.groups[i].Visible)
{
nextGroup = this.groups[i];
break;
}
}
while (!finished)
{
ListBarItem lastItem =
selectedGroup.Items[selectedGroup.Items.Count - 1];
Rectangle rcItemLast = new Rectangle(lastItem.Location,
new Size(this.ClientRectangle.Width, lastItem.Height));
rcItemLast.Offset(0, selectedGroup.ScrollOffset +
selectedGroup.ButtonLocation.Y +
selectedGroup.ButtonHeight );
Rectangle rcView = new Rectangle(
selectedGroup.ButtonLocation.X,
selectedGroup.ButtonLocation.Y +
selectedGroup.ButtonHeight,
this.ClientRectangle.Width, 0);
if (nextGroup == null)
{
rcView.Height = this.ClientRectangle.Height -
rcView.Top;
}
else
{
rcView.Height = nextGroup.ButtonLocation.Y - rcView.Top;
}
if (rcItemLast.Bottom < rcView.Bottom + rcItemLast.Height)
{
// we can scroll up:
Scroll(1);
}
else
{
finished = true;
}
if (selectedGroup.ScrollOffset == 0)
{
finished = true;
}
}
}
}
}
}
private void InternalDragOverProcess(DragEventArgs e, bool assumeItem)
{
// TODO: this method requires refactoring
ListBarItem itemBefore = null;
ListBarItem itemAfter = null;
bool overEmptyBar = false;
IMouseObject newDragHoverOver = null;
bool overGroup = false;
// see if the drag drop data contains a ListBarItem:
if ((GetTypeOrSubClassFromData(e, typeof(ListBarItem)) != null)
|| (assumeItem && (GetTypeOrSubClassFromData(e,
typeof(ListBarGroup)) == null)))
{
// check if we're over an item:
Point pt = new Point(e.X, e.Y);
pt = this.PointToClient(pt);
IMouseObject obj = HitTest(pt, true);
newDragHoverOver = obj;
if (obj != null)
{
// Do scrolling checks on this bar. Scrolling
// is rate limited to once per 250ms to assist
// with usability
System.TimeSpan diff = DateTime.Now.Subtract(lastScrollTime);
if (diff.Milliseconds > 250)
{
// Firstly, check if we're over an actual scroll button:
if
(typeof(ListBarScrollButton).IsAssignableFrom(obj.GetType()))
{
// scroll in the appropriate direction:
ListBarScrollButton scrollButton =
(ListBarScrollButton)obj;
Scroll(scrollButton, true);
lastScrollTime = DateTime.Now;
}
else
{
// Otherwise, we may be within the boundary of the
// scroll buttons:
if (btnUp.Visible)
{
Rectangle rcBtnUp = btnUp.Rectangle;
rcBtnUp.X = 0;
rcBtnUp.Width = this.ClientRectangle.Width;
if (rcBtnUp.Contains(pt))
{
Scroll(1,true);
lastScrollTime = DateTime.Now;
}
}
if (btnDown.Visible)
{
Rectangle rcBtnDown = btnDown.Rectangle;
rcBtnDown.X = 0;
rcBtnDown.Width = this.ClientRectangle.Width;
if (rcBtnDown.Contains(pt))
{
Scroll(-1,true);
lastScrollTime = DateTime.Now;
}
}
}
}
// Now check for being over an item or an empty bar:
if (typeof(ListBarItem).IsAssignableFrom(obj.GetType()))
{
ListBarItem item = (ListBarItem)obj;
object objDragItem = GetTypeOrSubClassFromData(e,
typeof(ListBarItem));
bool itemEqualsDragItem = false;
ListBarItem dragItem = null;
if (objDragItem != null)
{
dragItem = (ListBarItem)objDragItem;
itemEqualsDragItem = item.Equals(dragItem);
}
if (!itemEqualsDragItem)
{
// we're over an item.
// Get the rectangle relative to the bar:
Rectangle rc = new Rectangle(item.Location, new
Size(item.Width, item.Height));
// Adjust the rectangle so it's relative to the control:
ListBarGroup selectedGroup = SelectedGroup;
rc.Offset(0, selectedGroup.ButtonLocation.Y +
selectedGroup.ButtonHeight + selectedGroup.ScrollOffset);
// The commented section here is an 8 pixel
// margin from the top or bottom of an item,
// as per the Outlook bar. I found the control
// more usable with a ListView style drag-drop
// approach, where the before/after decision
// is made depending on where you are relative
// to the centre of the item.
/*
** BEGIN: Outlook style drag-drop logic
if (((pt.Y - rc.Top) > -8) && ((pt.Y - rc.Top) < 8))
{
itemBefore = item;
// we can't go before the item which follows
// the one we're dragging:
if ((selectedGroup.Items.IndexOf(itemBefore) - 1) ==
selectedGroup.Items.IndexOf(dragItem))
{
itemBefore = null;
}
}
if (((rc.Bottom - pt.Y) > -8) && ((rc.Bottom - pt.Y) < 16))
{
itemAfter = item;
// we can't go after the item which is before
// the one we're dragging:
if ((selectedGroup.Items.IndexOf(itemAfter) + 1) ==
selectedGroup.Items.IndexOf(dragItem))
{
itemAfter = null;
}
}
** END: Outlook style drag drop logic
*/
// ListView style drag insert point logic.
if ((selectedGroup.View ==
ListBarGroupView.SmallIconsOnly) || (selectedGroup.View
== ListBarGroupView.LargeIconsOnly))
{
int distRight = Math.Abs(rc.Right - pt.X);
int distLeft = Math.Abs(pt.X - rc.Left);
if (distRight < distLeft)
{
itemAfter = item;
}
else
{
itemBefore = item;
}
}
else
{
int distBottom = Math.Abs(rc.Bottom - pt.Y);
int distTop = Math.Abs(pt.Y - rc.Top);
if (distBottom < distTop)
{
itemAfter = item;
}
else
{
itemBefore = item;
}
}
if (itemAfter != null)
{
// we can't go after the item which is before
// the one we're dragging:
/*
if ((selectedGroup.Items.IndexOf(itemAfter) + 1) ==
selectedGroup.Items.IndexOf(dragItem))
{
itemAfter = null;
}
*/
if (itemAfter != null)
{
// check there isn't an appropriate item before:
if (selectedGroup.Items.IndexOf(itemAfter) <
selectedGroup.Items.Count - 1)
{
itemBefore =
selectedGroup.Items[selectedGroup.Items.IndexOf(i
temAfter) + 1];
}
}
}
else if (itemBefore != null)
{
// we can't go before the item which follows
// the one we're dragging:
/*
if ((selectedGroup.Items.IndexOf(itemBefore) - 1) ==
selectedGroup.Items.IndexOf(dragItem))
{
itemBefore = null;
}
*/
if (itemBefore != null)
{
// check there isn't an appropriate item after:
if (selectedGroup.Items.IndexOf(itemBefore) > 0)
{
itemAfter =
selectedGroup.Items[selectedGroup.Items.IndexOf(i
temBefore) - 1];
}
}
}
}
}
else if (typeof(ListBarGroup).IsAssignableFrom(obj.GetType()))
{
overGroup = true;
// over a group
if (dragHoverOver != null)
{
if (dragHoverOver.Equals(obj))
{
System.TimeSpan overTime =
DateTime.Now.Subtract(dragHoverOverStartTime);
if (overTime.Milliseconds > 350)
{
// we should select this group:
dragHoverOver = null;
SelectGroup((ListBarGroup)obj);
// Prevent the control from scrolling for a little
bit.
// TODO
// Actually what we really want to do here is to say
// that unless the mouse moves > 4 pixels from this
// spot scrolling will not occur in the new bar
lastScrollTime = DateTime.Now.AddMilliseconds(500);
}
}
}
}
}
else
{
// we may be over the bar section:
ListBarGroup selectedGroup = SelectedGroup;
if (selectedGroup != null)
{
if (selectedGroup.Items.Count > 0)
{
// we're not over an item. Check if we're
// within the bar:
if (obj == null)
{
// Check if the selected group is the last group in
// the control:
ListBarGroup nextGroup = null;
for (int i = this.groups.IndexOf(selectedGroup) + 1; i
< this.groups.Count; i++)
{
if (this.groups[i].Visible)
{
nextGroup = this.groups[i];
break;
}
}
if (nextGroup == null)
{
// If so the bar area extends from the bottom
// of the button rectangle to the bottom of the
// control:
if ((pt.Y > (selectedGroup.ButtonLocation.Y +
selectedGroup.ButtonHeight)) &&
(pt.Y < this.ClientRectangle.Height))
{
itemAfter =
selectedGroup.Items[selectedGroup.Items.Count -
1];
}
}
else
{
// Otherwise the bar area extends from the bottom
// of the button rectangle of this control to the
// top of the button rectangle of the next control:
if ((pt.Y > (selectedGroup.ButtonLocation.Y +
selectedGroup.ButtonHeight)) &&
(pt.Y < nextGroup.ButtonLocation.Y))
{
itemAfter =
selectedGroup.Items[selectedGroup.Items.Count -
1];
}
}
}
}
else
{
overEmptyBar = true;
}
}
}
}
else if (GetTypeOrSubClassFromData(e, typeof(ListBarGroup)) != null)
{
itemAfter = null;
itemBefore = null;
// Here we check if we should drag the list bar
// into a new position
// check if we're over an item:
Point pt = new Point(e.X, e.Y);
pt = this.PointToClient(pt);
IMouseObject obj = HitTest(pt, true);
newDragHoverOver = obj;
if (obj != null)
{
if (typeof(ListBarGroup).IsAssignableFrom(obj.GetType()))
{
overGroup = true;
ListBarGroup dragGroup =
(ListBarGroup)GetTypeOrSubClassFromData(e,
typeof(ListBarGroup));
ListBarGroup dragOverGroup = (ListBarGroup)obj;
if (!dragOverGroup.Equals(dragGroup))
{
bool reSelect = false;
if (dragGroup.Selected)
{
reSelect = true;
}
bool isLastGroup = true;
for (int i = groups.IndexOf(dragOverGroup) + 1; i <
this.groups.Count; i++)
{
if (this.groups[i].Visible)
{
isLastGroup = false;
}
}
this.groups.Remove(dragGroup);
if (isLastGroup)
{
this.groups.Add(dragGroup);
}
else
{
this.groups.Insert(groups.IndexOf(dragOverGroup),
dragGroup);
}
if (reSelect)
{
for (int i = 0; i < this.groups.Count; i++)
{
this.groups[i].Selected = false;
}
dragGroup.Selected = true;
}
DoResize();
Invalidate();
}
}
}
}
// Now check if we have any drag/drop insert position:
if ((itemBefore != null) || (itemAfter != null) || (overEmptyBar))
{
e.Effect = DragDropEffects.Move;
ListBarDragDropInsertPoint newInsertPoint =
new ListBarDragDropInsertPoint(itemBefore, itemAfter,
overEmptyBar);
// do we currently have an insert point?
if (dragInsertPoint != null)
{
if (dragInsertPoint.CompareTo(newInsertPoint) == 0)
{
// we have nothing to do
newInsertPoint = null;
}
}
if (newInsertPoint != null)
{
Trace.WriteLine("Drag Insert Point has changed");
dragInsertPoint = newInsertPoint;
Invalidate();
}
}
else
{
if (overGroup)
{
e.Effect = DragDropEffects.Move;
}
else
{
e.Effect = DragDropEffects.None;
}
// Clear the drag insert point if it's set:
if (dragInsertPoint != null)
{
dragInsertPoint = null;
// redraw the control:
Invalidate();
}
}
if ((newDragHoverOver != null) && (dragHoverOver != null))
{
if (!newDragHoverOver.Equals(dragHoverOver))
{
dragHoverOver = newDragHoverOver;
dragHoverOverStartTime = DateTime.Now;
}
// else we keep the drag hover over time.
}
else
{
dragHoverOver = newDragHoverOver;
dragHoverOverStartTime = DateTime.Now;
}
}
#endregion
#region API
/// <summary>
/// Called by the control's internal sizing mechanism.
/// Returns the size of a <see cref="ListBarGroup"/> button
/// rectangle.
/// </summary>
/// <param name="group">The <see cref="ListBarGroup"/> to get the
/// button width for.</param>
/// <returns>The width of the button.</returns>
protected virtual int GetGroupButtonWidth(ListBarGroup group)
{
return this.ClientRectangle.Width - 2;
}
/// <summary>
/// Gets/sets the default <see cref="System.Drawing.Font"/> used to
/// render text in the control.
/// </summary>
[Description("Gets/sets the Font used to render the text in this
control.")]
public override System.Drawing.Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
// Force all of the ListBar items to be measured
foreach (ListBarGroup group in groups)
{
GroupChanged(group, true);
}
}
}
/// <summary>
/// Gets/sets whether items are selected on MouseDown,
/// rather than on MouseUp, which is the default.
/// </summary>
[Description("Gets/sets whether items are selected on MouseDown, rather
than on MouseUp.")]
public bool SelectOnMouseDown
{
get
{
return this.selectOnMouseDown;
}
set
{
this.selectOnMouseDown = value;
}
}
/// <summary>
/// Gets/sets whether items will be dragged
/// in the control automatically. The default
/// is <c>True</c>.
/// </summary>
[Description("Gets/sets whether items will be dragged in the control
automatically. The default is True.")]
public bool AllowDragItems
{
get
{
return this.allowDragItems;
}
set
{
this.allowDragItems = value;
}
}
/// <summary>
/// Gets/sets whether groups will be dragged
/// in the control automatically. The default
/// is <c>True</c>. (Note in MS Outlook
/// Groups cannot be reordered by dragging, but
/// they can in VS.NET).
/// </summary>
[Description("Gets/sets whether groups can be dragged automatically in
the control. The default is True.")]
public bool AllowDragGroups
{
get
{
return this.allowDragGroups;
}
set
{
this.allowDragGroups = value;
}
}
/// <summary>
/// Sets the groups object associated with this control
/// to a new group collection.
/// </summary>
/// <param name="groups">The <see cref="ListBarGroupCollection"/> object
holding
/// the new collection of groups to associate with this control.</param>
[Description("Sets the internal collection holding the Groups associated
with this control to a new object.")]
public void SetGroups(ListBarGroupCollection groups)
{
this.BeginUpdate();
groups.SetOwner(this);
this.EndUpdate();
this.groups = groups;
DoResize();
this.Invalidate();
}
/// <summary>
/// Gets the item which is currently being edited, if any,
/// otherwise returns null.
/// </summary>
[Description("Gets the item which is currently being edited, if any,
otherwise returns null.")]
public ListBarItem EditItem
{
get
{
return this.editItem;
}
}
/// <summary>
/// Prevents the control from drawing until the
/// <see cref="EndUpdate"/> method is called.
/// </summary>
[Description("Prevents the control from drawing until the EndUpdate
method is called.")]
public void BeginUpdate()
{
this.redraw = false;
}
/// <summary>
/// Resumes drawing of the control after drawing was suspended by the
/// <see cref="BeginUpdate"/> method.
/// </summary>
[Description("Resumes drawing of the control after drawing was suspended
by the BeginUpdate method.")]
public void EndUpdate()
{
this.redraw = true;
DoResize();
Invalidate();
}
/// <summary>
/// Renders the control when a new group is being scrolled
/// into view.
/// </summary>
/// <param name="pe">The arguments associated with the paint
/// event.</param>
[Description("Renders the control as a new group is being scrolled into
view")]
protected virtual void RenderScrollNewGroup(PaintEventArgs pe)
{
int lastBar = 0;
int currentNext = this.groups.Count -1;
int newNext = this.groups.Count - 1;
for (int i = 0; i < this.groups.Count; i++)
{
if (this.groups[i].Visible)
{
if (i > lastBar)
{
lastBar = i;
}
if ((i > this.indexCurrent) && (i < currentNext))
{
currentNext = i;
}
if ((i > this.indexNew) && ( i < newNext))
{
newNext = i;
}
}
}
ListBarGroup currentBar = this.groups[this.indexCurrent];
ListBarGroup newBar = this.groups[this.indexNew];
// get the rectangle for currentBar:
Rectangle currentBarBounds = new Rectangle(
currentBar.ButtonLocation,
new Size(currentBar.ButtonWidth, 0));
// the height of the current bar rect is the height of the control
// or the top of the rectangle of the next button along:
if (this.indexCurrent == lastBar)
{
currentBarBounds.Height = this.ClientRectangle.Height -
currentBarBounds.Top;
}
else
{
currentBarBounds.Height = this.groups[currentNext].ButtonLocation.Y
-
currentBarBounds.Top;
}
// get the rectangle for newBar:
Rectangle newBarBounds = new Rectangle(
newBar.ButtonLocation,
new Size(newBar.ButtonWidth, 0));
// the height of the new bar is the height of the control or
// the top of the rectangle of the next bar along:
if (this.indexNew == lastBar)
{
newBarBounds.Height = this.ClientRectangle.Height -
newBarBounds.Top;
}
else
{
newBarBounds.Height = this.groups[newNext].ButtonLocation.Y -
newBarBounds.Top;
}
// Draw the current bar contents:
currentBar.DrawBar(
pe.Graphics, currentBarBounds,
(currentBar.View == ListBarGroupView.LargeIcons ? largeImageList :
smallImageList),
this.Font,
this.drawStyle,
this.Enabled);
// Draw the new bar contents:
newBar.DrawBar(
pe.Graphics, newBarBounds,
(newBar.View == ListBarGroupView.LargeIcons ? largeImageList :
smallImageList),
this.Font,
this.drawStyle,
this.Enabled);
// Draw the buttons:
foreach (ListBarGroup group in this.groups)
{
group.DrawButton(pe.Graphics, this.Font, this.Enabled);
}
// Draw the border:
RenderControlBorder(pe.Graphics);
}
/// <summary>
/// Renders the control given the object passed to a Paint event.
/// </summary>
/// <param name="pe">The arguments associated with the paint
/// event.</param>
protected virtual void Render(PaintEventArgs pe)
{
if (this.redraw)
{
// background - does not need to be painted
// as the control does it itself
// draw the control elements:
if (groups.Count > 0)
{
// First draw the items in the selected group,
// if any:
ListBarGroup selectedGroup = SelectedGroup;
if ((selectedGroup != null) && (selectedGroup.Visible))
{
// Draw the items in the group:
selectedGroup.DrawBar(pe.Graphics, rcListView,
((selectedGroup.View == ListBarGroupView.LargeIcons ||
selectedGroup.View == ListBarGroupView.LargeIconsOnly)
?
largeImageList : smallImageList),
this.Font,
this.drawStyle,
this.Enabled);
// Render the drag-drop insertion point, if any:
RenderDragInsertPoint(pe.Graphics,
selectedGroup);
}
// draw the scroll buttons:
Color defaultBackColor =
Color.FromKnownColor(KnownColor.Control);
if (this.drawStyle == ListBarDrawStyle.ListBarDrawStyleNormal)
{
if
(!this.BackColor.Equals(Color.FromKnownColor(KnownColor.Contr
olDark)))
{
defaultBackColor = this.BackColor;
}
}
else if (this.DrawStyle ==
ListBarDrawStyle.ListBarDrawStyleOfficeXP)
{
defaultBackColor = this.BackColor;
}
btnUp.DrawItem(pe.Graphics, defaultBackColor, this.Enabled );
btnDown.DrawItem(pe.Graphics, defaultBackColor, this.Enabled);
// Now draw the group buttons, if any:
foreach (ListBarGroup group in this.groups)
{
if (group.Visible)
{
group.DrawButton(pe.Graphics, this.Font, this.Enabled);
}
}
}
// border:
RenderControlBorder(pe.Graphics);
}
}
/// <summary>
/// Draw a border around the control. The default
/// implementation draws a 1 pixel inset border.
/// </summary>
/// <param name="gfx">The graphics object to drawn onto.</param>
protected virtual void RenderControlBorder(
Graphics gfx)
{
// draw the control's border
Pen darkPen = new Pen(CustomBorderColor.ColorDark(this.BackColor));
Pen lightPen = new
Pen(CustomBorderColor.ColorLightLight(this.BackColor));
gfx.DrawLine(darkPen,
this.ClientRectangle.Left, this.ClientRectangle.Bottom -2,
this.ClientRectangle.Left, this.ClientRectangle.Top);
gfx.DrawLine(darkPen,
this.ClientRectangle.Left, this.ClientRectangle.Top,
this.ClientRectangle.Right -2, this.ClientRectangle.Top);
gfx.DrawLine(lightPen,
this.ClientRectangle.Right - 1, this.ClientRectangle.Top,
this.ClientRectangle.Right -1, this.ClientRectangle.Bottom -1);
gfx.DrawLine(lightPen,
this.ClientRectangle.Right - 1, this.ClientRectangle.Bottom - 1,
this.ClientRectangle.Left, this.ClientRectangle.Bottom -1);
darkPen.Dispose();
lightPen.Dispose();
}
/// <s |