using DevComponents.DotNetBar.Primitives; using DevComponents.DotNetBar.Rendering; using DevComponents.DotNetBar.TextMarkup; using DevComponents.Editors; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.Drawing.Drawing2D; using System.Text; using System.Windows.Forms; namespace DevComponents.DotNetBar.Controls { [ToolboxItem(true), ToolboxBitmap(typeof(TokenEditor), "Controls.TokenEditor.ico")] [Designer("DevComponents.DotNetBar.Design.TokenEditorDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf")] [DefaultEvent("SelectedTokensChanged")] public class TokenEditor : Control, IMessageHandlerClient { #region Constructor private TextBoxX _EditBox; private VScrollBarAdv _VScrollBar = null; /// /// Initializes a new instance of the TokenEditor class. /// public TokenEditor() { _SelectedTokens = new CustomCollection(); _SelectedTokens.CollectionChanged += SelectedTokensCollectionChanged; _Tokens = new CustomCollection(); _Tokens.CollectionChanged += TokensCollectionChanged; this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque | ControlStyles.ResizeRedraw | ControlStyles.Selectable | ControlStyles.StandardDoubleClick | DisplayHelp.DoubleBufferFlag, true); _BackgroundStyle = new ElementStyle(); _BackgroundStyle.Class = ElementStyleClassKeys.DateTimeInputBackgroundKey; _BackgroundStyle.StyleChanged += new EventHandler(this.VisualPropertyChanged); _EditBox = new TextBoxX(); _EditBox.BorderStyle = BorderStyle.None; _EditBox.Visible = false; _EditBox.TextChanged += EditBoxTextChanged; _EditBox.KeyDown += EditBoxKeyDown; _EditBox.GotFocus += EditBoxGotFocus; this.Controls.Add(_EditBox); _EditBox.SetAutoHeight(); _VScrollBar = new VScrollBarAdv(); _VScrollBar.Visible = false; _VScrollBar.Scroll += VScrollBarScroll; this.Controls.Add(_VScrollBar); VisualGroup group = new VisualGroup(); group.HorizontalItemSpacing = 0; group.ArrangeInvalid += ButtonGroupArrangeInvalid; group.RenderInvalid += ButtonGroupRenderInvalid; group.ResetMouseHover += ButtonGroupResetMouseHover; _ButtonGroup = group; CreateButtons(); } protected override void Dispose(bool disposing) { if (_MessageHandlerInstalled) { MessageHandler.UnregisterMessageClient(this); _MessageHandlerInstalled = false; } base.Dispose(disposing); } #endregion #region Implementation protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { if (Dpi.RecordScalePerControl) Dpi.SetScaling(factor); base.ScaleControl(factor, specified); _EditBox.SetAutoHeight(); LayoutTokens(); } private void EditBoxGotFocus(object sender, EventArgs e) { FocusToken = null; } private TokenEditorColorTable GetTokenColorTable() { return ((Office2007Renderer)GlobalManager.Renderer).ColorTable.TokenEditor; } protected override void OnPaint(PaintEventArgs e) { if ((this.BackColor.IsEmpty || this.BackColor == Color.Transparent)) { base.OnPaintBackground(e); } if (_BackgroundStyle != null) _BackgroundStyle.SetColorScheme(this.GetColorScheme()); TokenEditorColorTable colorTable = GetTokenColorTable(); PaintBackground(e); Rectangle clientBounds = GetClientBounds(); e.Graphics.SetClip(clientBounds); if (this.WatermarkEnabled && this.WatermarkText.Length > 0 && this.IsWatermarkRendered) { DrawWatermark(e.Graphics); } foreach (EditToken token in _SelectedTokens) { PaintToken(token, e, colorTable); } PaintButtons(e.Graphics); e.Graphics.ResetClip(); base.OnPaint(e); } private void PaintToken(EditToken token, PaintEventArgs e, TokenEditorColorTable colorTable) { Graphics g = e.Graphics; Rectangle r = token.Bounds; r.Offset(_AutoScrollPosition); SmoothingMode sm = g.SmoothingMode; g.SmoothingMode = SmoothingMode.HighQuality; Color textColor = colorTable.Normal.TextColor; LinearGradientColorTable background = colorTable.Normal.Background; if (token.IsFocused) { textColor = colorTable.Focused.TextColor; background = colorTable.Focused.Background; } else if (token.MouseOverPart == eTokenPart.Token || token.MouseOverPart == eTokenPart.Image) background = colorTable.MouseOver.Background; DisplayHelp.FillRoundedRectangle(g, r, 2, background.Start, background.End, background.GradientAngle); g.SmoothingMode = sm; string text = GetTokenText(token); Rectangle rText = r; if (EffectiveRemoveTokenButtonVisible) { Rectangle removeBounds = new Rectangle(r.X, r.Y + (r.Height - _RemoveTokenButtonSize.Height) / 2, _RemoveTokenButtonSize.Width, _RemoveTokenButtonSize.Height); Color closeTextColor = textColor; if (token.MouseOverPart == eTokenPart.RemoveButton) { Office2007ColorTable ct = ((Office2007Renderer)GlobalManager.Renderer).ColorTable; Office2007ButtonItemColorTable buttonColorTable = ct.ButtonItemColors[Enum.GetName(typeof(eButtonColor), eButtonColor.OrangeWithBackground)]; Office2007ButtonItemPainter.PaintBackground(g, buttonColorTable.MouseOver, removeBounds, RoundRectangleShapeDescriptor.RectangleShape); closeTextColor = buttonColorTable.MouseOver.Text; //g.FillRectangle(Brushes.Red, removeBounds); } float symbolSize = Math.Max(1, Math.Min(removeBounds.Width, removeBounds.Height) * 72 / g.DpiX - 1.5f); TextDrawing.DrawStringLegacy(g, "\uf00d", Symbols.GetFont(symbolSize, eSymbolSet.Awesome), closeTextColor, removeBounds, eTextFormat.HorizontalCenter | eTextFormat.VerticalCenter); token.RemoveButtonBounds = removeBounds; rText.X = removeBounds.Right + _TokenPartSpacing; rText.Width -= rText.Right - r.Right; } if (!string.IsNullOrEmpty(token.SymbolRealized)) { Rectangle imageBounds = new Rectangle(rText.X, r.Y, r.Height, r.Height); float symbolSize = Math.Max(1, Math.Min(imageBounds.Width, imageBounds.Height) * 72 / g.DpiX - 1.5f); TextDrawing.DrawStringLegacy(g, token.SymbolRealized, Symbols.GetFont(symbolSize, token.SymbolSet), (token.SymbolColor.IsEmpty ? textColor : token.SymbolColor), imageBounds, eTextFormat.HorizontalCenter | eTextFormat.VerticalCenter); token.ImageBounds = imageBounds; rText.X = imageBounds.Right + _TokenPartSpacing; rText.Width -= rText.Right - r.Right; } else if (token.Image != null) { Rectangle imageBounds = new Rectangle(rText.X, r.Y, token.ImageBounds.Width, token.Image.Height); g.DrawImage(token.Image, imageBounds); token.ImageBounds = imageBounds; rText.X = imageBounds.Right + _TokenPartSpacing; rText.Width -= rText.Right - r.Right; } TextDrawing.DrawString(g, text, this.Font, textColor, rText, eTextFormat.Default | eTextFormat.VerticalCenter); } protected virtual void PaintBackground(PaintEventArgs e) { Graphics g = e.Graphics; Rectangle r = this.ClientRectangle; ElementStyle style = _BackgroundStyle; if (!this.BackColor.IsEmpty && this.BackColor != Color.Transparent) { DisplayHelp.FillRectangle(g, r, this.BackColor); } if (this.BackgroundImage != null) base.OnPaintBackground(e); if (style.Custom) { SmoothingMode sm = g.SmoothingMode; //if (m_AntiAlias) // g.SmoothingMode = SmoothingMode.HighQuality; ElementStyleDisplayInfo displayInfo = new ElementStyleDisplayInfo(style, e.Graphics, r); ElementStyleDisplay.Paint(displayInfo); //if (m_AntiAlias) // g.SmoothingMode = sm; } } protected override void OnResize(EventArgs e) { LayoutTokens(); base.OnResize(e); } /// /// Gets or sets the location of the auto-scroll position. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false), Description("Indicates location of the auto-scroll position.")] public Point AutoScrollPosition { get { return _AutoScrollPosition; } set { if (_AutoScrollPosition == value) return; _AutoScrollPosition = value; if (_VScrollBar != null && _VScrollBar.Value != -_AutoScrollPosition.Y) _VScrollBar.Value = Math.Min(_VScrollBar.Maximum, Math.Max(_VScrollBar.Minimum, -_AutoScrollPosition.Y)); RepositionEditTextBox(); Invalidate(); } } private Rectangle GetClientBounds() { bool disposeStyle = false; ElementStyle style = ElementStyleDisplay.GetElementStyle(_BackgroundStyle, out disposeStyle); Rectangle clientRect = this.ClientRectangle; clientRect.X += style.PaddingLeft; clientRect.Width -= style.PaddingHorizontal; clientRect.Y += style.PaddingTop; clientRect.Height -= style.PaddingVertical; if (disposeStyle) style.Dispose(); style = null; return clientRect; } private Point _AutoScrollPosition = Point.Empty; private int _TokenSpacing = 2; private int _TokenPartSpacing = 2; // Spacing between different parts of the token, like between image and text and image and close button etc. Its spacing between each part. private readonly Size _TokenPadding = new Size(2, 2); private void LayoutTokens() { if (!this.IsHandleCreated || this.IsDisposed || this.Width == 0 || this.Height == 0) return; int lineCount = 0; Rectangle clientRect = GetClientBounds(); Graphics g = this.CreateGraphics(); Size totalSize = LayoutTokens(g, clientRect, out lineCount); if (_AutoSizeHeight) { int heightLines = Math.Min(_MaxHeightLines, lineCount); int newHeight = (int)(Math.Ceiling((double)totalSize.Height / lineCount) * heightLines) + (this.Height - clientRect.Height); if (_MaxHeightLines == 1) newHeight = GetSingleLineTextBoxHeight(); if (newHeight != this.Height) { g.Dispose(); this.Height = newHeight; return; } } if (totalSize.Height > clientRect.Height) { Rectangle reducedBounds = clientRect; int visibleLines = (int)(clientRect.Height / (Math.Ceiling((double)totalSize.Height / lineCount))); if (_DropDownButtonVisible && visibleLines <= 2 || visibleLines == 1) { // Using scroll button due to space limitation _VScrollBar.Visible = false; _ScrollButton.Visible = true; reducedBounds.Width -= SystemInformation.VerticalScrollBarWidth * (_DropDownButtonVisible ? 2 : 1); if (visibleLines > 1) _ButtonGroup.Orientation = eOrientation.Vertical; else _ButtonGroup.Orientation = eOrientation.Horizontal; } else { int buttonOffset = (_DropDownButtonVisible ? GetButtonHeight() + 1 : 0); // Activate Scroll-bar _VScrollBar.Bounds = new Rectangle(clientRect.Right - SystemInformation.VerticalScrollBarWidth, clientRect.Y + buttonOffset, SystemInformation.VerticalScrollBarWidth, clientRect.Height - buttonOffset); _VScrollBar.Visible = true; _ScrollButton.Visible = false; reducedBounds.Width -= _VScrollBar.Width; } totalSize = LayoutTokens(g, reducedBounds, out lineCount); _VScrollBar.Maximum = totalSize.Height; _VScrollBar.LargeChange = clientRect.Height; if (this.SelectedTokens.Count > 0) _VScrollBar.SmallChange = this.SelectedTokens[0].Bounds.Height; else _VScrollBar.SmallChange = 17; } else { _VScrollBar.Visible = false; _AutoScrollPosition = Point.Empty; _ScrollButton.Visible = false; } g.Dispose(); RepositionEditTextBox(); EnsureTextBoxScrollPosition(); UpdateButtons(); this.Invalidate(); } private int GetSingleLineTextBoxHeight() { Font font = this.Font; return font.Height + ((SystemInformation.BorderSize.Height * 4) + 4); } private Size LayoutTokens(Graphics g, Rectangle clientRect, out int lineCount) { lineCount = 0; if (!this.IsHandleCreated || this.IsDisposed) return Size.Empty; clientRect.Inflate(-1, -1); Font font = this.Font; Size totalSize = Size.Empty; int singleLineTextBoxHeight = GetSingleLineTextBoxHeight() - 2; // 2 is for top and bottom border if (_SelectedTokens.Count == 0) { lineCount = 1; totalSize = new Size(clientRect.Width, singleLineTextBoxHeight); _EditBoxLocation = new Point(clientRect.X, clientRect.Y + (totalSize.Height - _EditBox.Height) / 2); _EditBox.Width = clientRect.Width - (_DropDownButtonVisible ? SystemInformation.VerticalScrollBarWidth : 0); return totalSize; } int largestTokenHeight = 0; foreach (EditToken token in _SelectedTokens) { token.RemoveButtonBounds = Rectangle.Empty; token.ImageBounds = Rectangle.Empty; string text = GetTokenText(token); Size size = TextDrawing.MeasureString(g, (string.IsNullOrEmpty(text) ? "A" : text), font); size.Width += _TokenPadding.Width * 2; size.Height += _TokenPadding.Height * 2; if (EffectiveRemoveTokenButtonVisible) { size.Width += _RemoveTokenButtonSize.Width + _TokenPartSpacing; if (_RemoveTokenButtonSize.Height > size.Height) size.Height = _RemoveTokenButtonSize.Height; } if (!string.IsNullOrEmpty(token.SymbolRealized)) { size.Width += size.Height + _TokenPartSpacing; } else if (token.Image != null) { size.Width += token.Image.Width + _TokenPartSpacing; if (token.Image.Height > size.Height) size.Height = token.Image.Height; } largestTokenHeight = Math.Max(largestTokenHeight, size.Height); token.Bounds = new Rectangle(Point.Empty, size); } lineCount = 1; totalSize.Height = largestTokenHeight; Point currentPos = clientRect.Location; foreach (EditToken token in _SelectedTokens) { Rectangle tokenBounds = new Rectangle(currentPos.X, currentPos.Y, token.Bounds.Width, largestTokenHeight); if (tokenBounds.Right > clientRect.Right && currentPos.X > clientRect.X) { currentPos.Y += largestTokenHeight + _TokenSpacing; currentPos.X = clientRect.X; tokenBounds.Y = currentPos.Y; tokenBounds.X = currentPos.X; totalSize.Height += largestTokenHeight + _TokenSpacing; lineCount++; } currentPos.X += tokenBounds.Width + _TokenSpacing; token.Bounds = tokenBounds; if (tokenBounds.Right - clientRect.X > totalSize.Width) totalSize.Width = tokenBounds.Right - clientRect.X; } if (clientRect.Right - currentPos.X < _TextBoxMinWidth) { currentPos.Y += largestTokenHeight + _TokenSpacing; currentPos.X = clientRect.X; lineCount++; totalSize.Height += largestTokenHeight + _TokenSpacing; } if (lineCount == 1) totalSize.Height = Math.Max(totalSize.Height, singleLineTextBoxHeight); _EditBoxLocation = new Point(currentPos.X, currentPos.Y + (largestTokenHeight - _EditBox.Height) / 2); _EditBox.Width = clientRect.Right - currentPos.X - (lineCount == 1 && _DropDownButtonVisible ? SystemInformation.VerticalScrollBarWidth : 0); return totalSize; } private Point _EditBoxLocation = Point.Empty; private void RepositionEditTextBox() { Point loc = _EditBoxLocation; loc.Offset(_AutoScrollPosition); _EditBox.Location = loc; } private void VScrollBarScroll(object sender, ScrollEventArgs e) { if (e.NewValue != e.OldValue) { _AutoScrollPosition.Y = -e.NewValue; RepositionEditTextBox(); this.Invalidate(); } } private const int _TextBoxMinWidth = 36; protected override void OnHandleCreated(EventArgs e) { _EditBox.SetAutoHeight(); LayoutTokens(); base.OnHandleCreated(e); } private string GetTokenText(EditToken token) { if (!string.IsNullOrEmpty(token.Text)) return token.Text; return token.Value; } /// /// Returns the color scheme used by control. Color scheme for Office2007 style will be retrieved from the current renderer instead of /// local color scheme referenced by ColorScheme property. /// /// An instance of ColorScheme object. protected virtual ColorScheme GetColorScheme() { BaseRenderer r = Rendering.GlobalManager.Renderer; if (r is Office2007Renderer) return ((Office2007Renderer)r).ColorTable.LegacyColors; return new ColorScheme(); } private ElementStyle _BackgroundStyle = null; /// /// Specifies the background style of the control. /// [Browsable(true), Category("Style"), Description("Gets or sets bar background style."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public ElementStyle BackgroundStyle { get { return _BackgroundStyle; } } /// /// Resets style to default value. Used by windows forms designer. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetBackgroundStyle() { _BackgroundStyle.StyleChanged -= new EventHandler(this.VisualPropertyChanged); _BackgroundStyle = new ElementStyle(); _BackgroundStyle.StyleChanged += new EventHandler(this.VisualPropertyChanged); this.Invalidate(); } private void VisualPropertyChanged(object sender, EventArgs e) { this.Invalidate(); } private CustomCollection _SelectedTokens; /// /// Gets the collection of the selected tokens. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public CustomCollection SelectedTokens { get { return _SelectedTokens; } } private void SelectedTokensCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { foreach (object item in e.NewItems) { ((EditToken)item).IsSelected = true; } } else if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (object item in e.OldItems) { ((EditToken)item).IsSelected = false; } } else if (e.Action == NotifyCollectionChangedAction.Replace) { foreach (object item in e.NewItems) { ((EditToken)item).IsSelected = true; } foreach (object item in e.OldItems) { ((EditToken)item).IsSelected = false; } } else if (e.Action == NotifyCollectionChangedAction.Reset) { foreach (EditToken item in _Tokens) { item.IsSelected = false; } } LayoutTokens(); UpdateText(); UpdateEditBoxWatermark(); OnSelectedTokensChanged(EventArgs.Empty); } /// /// Occurs when SelectedTokens collection changes. /// [Description("Occurs when SelectedTokens collection changes.")] public event EventHandler SelectedTokensChanged; /// /// Raises SelectedTokensChanged event. /// /// Provides event arguments. protected virtual void OnSelectedTokensChanged(EventArgs e) { EventHandler handler = SelectedTokensChanged; if (handler != null) handler(this, e); } private CustomCollection _Tokens; /// /// Gets the collection of the tokens available for selection. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public CustomCollection Tokens { get { return _Tokens; } } private void TokensCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { } private bool EffectiveRemoveTokenButtonVisible { get { return _RemoveTokenButtonVisible && !_ReadOnly; } } private Size _RemoveTokenButtonSize = new Size(14, 14); private bool _RemoveTokenButtonVisible = true; /// /// Indicates whether remove token button is displayed on individual tokens so they can be removed from the selection. /// [DefaultValue(true), Category("Behavior"), Description("Indicates whether remove token button is displayed on individual tokens so they can be removed from the selection.")] public bool RemoveTokenButtonVisible { get { return _RemoveTokenButtonVisible; } set { if (value != _RemoveTokenButtonVisible) { bool oldValue = _RemoveTokenButtonVisible; _RemoveTokenButtonVisible = value; OnRemoveTokenButtonVisibleChanged(oldValue, value); } } } /// /// Called when RemoveTokenButtonVisible property has changed. /// /// Old property value /// New property value protected virtual void OnRemoveTokenButtonVisibleChanged(bool oldValue, bool newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("RemoveTokenButtonVisible")); if (_SelectedTokens.Count > 0) LayoutTokens(); } /// /// Returns the token from SelectedTokens at specified position or null/nothing if no token is at given location. /// /// Location in client coordinates to test. /// EditToken instance or null/nothing public EditToken GetSelectedTokenAt(Point p) { foreach (EditToken token in _SelectedTokens) { Rectangle tokenBounds = token.Bounds; tokenBounds.Offset(_AutoScrollPosition); if (tokenBounds.Contains(p)) return token; } return null; } private EditToken _MouseOverToken = null; private EditToken MouseOverToken { get { return _MouseOverToken; } set { if (_MouseOverToken != value) { if (_MouseOverToken != null) _MouseOverToken = value; this.Invalidate(); } } } /// /// Occurs when mouse enters one of the SelectedTokens token. /// [Description("Occurs when mouse enters one of the SelectedTokens token.")] public event EventHandler TokenMouseEnter; /// /// Raises TokenMouseEnter event. /// /// Provides event arguments. protected virtual void OnTokenMouseEnter(object sender, EventArgs e) { EventHandler handler = TokenMouseEnter; if (handler != null) handler(sender, e); } /// /// Occurs when mouse leaves one of the SelectedTokens token. /// [Description("Occurs when mouse leaves one of the SelectedTokens token.")] public event EventHandler TokenMouseLeave; /// /// Raises TokenMouseLeave event. /// /// Provides event arguments. protected virtual void OnTokenMouseLeave(object sender, EventArgs e) { HideToolTip(); EventHandler handler = TokenMouseLeave; if (handler != null) handler(sender, e); } /// /// Occurs when mouse clicks one of the SelectedTokens token. /// [Description("Occurs when mouse clicks one of the SelectedTokens token.")] public event MouseEventHandler TokenMouseClick; /// /// Raises TokenMouseClick event. /// /// Provides event arguments. protected virtual void OnTokenMouseClick(object sender, MouseEventArgs e) { MouseEventHandler handler = TokenMouseClick; if (handler != null) handler(sender, e); } /// /// Occurs when mouse double clicks one of the SelectedTokens token. /// [Description("Occurs when mouse double clicks one of the SelectedTokens token.")] public event MouseEventHandler TokenMouseDoubleClick; /// /// Raises TokenMouseClick event. /// /// Provides event arguments. protected virtual void OnTokenMouseDoubleClick(object sender, MouseEventArgs e) { MouseEventHandler handler = TokenMouseDoubleClick; if (handler != null) handler(sender, e); } /// /// Occurs when mouse hovers one of the SelectedTokens token. /// [Description("Occurs when mouse hovers one of the SelectedTokens token.")] public event EventHandler TokenMouseHover; /// /// Raises TokenMouseHover event. /// /// Provides event arguments. protected virtual void OnTokenMouseHover(object sender, EventArgs e) { EventHandler handler = TokenMouseHover; if (handler != null) handler(sender, e); } protected override void OnMouseMove(MouseEventArgs e) { EditToken token = GetSelectedTokenAt(e.Location); if (token != _MouseOverToken) { if (_MouseOverToken != null) { _MouseOverToken.MouseOverPart = eTokenPart.None; OnTokenMouseLeave(_MouseOverToken, e); } _MouseOverToken = token; this.Invalidate(); DevComponents.AdvTree.Interop.WinApi.ResetHover(this); if (_MouseOverToken != null) OnTokenMouseEnter(_MouseOverToken, e); } if (_MouseOverToken != null) { eTokenPart oldMouseOverPart = _MouseOverToken.MouseOverPart; if (EffectiveRemoveTokenButtonVisible && !_MouseOverToken.RemoveButtonBounds.IsEmpty && _MouseOverToken.RemoveButtonBounds.Contains(e.Location)) _MouseOverToken.MouseOverPart = eTokenPart.RemoveButton; else if (!_MouseOverToken.ImageBounds.IsEmpty && _MouseOverToken.ImageBounds.Contains(e.Location)) _MouseOverToken.MouseOverPart = eTokenPart.Image; else _MouseOverToken.MouseOverPart = eTokenPart.Token; if (oldMouseOverPart != _MouseOverToken.MouseOverPart) this.Invalidate(); } if (_ButtonGroup.Visible) _ButtonGroup.ProcessMouseMove(e); base.OnMouseMove(e); } protected override void OnMouseDown(MouseEventArgs e) { HideToolTip(); if (_MouseOverToken == null && !_ReadOnly && !(_ButtonGroup.Visible && _ButtonGroup.RenderBounds.Contains(e.Location))) { ShowEditTextBox(); } else this.Select(); if (_ButtonGroup.Visible) _ButtonGroup.ProcessMouseDown(e); base.OnMouseDown(e); } protected override void OnMouseUp(MouseEventArgs e) { if (_ButtonGroup.Visible) _ButtonGroup.ProcessMouseUp(e); base.OnMouseUp(e); } protected override void OnMouseClick(MouseEventArgs e) { if (_MouseOverToken != null && _MouseOverToken.MouseOverPart == eTokenPart.RemoveButton) { EditToken token = _MouseOverToken; RemovingTokenEventArgs args = new RemovingTokenEventArgs(token, eEventSource.Mouse); OnRemovingToken(args); if (args.Cancel) return; LeaveMouseOverToken(); this.SelectedTokens.Remove(token); } else if (_MouseOverToken != null) { OnTokenMouseClick(_MouseOverToken, e); } if (_ButtonGroup.Visible) _ButtonGroup.ProcessMouseClick(e); base.OnMouseClick(e); } protected override void OnMouseDoubleClick(MouseEventArgs e) { if (_MouseOverToken != null) { OnTokenMouseDoubleClick(_MouseOverToken, e); } base.OnMouseDoubleClick(e); } protected override void OnMouseHover(EventArgs e) { if (_MouseOverToken != null) { OnTokenMouseHover(_MouseOverToken, e); ShowToolTip(_MouseOverToken); } if (_ButtonGroup.Visible) _ButtonGroup.ProcessMouseHover(e); base.OnMouseHover(e); } /// /// Occurs before token is removed from the SelectedTokens by end user. /// [Description("Occurs before token is removed from the SelectedTokens by end user.")] public event RemovingTokenEventHandler RemovingToken; /// /// Raises RemovingToken event. /// /// Provides event arguments. protected virtual void OnRemovingToken(RemovingTokenEventArgs e) { RemovingTokenEventHandler handler = RemovingToken; if (handler != null) handler(this, e); } private void LeaveMouseOverToken() { HideToolTip(); if (_MouseOverToken != null) { _MouseOverToken.MouseOverPart = eTokenPart.None; OnTokenMouseLeave(_MouseOverToken, EventArgs.Empty); _MouseOverToken = null; this.Invalidate(); } } private bool _IsMouseOver = false; protected override void OnMouseLeave(EventArgs e) { _IsMouseOver = false; LeaveMouseOverToken(); if (_ButtonGroup.Visible) _ButtonGroup.ProcessMouseLeave(); base.OnMouseLeave(e); } protected override void OnMouseEnter(EventArgs e) { _IsMouseOver = true; base.OnMouseEnter(e); } private EditToken _FocusToken = null; private EditToken FocusToken { get { return _FocusToken; } set { if (_FocusToken != value) { if (_FocusToken != null) _FocusToken.IsFocused = false; _FocusToken = value; if (_FocusToken != null) _FocusToken.IsFocused = true; this.Invalidate(); } } } private void FocusPreviousToken() { if (this.SelectedTokens.Count == 0) return; int index = _SelectedTokens.Count - 1; if (_FocusToken != null) index = Math.Max(0, _SelectedTokens.IndexOf(_FocusToken) - 1); FocusToken = _SelectedTokens[index]; } private void FocusNextToken() { if (this.SelectedTokens.Count == 0) return; int index = 0; if (_FocusToken != null) { if (_SelectedTokens.IndexOf(_FocusToken) == _SelectedTokens.Count - 1) { FocusToken = null; _EditBox.Select(); return; } index = Math.Min(_SelectedTokens.Count - 1, _SelectedTokens.IndexOf(_FocusToken) + 1); } FocusToken = _SelectedTokens[index]; } private void DeleteFocusedToken(bool isBackspace, eEventSource eventSource) { EditToken focusToken = _FocusToken; if (focusToken == null) return; RemovingTokenEventArgs e = new RemovingTokenEventArgs(focusToken, eventSource); OnRemovingToken(e); if (e.Cancel) return; FocusToken = null; int index = Math.Max(0, _SelectedTokens.IndexOf(focusToken) - 1); _SelectedTokens.Remove(focusToken); if (_SelectedTokens.Count > 0) { FocusToken = _SelectedTokens[index]; } else { _EditBox.Select(); } } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (!_EditBox.Focused) { if (keyData == Keys.Left) { FocusPreviousToken(); return true; } else if (keyData == Keys.Right) { FocusNextToken(); return true; } else if (keyData == Keys.Back) { DeleteFocusedToken(true, eEventSource.Keyboard); return true; } else if (keyData == Keys.Delete) { DeleteFocusedToken(false, eEventSource.Keyboard); return true; } } return base.ProcessCmdKey(ref msg, keyData); } protected override void OnKeyDown(KeyEventArgs e) { if (_FocusToken != null) { FocusToken = null; _EditBox.Select(); } base.OnKeyDown(e); } protected override void OnLeave(EventArgs e) { if (_EditBox.Visible) { _EditBox.Visible = false; if (_ValidateTokenTextOnLostFocus && !string.IsNullOrEmpty(_EditBox.Text)) { ValidateAndConvertEditText(_EditBox.Text); } } _EditBox.Text = ""; FocusToken = null; if (IsPopupOpen) CloseAutoCompleteDropDown(); HideToolTip(); base.OnLeave(e); } protected override void OnEnter(EventArgs e) { if (!_ReadOnly) { ShowEditTextBox(); } base.OnEnter(e); } private bool _ReadOnly = false; /// /// Indicates whether tokens can be added or removed by end user. Default value is false. /// [DefaultValue(false), Category("Behavior"), Description("Indicates whether tokens can be added or removed by end user. Default value is false.")] public bool ReadOnly { get { return _ReadOnly; } set { if (value != _ReadOnly) { bool oldValue = _ReadOnly; _ReadOnly = value; OnReadOnlyChanged(oldValue, value); } } } private void ShowEditTextBox() { _EditBox.Visible = true; _EditBox.Focus(); EnsureTextBoxScrollPosition(); } private void EnsureTextBoxScrollPosition() { if (!_EditBox.Visible) return; Rectangle clientBounds = GetClientBounds(); if (clientBounds.Contains(_EditBox.Bounds)) return; // Scroll content vertically to ensure text-box is withing client bounds AutoScrollPosition = new Point(0, AutoScrollPosition.Y + (clientBounds.Bottom - _EditBox.Bounds.Bottom)); } /// /// Called when ReadOnly property has changed. /// /// Old property value /// New property value protected virtual void OnReadOnlyChanged(bool oldValue, bool newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("ReadOnly")); if (_ReadOnly) { if (_EditBox.Visible) _EditBox.Visible = false; } else if (this.Focused) { ShowEditTextBox(); _EditBox.Focus(); } if (_RemoveTokenButtonVisible) LayoutTokens(); } private int _DropDownHeight = 120; /// /// Indicates the height of the auto-complete drop-down. /// [DefaultValue(120), Category("Appearance"), Description("Indicates the height of the auto-complete drop-down")] public int DropDownHeight { get { return _DropDownHeight; } set { if (value != _DropDownHeight) { int oldValue = _DropDownHeight; _DropDownHeight = value; OnDropDownHeightChanged(oldValue, value); } } } /// /// Called when DropDownHeight property has changed. /// /// Old property value /// New property value protected virtual void OnDropDownHeightChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("DropDownHeight")); } private int _DropDownWidth = 180; /// /// Indicates the width of the auto-complete drop-down. /// [DefaultValue(180), Category("Appearance"), Description("Indicates the width of the auto-complete drop-down.")] public int DropDownWidth { get { return _DropDownWidth; } set { if (value != _DropDownWidth) { int oldValue = _DropDownWidth; _DropDownWidth = value; OnDropDownWidthChanged(oldValue, value); } } } /// /// Called when DropDownWidth property has changed. /// /// Old property value /// New property value protected virtual void OnDropDownWidthChanged(int oldValue, int newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("DropDownWidth")); } private bool _EnterKeyValidatesToken = true; /// /// Indicates whether when token text is entered into the text-box pressing the Enter key attempts to validate the token and converts the text to token. /// [DefaultValue(true), Category("Behavior"), Description("Indicates whether when token text is entered into the text-box pressing the Enter key attempts to validate the token and converts the text to token.")] public bool EnterKeyValidatesToken { get { return _EnterKeyValidatesToken; } set { _EnterKeyValidatesToken = value; } } private void EditBoxKeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Escape && IsPopupOpen) { CloseAutoCompleteDropDown(); } else if (e.KeyCode == Keys.Enter && IsPopupOpen && _AutoCompleteListBox != null && _AutoCompleteListBox.SelectedIndex >= 0) { SelectToken(_Tokens[_AutoCompleteListBox.SelectedIndex]); } else if (e.KeyCode == Keys.Enter && EnterKeyValidatesToken) { ValidateAndConvertEditText(_EditBox.Text); } else if (e.KeyCode == Keys.Down) { if (IsPopupOpen && _AutoCompleteListBox != null) { _AutoCompleteListBox.SelectNextItem(); e.Handled = true; } else if (!IsPopupOpen && string.IsNullOrEmpty(_EditBox.Text)) { IsPopupOpen = true; e.Handled = true; } } else if (_AutoCompleteListBox != null && e.KeyCode == Keys.Up) { _AutoCompleteListBox.SelectPreviousItem(); e.Handled = true; } else if ((e.KeyCode == Keys.Left || e.KeyCode == Keys.Back) && string.IsNullOrEmpty(_EditBox.Text)) { CloseAutoCompleteDropDown(); if (this.SelectedTokens.Count > 0) { this.Select(); FocusPreviousToken(); } e.Handled = true; } } private void AutoCompleteListBoxItemClick(object sender, EventArgs e) { if (_AutoCompleteListBox.SelectedIndex >= 0) SelectToken(_Tokens[_AutoCompleteListBox.SelectedIndex]); } private bool SelectToken(EditToken token) { if (token.IsSelected) return false; ValidateTokenEventArgs args = new ValidateTokenEventArgs(token, false); if (args.IsValid && args.Token != null) { token = args.Token; this.SelectedTokens.Add(token); _EditBox.Text = ""; CloseAutoCompleteDropDown(); return true; } return false; } private bool ValidateAndConvertEditText(string text) { if (string.IsNullOrEmpty(text)) return false; IsPopupOpen = false; bool isNewToken = false; EditToken token = FindOrCreateToken(text, out isNewToken); ValidateTokenEventArgs args = new ValidateTokenEventArgs(token, isNewToken); OnValidateToken(args); if (args.IsValid && args.Token != null) { token = args.Token; this.SelectedTokens.Add(token); _EditBox.Text = ""; CloseAutoCompleteDropDown(); return true; } return false; } private bool IsSelected(EditToken item) { return item.IsSelected; //foreach (EditToken selectedToken in _SelectedTokens) //{ // if(selectedToken == item) // return true; //} //return false; } private EditToken FindOrCreateToken(string text, out bool isNew) { isNew = false; foreach (EditToken item in _Tokens) { if (!item.IsSelected && TokenExactMatch(text, item, _TokenFilterBehavior)) { return item; } } isNew = true; return new EditToken(text); } private void EditBoxTextChanged(object sender, EventArgs e) { string text = _EditBox.Text; if (!_DroppedDown && FilteredTokenExists(text)) { OpenAutoCompleteDropDown(); } if (_AutoCompleteListBox != null) { FilterAutoCompleteBox(_AutoCompleteListBox, text); } foreach (string separator in _Separators) { if (text.EndsWith(separator)) { text = text.Substring(0, text.Length - separator.Length); ValidateAndConvertEditText(text); break; } } } private PopupHostController _PopupController = null; private ListBoxAdv _AutoCompleteListBox = null; private DateTime _MultiDropDownOpenedAtTime = DateTime.MinValue; private DateTime _MultiDropDownClosedAtTime = DateTime.MinValue; /// /// Occurs before token auto-complete popup is displayed and allows cancelation of popup display. /// [Description("Occurs before token auto-complete popup is displayed and allows cancelation of popup display.")] public event CancelEventHandler BeforeAutoCompletePopupOpen; /// /// Occurs after auto-complete popup is open. /// [Description("Occurs after auto-complete popup is open.")] public event EventHandler AutoCompletePopupOpened; /// /// Raises AutoCompletePopupOpened event. /// /// Provides event arguments. protected virtual void OnAutoCompletePopupOpened(EventArgs e) { EventHandler handler = AutoCompletePopupOpened; if (handler != null) handler(this, e); } /// /// Raises BeforeAutoCompletePopupOpen event. /// /// Provides event arguments. protected virtual void OnBeforeAutoCompletePopupOpen(CancelEventArgs e) { CancelEventHandler handler = BeforeAutoCompletePopupOpen; if (handler != null) handler(this, e); } private void OpenAutoCompleteDropDown() { if (_PopupController == null) { _PopupController = new PopupHostController(); _PopupController.Closed += PopupControllerClosed; } if (_AutoCompleteListBox == null) { _AutoCompleteListBox = CreateAutoCompleteListBox(); //_AutoCompleteListBox.Resize += _AutoCompleteListBox_Resize; //this.Parent.Controls.Add(_AutoCompleteListBox); //_AutoCompleteListBox.Location = new Point(10,100); } if (!_MessageHandlerInstalled && _DropDownButtonVisible) { MessageHandler.RegisterMessageClient(this); _MessageHandlerInstalled = true; } LoadAutoCompleteBox(_AutoCompleteListBox); if (!_PopupController.PopupUserSize || !_PreservePopupSize) { _AutoCompleteListBox.Height = Dpi.Height(this.DropDownHeight); _AutoCompleteListBox.Width = (this.DropDownWidth > 0 ? Dpi.Width(this.DropDownWidth) : this.Width); //_AutoCompleteListBox.MinimumSize = _AutoCompleteListBox.Size; } CancelEventArgs cancelArgs = new CancelEventArgs(); OnBeforeAutoCompletePopupOpen(cancelArgs); if (cancelArgs.Cancel) return; Point location = PointToScreen(new Point(0, Height)); TokenEditorPopupEventArgs popupArgs = new TokenEditorPopupEventArgs(location, _AutoCompleteListBox.Size); OnBeforePopupOpen(popupArgs); location = popupArgs.PopupLocation; ePopupResizeEdge resize = _EnablePopupResize ? ePopupResizeEdge.BottomRight : ePopupResizeEdge.None; _PopupController.AutoClose = false; _PopupController.ParentControlBounds = new Rectangle(PointToScreen(Point.Empty), this.Size); _PopupController.Show(_AutoCompleteListBox, location.X, location.Y, 0, 0, resize); _EditBox.Focus(); _EditBox.SelectionLength = 0; // Clear selection that happens upon focus if (_EditBox.TextLength > 0) _EditBox.SelectionStart = _EditBox.TextLength; _PopupController.AutoClose = true; _DroppedDown = true; _MultiDropDownOpenedAtTime = DateTime.Now; OnAutoCompletePopupOpened(EventArgs.Empty); } /// /// Occurs before the auto-complete popup is displayed and allows you to adjust popup location. /// [Description("Occurs before the auto-complete popup is displayed and allows you to adjust popup location.")] public event TokenEditorPopupEventHandler BeforePopupOpen; /// /// Raises BeforePopupOpen event. /// /// Provides event arguments. protected virtual void OnBeforePopupOpen(TokenEditorPopupEventArgs e) { TokenEditorPopupEventHandler handler = BeforePopupOpen; if (handler != null) handler(this, e); } private void LoadAutoCompleteBox(ListBoxAdv listBox) { listBox.BeginUpdate(); listBox.Items.Clear(); listBox.CheckBoxesVisible = _CheckBoxesVisible; listBox.ShowToolTips = _ShowToolTips; foreach (EditToken item in _Tokens) { ListBoxItem listItem = new ListBoxItem(); listItem.Text = GetTokenText(item); listItem.Tooltip = item.Tooltip; if (_CheckBoxesVisible) listItem.CheckState = (item.IsSelected ? CheckState.Checked : CheckState.Unchecked); else listItem.Visible = !item.IsSelected; listBox.Items.Add(listItem); } listBox.EndUpdate(); } private bool FilteredTokenExists(string text) { foreach (EditToken item in _Tokens) { if (item.IsSelected) continue; if (TokenMatch(text, item, _TokenFilterBehavior)) return true; } return false; } private eTokenFilterBehavior _TokenFilterBehavior = eTokenFilterBehavior.TextAndValue; /// /// Indicates how tokens are filtered based on the entered text /// [DefaultValue(eTokenFilterBehavior.TextAndValue), Category("Behavior"), Description("Indicates how tokens are filtered based on the entered text")] public eTokenFilterBehavior TokenFilterBehavior { get { return _TokenFilterBehavior; } set { _TokenFilterBehavior = value; } } private static bool TokenMatch(string filterText, EditToken token, eTokenFilterBehavior behavior) { filterText = filterText.ToLower(); if(behavior == eTokenFilterBehavior.Text) return !string.IsNullOrEmpty(token.Text) && token.Text.ToLower().Contains(filterText); else if(behavior == eTokenFilterBehavior.Value) return !string.IsNullOrEmpty(token.Value) && token.Value.ToLower().Contains(filterText); return !string.IsNullOrEmpty(token.Text) && token.Text.ToLower().Contains(filterText) || !string.IsNullOrEmpty(token.Value) && token.Value.ToLower().Contains(filterText); } private static bool TokenExactMatch(string filterText, EditToken token, eTokenFilterBehavior behavior) { filterText = filterText.ToLower(); if (behavior == eTokenFilterBehavior.Text) return !string.IsNullOrEmpty(token.Text) && token.Text.ToLower() == filterText; else if (behavior == eTokenFilterBehavior.Value) return !string.IsNullOrEmpty(token.Value) && token.Value.ToLower() == filterText; return !string.IsNullOrEmpty(token.Text) && token.Text.ToLower() == filterText || !string.IsNullOrEmpty(token.Value) && token.Value.ToLower() == filterText; } private void FilterAutoCompleteBox(ListBoxAdv listBox, string filterText) { bool oneVisible = false; listBox.BeginUpdate(); if (string.IsNullOrEmpty(filterText)) { if (!_CheckBoxesVisible) { foreach (ListBoxItem item in listBox.Items) { item.IsSelected = false; item.Visible = !item.IsSelected; if (item.Visible) oneVisible = true; } } } else { for (int i = 0; i < listBox.Items.Count; i++) { ListBoxItem item = (ListBoxItem)listBox.Items[i]; EditToken token = _Tokens[i]; if (!token.IsSelected && TokenMatch(filterText, token, _TokenFilterBehavior)) { if (!_CheckBoxesVisible) item.Visible = true; if (!oneVisible) item.IsSelected = true; else item.IsSelected = false; oneVisible = true; } else if (!_CheckBoxesVisible) item.Visible = false; else item.IsSelected = false; } } listBox.EndUpdate(); if (!oneVisible && !_CheckBoxesVisible) CloseAutoCompleteDropDown(); } private ListBoxAdv CreateAutoCompleteListBox() { ListBoxAdv listBox = new ListBoxAdv(); #if (!TRIAL) listBox.LicenseKey = "F962CEC7-CD8F-4911-A9E9-CAB39962FC1F"; #endif //listBox.BackColor = Color.White; listBox.BackgroundStyle.Class = ElementStyleClassKeys.ListBoxAdvKey; listBox.ItemClick += AutoCompleteListBoxItemClick; listBox.ItemCheck += AutoCompleteListBoxItemCheck; listBox.AutoScroll = true; //listBox.BackgroundStyle.Reset(); //listBox.BackgroundStyle.BackColor = SystemColors.Window; //listBox.BackgroundStyle.Border = eStyleBorderType.None; return listBox; } void AutoCompleteListBoxItemCheck(object sender, ListBoxAdvItemCheckEventArgs e) { int index = _AutoCompleteListBox.Items.IndexOf(e.Item); EditToken token = _Tokens[index]; if (e.Item.CheckState == CheckState.Unchecked && token.IsSelected) this.SelectedTokens.Remove(token); else if (!token.IsSelected) this.SelectedTokens.Add(token); } private void PopupControllerClosed(object sender, ToolStripDropDownClosedEventArgs e) { _DroppedDown = false; //m_SelectedIndexInternal = this.SelectedIndex; _MultiDropDownClosedAtTime = DateTime.Now; //OnDropDownClosed(e); } private void CloseAutoCompleteDropDown() { if (_PopupController != null && _DroppedDown) { _PopupController.Hide(); } } private bool _PreservePopupSize = true; /// /// Indicates whether auto-complete popup size is preserved between popup displays if popup is resized by end-user. /// [DefaultValue(true), Category("Auto-Complete"), Description("Indicates whether auto-complete popup size is preserved between popup displays if popup is resized by end-user.")] public bool PreservePopupSize { get { return _PreservePopupSize; } set { _PreservePopupSize = value; } } private bool _EnablePopupResize = true; /// /// Indicates whether auto-complete popup can be resized by end user. /// [DefaultValue(true), Category("Auto-Complete"), Description("Indicates whether auto-complete popup can be resized by end user.")] public bool EnablePopupResize { get { return _EnablePopupResize; } set { _EnablePopupResize = value; } } private bool _PopupCloseButtonVisible = true; /// /// Indicates whether multi-column popup close button is visible. /// [DefaultValue(true), Category("Auto-Complete"), Description("Indicates whether multi-column popup close button is visible.")] public bool PopupCloseButtonVisible { get { return _PopupCloseButtonVisible; } set { _PopupCloseButtonVisible = value; } } private bool _DroppedDown = false; /// /// Gets or sets whether auto-complete popup window is open. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IsPopupOpen { get { return _DroppedDown; } set { if (value != _DroppedDown) { if (value) OpenAutoCompleteDropDown(); else CloseAutoCompleteDropDown(); } } } private List _Separators = new List(); /// /// Gets the list of separators which are used to divide entered text into the tokens. /// [Category("Behavior"), Description("Gets the list of separators which are used to divide entered text into the tokens."), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), MergableProperty(false), Localizable(true)] [Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, System.Design, Version= 2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))] public List Separators { get { return _Separators; } } /// /// Occurs when an token is selected from the auto-complete list or when text entry by end user is parsed into token to validate it. /// [Description("Occurs when an token is selected from the auto-complete list or when text entry by end user is parsed into token to validate it.")] public event ValidateTokenEventHandler ValidateToken; /// /// Raises ValidateToken event. /// /// Provides event arguments. protected virtual void OnValidateToken(ValidateTokenEventArgs e) { ValidateTokenEventHandler handler = ValidateToken; if (handler != null) handler(this, e); } private bool _AutoSizeHeight = true; /// /// Indicates whether control automatically increases its height as more tokens are selected. MaxHeightLines property controls the maximum number of lines control will grow to before showing scroll-bar. /// [DefaultValue(true), Category("Behavior"), Description("Indicates whether control automatically increases its height as more tokens are selected. MaxHeightLines property controls the maximum number of lines control will grow to before showing scroll-bar.")] public bool AutoSizeHeight { get { return _AutoSizeHeight; } set { if (_AutoSizeHeight != value) { _AutoSizeHeight = value; if (_AutoSizeHeight) LayoutTokens(); } } } private int _MaxHeightLines = 5; /// /// Indicates maximum number of lines control will grow to when AutoSizeHeight=true. Set to 0 to indicates unlimited growth. /// Default value is 5. /// [DefaultValue(5), Category("Behavior"), Description("Indicates maximum number of lines control will grow to when AutoSizeHeight=true. Set to 0 to indicates unlimited growth.")] public int MaxHeightLines { get { return _MaxHeightLines; } set { _MaxHeightLines = value; } } /// /// Gets reference to internal text-box control that is used to input the token text. /// [Browsable(false)] public TextBox EditTextBox { get { return _EditBox; } } private bool _ValidateTokenTextOnLostFocus = true; /// /// Indicates whether any text entered into the token editor is validated and converted to token when control loses focus. /// [DefaultValue(true), Category("Behavior"), Description("Indicates whether any text entered into the token editor is validated and converted to token when control loses focus.")] public bool ValidateTokenTextOnLostFocus { get { return _ValidateTokenTextOnLostFocus; } set { _ValidateTokenTextOnLostFocus = value; } } private string _WatermarkText = ""; private Font _WatermarkFont = null; private Color _WatermarkColor = SystemColors.GrayText; private bool _WatermarkEnabled = true; /// /// Gets or sets whether watermark text is displayed when control is empty. Default value is true. /// [DefaultValue(true), Description("Indicates whether watermark text is displayed when control is empty.")] public virtual bool WatermarkEnabled { get { return _WatermarkEnabled; } set { _WatermarkEnabled = value; this.Invalidate(); UpdateEditBoxWatermark(); } } private Image _WatermarkImage = null; /// /// Gets or sets the watermark image displayed inside of the control when Text is not set and control does not have input focus. /// [DefaultValue(null), Category("Appearance"), Description("Indicates watermark image displayed inside of the control when Text is not set and control does not have input focus.")] public Image WatermarkImage { get { return _WatermarkImage; } set { _WatermarkImage = value; this.Invalidate(); UpdateEditBoxWatermark(); } } private ContentAlignment _WatermarkImageAlignment = ContentAlignment.MiddleLeft; /// /// Gets or sets the watermark image alignment. /// [DefaultValue(ContentAlignment.MiddleLeft), Category("Appearance"), Description("Indicates watermark image alignment.")] public ContentAlignment WatermarkImageAlignment { get { return _WatermarkImageAlignment; } set { _WatermarkImageAlignment = value; this.Invalidate(); UpdateEditBoxWatermark(); } } /// /// Gets or sets the watermark (tip) text displayed inside of the control when Text is not set and control does not have input focus. This property supports text-markup. /// [Browsable(true), DefaultValue(""), Localizable(true), Category("Appearance"), Description("Indicates watermark text displayed inside of the control when Text is not set and control does not have input focus."), Editor("DevComponents.DotNetBar.Design.TextMarkupUIEditor, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(System.Drawing.Design.UITypeEditor))] public string WatermarkText { get { return _WatermarkText; } set { if (value == null) value = ""; _WatermarkText = value; MarkupTextChanged(); UpdateEditBoxWatermark(); this.Invalidate(); } } /// /// Gets or sets the watermark font. /// [Browsable(true), Category("Appearance"), Description("Indicates watermark font."), DefaultValue(null)] public Font WatermarkFont { get { return _WatermarkFont; } set { _WatermarkFont = value; this.Invalidate(); UpdateEditBoxWatermark(); } } /// /// Gets or sets the watermark text color. /// [Browsable(true), Category("Appearance"), Description("Indicates watermark text color.")] public Color WatermarkColor { get { return _WatermarkColor; } set { _WatermarkColor = value; this.Invalidate(); UpdateEditBoxWatermark(); } } /// /// Indicates whether property should be serialized by Windows Forms designer. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeWatermarkColor() { return _WatermarkColor != SystemColors.GrayText; } /// /// Resets the property to default value. /// [EditorBrowsable(EditorBrowsableState.Never)] public void ResetWatermarkColor() { this.WatermarkColor = SystemColors.GrayText; } protected virtual bool IsWatermarkRendered { get { return (!this.Focused || _WatermarkBehavior == eWatermarkBehavior.HideNonEmpty) && this.SelectedTokens.Count == 0; } } private eWatermarkBehavior _WatermarkBehavior = eWatermarkBehavior.HideOnFocus; /// /// Gets or sets the watermark hiding behaviour. Default value indicates that watermark is hidden when control receives input focus. /// [DefaultValue(eWatermarkBehavior.HideOnFocus), Category("Behavior"), Description("Indicates watermark hiding behaviour.")] public eWatermarkBehavior WatermarkBehavior { get { return _WatermarkBehavior; } set { _WatermarkBehavior = value; this.Invalidate(); UpdateEditBoxWatermark(); } } private void UpdateEditBoxWatermark() { _EditBox.WatermarkText = _WatermarkText; _EditBox.WatermarkColor = _WatermarkColor; _EditBox.WatermarkFont = _WatermarkFont; _EditBox.WatermarkImage = _WatermarkImage; _EditBox.WatermarkImageAlignment = _WatermarkImageAlignment; if (this.SelectedTokens.Count > 0 || !_WatermarkEnabled) _EditBox.WatermarkEnabled = false; else if (_WatermarkBehavior == eWatermarkBehavior.HideNonEmpty) { _EditBox.WatermarkEnabled = true; _EditBox.WatermarkBehavior = eWatermarkBehavior.HideNonEmpty; } else { _EditBox.WatermarkBehavior = eWatermarkBehavior.HideOnFocus; } } private TextMarkup.BodyElement _TextMarkup = null; private void MarkupTextChanged() { _TextMarkup = null; if (!TextMarkup.MarkupParser.IsMarkup(ref _WatermarkText)) return; _TextMarkup = TextMarkup.MarkupParser.Parse(_WatermarkText); ResizeMarkup(); } private MarkupDrawContext GetMarkupDrawContext(Graphics g) { return new MarkupDrawContext(g, (_WatermarkFont == null ? this.Font : _WatermarkFont), _WatermarkColor, this.RightToLeft == RightToLeft.Yes); } private void ResizeMarkup() { if (_TextMarkup != null) { using (Graphics g = this.CreateGraphics()) { MarkupDrawContext dc = GetMarkupDrawContext(g); _TextMarkup.Measure(GetWatermarkBounds().Size, dc); Size sz = _TextMarkup.Bounds.Size; _TextMarkup.Arrange(new Rectangle(GetWatermarkBounds().Location, sz), dc); } } } private Rectangle GetWatermarkBounds() { Rectangle r = this.ClientRectangle; r.Inflate(-1, 0); r.X += 2; r.Width -= 2; return r; } private void DrawWatermark(Graphics g) { Rectangle bounds = GetWatermarkBounds(); if (_WatermarkImage != null) { Rectangle imageBounds = new Rectangle(Point.Empty, _WatermarkImage.Size); if (_WatermarkImageAlignment == ContentAlignment.BottomCenter) imageBounds.Location = new Point(bounds.X + (bounds.Width - imageBounds.Width) / 2, bounds.Bottom - imageBounds.Height); else if (_WatermarkImageAlignment == ContentAlignment.BottomLeft) imageBounds.Location = new Point(bounds.X, bounds.Bottom - imageBounds.Height); else if (_WatermarkImageAlignment == ContentAlignment.BottomRight) imageBounds.Location = new Point(bounds.Right - imageBounds.Width, bounds.Bottom - imageBounds.Height); else if (_WatermarkImageAlignment == ContentAlignment.MiddleCenter) imageBounds.Location = new Point(bounds.X + (bounds.Width - imageBounds.Width) / 2, bounds.Y + (bounds.Height - imageBounds.Height) / 2); else if (_WatermarkImageAlignment == ContentAlignment.MiddleLeft) imageBounds.Location = new Point(bounds.X, bounds.Y + (bounds.Height - imageBounds.Height) / 2); else if (_WatermarkImageAlignment == ContentAlignment.MiddleRight) imageBounds.Location = new Point(bounds.Right - imageBounds.Width, bounds.Y + (bounds.Height - imageBounds.Height) / 2); else if (_WatermarkImageAlignment == ContentAlignment.TopCenter) imageBounds.Location = new Point(bounds.X + (bounds.Width - imageBounds.Width) / 2, bounds.Y); else if (_WatermarkImageAlignment == ContentAlignment.TopLeft) imageBounds.Location = new Point(bounds.X, bounds.Y); else if (_WatermarkImageAlignment == ContentAlignment.TopRight) imageBounds.Location = new Point(bounds.Right - imageBounds.Width, bounds.Y); g.DrawImage(_WatermarkImage, imageBounds); if (_WatermarkImageAlignment == ContentAlignment.BottomLeft || _WatermarkImageAlignment == ContentAlignment.MiddleLeft || _WatermarkImageAlignment == ContentAlignment.TopLeft) { bounds.X = imageBounds.Right; bounds.Width = Math.Max(0, bounds.Width - imageBounds.Width); } else if (_WatermarkImageAlignment == ContentAlignment.BottomRight || _WatermarkImageAlignment == ContentAlignment.MiddleRight || _WatermarkImageAlignment == ContentAlignment.TopRight) { bounds.Width = Math.Max(0, bounds.Width - imageBounds.Width); } } if (bounds.Width <= 0) return; if (_TextMarkup != null) { MarkupDrawContext dc = GetMarkupDrawContext(g); if (_TextMarkup.Bounds.Height < bounds.Height) _TextMarkup.Bounds = new Rectangle(bounds.X, bounds.Y + (bounds.Height - _TextMarkup.Bounds.Height) / 2, _TextMarkup.Bounds.Width, _TextMarkup.Bounds.Height); _TextMarkup.Render(dc); } else { eTextFormat tf = eTextFormat.Left | eTextFormat.VerticalCenter; if (this.RightToLeft == RightToLeft.Yes) tf |= eTextFormat.RightToLeft; tf |= eTextFormat.EndEllipsis; tf |= eTextFormat.WordBreak; TextDrawing.DrawString(g, _WatermarkText, (_WatermarkFont == null ? this.Font : _WatermarkFont), _WatermarkColor, bounds, tf); } } protected override void OnTextChanged(EventArgs e) { if (!_UpdatingText) { this.SelectedTokens.Clear(); if (!string.IsNullOrEmpty(this.Text)) { if (!string.IsNullOrEmpty(_TextSeparator)) { string[] tokens = this.Text.Split(new string[] { _TextSeparator }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in tokens) { bool isNew = false; SelectToken(FindOrCreateToken(this.Text, out isNew)); } } else { bool isNew = false; SelectToken(FindOrCreateToken(this.Text, out isNew)); } } } base.OnTextChanged(e); } private bool _UpdatingText = false; private void UpdateText() { _UpdatingText = true; string text = ""; for (int i = 0; i < _SelectedTokens.Count; i++) { text += GetTokenText(_SelectedTokens[i]); if (i < _SelectedTokens.Count - 1) text += _TextSeparator; } this.Text = text; _UpdatingText = false; } private string _TextSeparator = ","; /// /// Indicates the character separator that is used to separate tokens when controls Text property is updated or parsed. /// [DefaultValue(","), Category("Behavior"), Description("Indicates the character separator that is used to separate tokens when controls Text property is updated or parsed.")] public string TextSeparator { get { return _TextSeparator; } set { if (value != _TextSeparator) { string oldValue = _TextSeparator; _TextSeparator = value; OnTextSeparatorChanged(oldValue, value); } } } /// /// Called when TextSeparator property has changed. /// /// Old property value /// New property value protected virtual void OnTextSeparatorChanged(string oldValue, string newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("TextSeparator")); UpdateText(); } [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public override string Text { get { return base.Text; } set { base.Text = value; } } private bool _DropDownButtonVisible = false; /// /// Indicates whether drop-down button which shows available token popup is displayed /// [DefaultValue(false), Category("Appearance"), Description("Indicates whether drop-down button which shows available token popup is displayed")] public bool DropDownButtonVisible { get { return _DropDownButtonVisible; } set { if (value != _DropDownButtonVisible) { bool oldValue = _DropDownButtonVisible; _DropDownButtonVisible = value; OnDropDownButtonVisibleChanged(oldValue, value); } } } /// /// Called when DropDownButtonVisible property has changed. /// /// Old property value /// New property value protected virtual void OnDropDownButtonVisibleChanged(bool oldValue, bool newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("DropDownButtonVisible")); UpdateButtons(); LayoutTokens(); Invalidate(); } private bool _CheckBoxesVisible = false; /// /// Indicates whether check-boxes are displayed on popup token selection list and used for token selection. /// [DefaultValue(false), Category("Behavior"), Description("Indicates whether check-boxes are displayed on popup token selection list and used for token selection.")] public bool CheckBoxesVisible { get { return _CheckBoxesVisible; } set { if (value != _CheckBoxesVisible) { bool oldValue = _CheckBoxesVisible; _CheckBoxesVisible = value; OnCheckBoxesVisibleChanged(oldValue, value); } } } /// /// Called when CheckBoxesVisible property has changed. /// /// Old property value /// New property value protected virtual void OnCheckBoxesVisibleChanged(bool oldValue, bool newValue) { //OnPropertyChanged(new PropertyChangedEventArgs("CheckBoxesVisible")); } private VisualGroup _ButtonGroup = null; private VisualDropDownButton _DropDownButton = null; private VisualUpDownButton _ScrollButton = null; private int GetButtonHeight() { return GetSingleLineTextBoxHeight() - 4; } private void UpdateButtons() { if (!_DropDownButtonVisible && !_ScrollButton.Visible) _ButtonGroup.Visible = false; else { _ButtonGroup.Visible = true; _DropDownButton.Height = GetButtonHeight(); } _DropDownButton.Visible = _DropDownButtonVisible; _ButtonGroup.InvalidateArrange(); } protected virtual void CreateButtons() { _DropDownButton = new VisualDropDownButton(); _DropDownButton.MouseClick += DropDownButtonClick; _DropDownButton.Visible = false; _ButtonGroup.Items.Add(_DropDownButton); _ScrollButton = new VisualUpDownButton(); _ScrollButton.UpClick += ScrollButtonUpClick; _ScrollButton.DownClick += ScrollButtonDownClick; _ScrollButton.Visible = false; _ButtonGroup.Items.Add(_ScrollButton); } void ScrollButtonDownClick(object sender, EventArgs e) { if (Math.Abs(_AutoScrollPosition.Y) < (_VScrollBar.Maximum - _VScrollBar.LargeChange - 1)) AutoScrollPosition = new Point(_AutoScrollPosition.X, _AutoScrollPosition.Y - _VScrollBar.SmallChange); } void ScrollButtonUpClick(object sender, EventArgs e) { if (_AutoScrollPosition.Y < 0) AutoScrollPosition = new Point(_AutoScrollPosition.X, Math.Min(0, _AutoScrollPosition.Y + _VScrollBar.SmallChange)); } void DropDownButtonClick(object sender, MouseEventArgs e) { IsPopupOpen = !IsPopupOpen; } private void ButtonGroupRenderInvalid(object sender, EventArgs e) { Invalidate(); } private void ButtonGroupArrangeInvalid(object sender, EventArgs e) { Invalidate(); } private void ButtonGroupResetMouseHover(object sender, EventArgs e) { DevComponents.AdvTree.Interop.WinApi.ResetHover(this); } private void PaintButtons(Graphics g) { PaintInfo p = CreatePaintInfo(g); if (!_ButtonGroup.IsLayoutValid) { _ButtonGroup.PerformLayout(p); } bool disposeStyle = false; ElementStyle style = ElementStyleDisplay.GetElementStyle(_BackgroundStyle, out disposeStyle); _ButtonGroup.RenderBounds = new Rectangle(this.Width - (ElementStyleLayout.RightWhiteSpace(style, eSpacePart.Border) + 1) - _ButtonGroup.Size.Width, ElementStyleLayout.TopWhiteSpace(style, eSpacePart.Border) + 1, _ButtonGroup.Size.Width, _ButtonGroup.Size.Height); _ButtonGroup.ProcessPaint(p); if (disposeStyle) style.Dispose(); } private PaintInfo CreatePaintInfo(Graphics g) { PaintInfo p = new PaintInfo(); p.Graphics = g; p.DefaultFont = this.Font; p.ForeColor = this.ForeColor; p.RenderOffset = new System.Drawing.Point(); Size s = this.Size; bool disposeStyle = false; ElementStyle style = ElementStyleDisplay.GetElementStyle(_BackgroundStyle, out disposeStyle); s.Height -= (ElementStyleLayout.TopWhiteSpace(style, eSpacePart.Border) + ElementStyleLayout.BottomWhiteSpace(style, eSpacePart.Border)) + 2; s.Width -= (ElementStyleLayout.LeftWhiteSpace(style, eSpacePart.Border) + ElementStyleLayout.RightWhiteSpace(style, eSpacePart.Border)) + 2; p.AvailableSize = s; p.ParentEnabled = this.Enabled; p.MouseOver = _IsMouseOver || this.Focused; if (disposeStyle) style.Dispose(); return p; } #endregion #region Tooltip Support private DevComponents.DotNetBar.ToolTip _ToolTipWnd = null; /// /// Shows tooltip for this item. /// public virtual void ShowToolTip(EditToken token) { if (this.DesignMode || token==null || !token.IsSelected) return; if (this.Visible && this.ShowToolTips ) { string tooltip = token.Tooltip; if (!string.IsNullOrEmpty(tooltip)) { if (_ToolTipWnd == null) _ToolTipWnd = new DevComponents.DotNetBar.ToolTip(); _ToolTipWnd.Style = StyleManager.GetEffectiveStyle(); _ToolTipWnd.Text = tooltip; _ToolTipWnd.ReferenceRectangle = new Rectangle(PointToScreen(token.Bounds.Location), token.Bounds.Size); OnToolTipVisibleChanged(new EventArgs()); _ToolTipWnd.ShowToolTip(); } } } /// /// Destroys tooltip window. /// internal protected void HideToolTip() { if (_ToolTipWnd != null) { System.Drawing.Rectangle tipRect = _ToolTipWnd.Bounds; tipRect.Width += 5; tipRect.Height += 6; OnToolTipVisibleChanged(new EventArgs()); try { if (_ToolTipWnd != null) { _ToolTipWnd.Hide(); _ToolTipWnd.Dispose(); _ToolTipWnd = null; } } catch { } this.Invalidate(); } } /// /// Occurs when item's tooltip visibility has changed. /// [System.ComponentModel.Description("Occurs when item's tooltip visibility has changed.")] public event EventHandler ToolTipVisibleChanged; private void OnToolTipVisibleChanged(EventArgs eventArgs) { EventHandler h = ToolTipVisibleChanged; if (h != null) ToolTipVisibleChanged(this, eventArgs); } private bool _ShowToolTips = true; /// /// Gets or sets whether tooltips are shown when mouse is over the selected token when Tooltip property is set. /// [DefaultValue(true), Category("Behavior"), Description("Indicates whether tooltips are shown when mouse is over the cell when Tooltip property is set.")] public bool ShowToolTips { get { return _ShowToolTips; } set { _ShowToolTips = value; } } #endregion #region IMessageHandlerClient private bool _MessageHandlerInstalled = false; bool IMessageHandlerClient.OnSysKeyDown(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { return false; } bool IMessageHandlerClient.OnSysKeyUp(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { return false; } bool IMessageHandlerClient.OnKeyDown(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { return false; } bool IMessageHandlerClient.OnMouseDown(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { if (IsPopupOpen && _PopupController != null) { if (!_PopupController.Bounds.Contains(Control.MousePosition)) { IsPopupOpen = false; } } return false; } bool IMessageHandlerClient.OnMouseMove(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { return false; } bool IMessageHandlerClient.OnMouseWheel(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { return false; } bool IMessageHandlerClient.IsModal { get { return false; } } #endregion } /// /// Delegate for the ValidateTokenEvent event. /// public delegate void ValidateTokenEventHandler(object sender, ValidateTokenEventArgs ea); /// /// Arguments for the ValidateTokenEvent event. /// public class ValidateTokenEventArgs : EventArgs { /// /// Indicates whether validated token is valid. Default value is true. When you set this property to false the token being validated will be discared. /// public bool IsValid = true; /// /// Indicates the Token that will be accepted by the control if IsValid=true. /// public EditToken Token = null; /// /// Indicates whether token is newly created. When false it means that token was taken from Tokens collection. /// public readonly bool IsNewToken; /// /// Initializes a new instance of the ValidateTokenEventArgs class. /// /// public ValidateTokenEventArgs(EditToken token, bool isNewToken) { Token = token; IsNewToken = isNewToken; } } /// /// Delegate for RemovingToken event. /// public delegate void RemovingTokenEventHandler(object sender, RemovingTokenEventArgs ea); /// /// Defines event arguments for RemovingToken event. /// public class RemovingTokenEventArgs : EventArgs { /// /// Indicates the Token that will be removed. /// public readonly EditToken Token; /// /// Set to true to cancel removal of the token. /// public bool Cancel = false; /// /// Indicates the source of the event. /// public readonly eEventSource EventSource; /// /// Initializes a new instance of the RemovingTokenEventArgs class. /// /// public RemovingTokenEventArgs(EditToken token, eEventSource eventSource) { Token = token; EventSource = eventSource; } } /// /// Specifies the filter behavior on token editor popup list. /// public enum eTokenFilterBehavior { /// /// Token text is searched for the match. /// Text, /// /// Token value is searched for the match. /// Value, /// /// Both token text and value are searched for the match. /// TextAndValue } /// /// Delegate for TokenEditor.BeforePopupOpen event. /// public delegate void TokenEditorPopupEventHandler(object sender, TokenEditorPopupEventArgs ea); /// /// Defines event arguments for BeforePopupOpen event. /// public class TokenEditorPopupEventArgs : EventArgs { /// /// Gets or sets the screen location of the popup in relation to the TokenEditor control. /// public Point PopupLocation = Point.Empty; /// /// Gets the suggested popup size. /// public readonly Size PopupSize; /// /// Initializes a new instance of the TokenEditorPopupEventArgs class. /// /// public TokenEditorPopupEventArgs(Point popupLocation, Size popupSize) { PopupLocation = popupLocation; PopupSize = popupSize; } } }