### 2024-12-20 星期五更新
------ #### SuperDesign V3.0.2412.2001 - *.[新增]新增程序更新日志设置和自动发布功能。 - *.[修复]修复Post数据格式不正确时双击文本框会导致软件闪退的BUG。
This commit is contained in:
1 parent
03bcb8c5fd
commit
993f1ca1a9
349 files changed
+81977
-13873
No files matched your search
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
/// <summary>
|
||||
/// Item of autocomplete menu
|
||||
/// </summary>
|
||||
public class AutocompleteItem
|
||||
{
|
||||
public object Tag;
|
||||
string toolTipTitle;
|
||||
string toolTipText;
|
||||
string menuText;
|
||||
|
||||
/// <summary>
|
||||
/// Parent AutocompleteMenu
|
||||
/// </summary>
|
||||
public AutocompleteMenu Parent { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Text for inserting into textbox
|
||||
/// </summary>
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Image index for this item
|
||||
/// </summary>
|
||||
public int ImageIndex{get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Alignment. The direction the text shows.
|
||||
/// </summary>
|
||||
public StringAlignment Alignment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Title for tooltip.
|
||||
/// </summary>
|
||||
/// <remarks>Return null for disable tooltip for this item</remarks>
|
||||
public virtual string ToolTipTitle
|
||||
{
|
||||
get { return toolTipTitle; }
|
||||
set { toolTipTitle = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tooltip text.
|
||||
/// </summary>
|
||||
/// <remarks>For display tooltip text, ToolTipTitle must be not null</remarks>
|
||||
public virtual string ToolTipText
|
||||
{
|
||||
get { return toolTipText; }
|
||||
set { toolTipText = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Menu text. This text is displayed in the drop-down menu.
|
||||
/// </summary>
|
||||
public virtual string MenuText
|
||||
{
|
||||
get { return menuText; }
|
||||
set { menuText = value; }
|
||||
}
|
||||
|
||||
public AutocompleteItem()
|
||||
{
|
||||
ImageIndex = -1;
|
||||
}
|
||||
|
||||
public AutocompleteItem(string text):this()
|
||||
{
|
||||
Text = text;
|
||||
Alignment = StringAlignment.Near;
|
||||
}
|
||||
|
||||
public AutocompleteItem(string text, int imageIndex)
|
||||
: this(text)
|
||||
{
|
||||
this.ImageIndex = imageIndex;
|
||||
}
|
||||
|
||||
public AutocompleteItem(string text, int imageIndex, string menuText)
|
||||
: this(text, imageIndex)
|
||||
{
|
||||
this.menuText = menuText;
|
||||
}
|
||||
|
||||
public AutocompleteItem(string text, int imageIndex, string menuText, string toolTipTitle, string toolTipText)
|
||||
: this(text, imageIndex, menuText)
|
||||
{
|
||||
this.toolTipTitle = toolTipTitle;
|
||||
this.toolTipText = toolTipText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns text for inserting into Textbox
|
||||
/// </summary>
|
||||
public virtual string GetTextForReplace()
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares fragment text with this item
|
||||
/// </summary>
|
||||
public virtual CompareResult Compare(string fragmentText)
|
||||
{
|
||||
if (Text.StartsWith(fragmentText, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
Text != fragmentText)
|
||||
return CompareResult.VisibleAndSelected;
|
||||
|
||||
return CompareResult.Hidden;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns text for display into popup menu
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return menuText ?? Text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is called after item was inserted into text
|
||||
/// </summary>
|
||||
public virtual void OnSelected(SelectedEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnPaint(PaintItemEventArgs e)
|
||||
{
|
||||
using(var brush = new SolidBrush(e.IsSelected ? e.Colors.SelectedForeColor : e.Colors.ForeColor))
|
||||
e.Graphics.DrawString(ToString(), e.Font, brush, e.TextRect, new StringFormat() {Alignment = Alignment});
|
||||
}
|
||||
}
|
||||
|
||||
public enum CompareResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Item do not appears
|
||||
/// </summary>
|
||||
Hidden,
|
||||
/// <summary>
|
||||
/// Item appears
|
||||
/// </summary>
|
||||
Visible,
|
||||
/// <summary>
|
||||
/// Item appears and will selected
|
||||
/// </summary>
|
||||
VisibleAndSelected
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
/// <summary>
|
||||
/// This autocomplete item appears after dot
|
||||
/// </summary>
|
||||
public class MethodAutocompleteItem : AutocompleteItem
|
||||
{
|
||||
string firstPart;
|
||||
string lowercaseText;
|
||||
|
||||
public MethodAutocompleteItem(string text)
|
||||
: base(text)
|
||||
{
|
||||
lowercaseText = Text.ToLower();
|
||||
}
|
||||
|
||||
public override CompareResult Compare(string fragmentText)
|
||||
{
|
||||
int i = fragmentText.LastIndexOf('.');
|
||||
if (i < 0)
|
||||
return CompareResult.Hidden;
|
||||
string lastPart = fragmentText.Substring(i + 1);
|
||||
firstPart = fragmentText.Substring(0, i);
|
||||
|
||||
if (lastPart == "") return CompareResult.Visible;
|
||||
if (Text.StartsWith(lastPart, StringComparison.InvariantCultureIgnoreCase))
|
||||
return CompareResult.VisibleAndSelected;
|
||||
if (lowercaseText.Contains(lastPart.ToLower()))
|
||||
return CompareResult.Visible;
|
||||
|
||||
return CompareResult.Hidden;
|
||||
}
|
||||
|
||||
public override string GetTextForReplace()
|
||||
{
|
||||
return firstPart + "." + Text;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Autocomplete item for code snippets
|
||||
/// </summary>
|
||||
/// <remarks>Snippet can contain special char ^ for caret position.</remarks>
|
||||
public class SnippetAutocompleteItem : AutocompleteItem
|
||||
{
|
||||
public SnippetAutocompleteItem(string snippet)
|
||||
{
|
||||
Text = snippet.Replace("\r", "");
|
||||
ToolTipTitle = "Code snippet:";
|
||||
ToolTipText = Text;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return MenuText ?? Text.Replace("\n", " ").Replace("^", "");
|
||||
}
|
||||
|
||||
public override string GetTextForReplace()
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
|
||||
public override void OnSelected(SelectedEventArgs e)
|
||||
{
|
||||
var tb = Parent.TargetControlWrapper;
|
||||
//
|
||||
if (!Text.Contains("^"))
|
||||
return;
|
||||
var text = tb.Text;
|
||||
for (int i = Parent.Fragment.Start; i < text.Length; i++)
|
||||
if (text[i] == '^')
|
||||
{
|
||||
tb.SelectionStart = i;
|
||||
tb.SelectionLength = 1;
|
||||
tb.SelectedText = "";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares fragment text with this item
|
||||
/// </summary>
|
||||
public override CompareResult Compare(string fragmentText)
|
||||
{
|
||||
if (Text.StartsWith(fragmentText, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
Text != fragmentText)
|
||||
return CompareResult.Visible;
|
||||
|
||||
return CompareResult.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class finds items by substring
|
||||
/// </summary>
|
||||
public class SubstringAutocompleteItem : AutocompleteItem
|
||||
{
|
||||
protected readonly string lowercaseText;
|
||||
protected readonly bool ignoreCase;
|
||||
|
||||
public SubstringAutocompleteItem(string text, bool ignoreCase = true)
|
||||
: base(text)
|
||||
{
|
||||
this.ignoreCase = ignoreCase;
|
||||
if(ignoreCase)
|
||||
lowercaseText = text.ToLower();
|
||||
}
|
||||
|
||||
public override CompareResult Compare(string fragmentText)
|
||||
{
|
||||
if(ignoreCase)
|
||||
{
|
||||
if (lowercaseText.Contains(fragmentText.ToLower()))
|
||||
return CompareResult.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Text.Contains(fragmentText))
|
||||
return CompareResult.Visible;
|
||||
}
|
||||
|
||||
return CompareResult.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This item draws multicolumn menu
|
||||
/// </summary>
|
||||
public class MulticolumnAutocompleteItem : SubstringAutocompleteItem
|
||||
{
|
||||
public bool CompareBySubstring { get; set; }
|
||||
public string[] MenuTextByColumns { get; set; }
|
||||
public int[] ColumnWidth { get; set; }
|
||||
public StringAlignment[] Alignments { get; set; }
|
||||
|
||||
public MulticolumnAutocompleteItem(string[] menuTextByColumns, string insertingText, bool compareBySubstring = true, bool ignoreCase = true)
|
||||
: base(insertingText, ignoreCase)
|
||||
{
|
||||
this.CompareBySubstring = compareBySubstring;
|
||||
this.MenuTextByColumns = menuTextByColumns;
|
||||
var tmp = new List<StringAlignment>();
|
||||
for(int i = 0; i < menuTextByColumns.Length; i++)
|
||||
tmp.Add(StringAlignment.Near);
|
||||
this.Alignments = tmp.ToArray();
|
||||
}
|
||||
|
||||
public override CompareResult Compare(string fragmentText)
|
||||
{
|
||||
if (CompareBySubstring)
|
||||
return base.Compare(fragmentText);
|
||||
|
||||
if(ignoreCase)
|
||||
{
|
||||
if (Text.StartsWith(fragmentText, StringComparison.InvariantCultureIgnoreCase))
|
||||
return CompareResult.VisibleAndSelected;
|
||||
} else
|
||||
if (Text.StartsWith(fragmentText))
|
||||
return CompareResult.VisibleAndSelected;
|
||||
|
||||
return CompareResult.Hidden;
|
||||
}
|
||||
|
||||
public override void OnPaint(PaintItemEventArgs e)
|
||||
{
|
||||
if (ColumnWidth != null && ColumnWidth.Length != MenuTextByColumns.Length)
|
||||
throw new Exception("ColumnWidth.Length != MenuTextByColumns.Length");
|
||||
|
||||
int[] columnWidth = ColumnWidth;
|
||||
if(columnWidth == null)
|
||||
{
|
||||
columnWidth = new int[MenuTextByColumns.Length];
|
||||
float step = e.TextRect.Width/MenuTextByColumns.Length;
|
||||
for (int i = 0; i < MenuTextByColumns.Length; i++)
|
||||
columnWidth[i] = (int)step;
|
||||
}
|
||||
|
||||
//draw columns
|
||||
Pen pen = Pens.Silver;
|
||||
float x = e.TextRect.X;
|
||||
e.StringFormat.FormatFlags = e.StringFormat.FormatFlags | StringFormatFlags.NoWrap;
|
||||
|
||||
using (var brush = new SolidBrush(e.IsSelected ? e.Colors.SelectedForeColor : e.Colors.ForeColor))
|
||||
for (int i=0;i<MenuTextByColumns.Length;i++)
|
||||
{
|
||||
var width = columnWidth[i];
|
||||
var rect = new RectangleF(x, e.TextRect.Top, width, e.TextRect.Height);
|
||||
e.Graphics.DrawLine(pen, new PointF(x, e.TextRect.Top), new PointF(x, e.TextRect.Bottom));
|
||||
e.Graphics.DrawString(MenuTextByColumns[i], e.Font, brush, rect, new StringFormat() {Alignment = Alignments[i]});
|
||||
x += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
[System.ComponentModel.ToolboxItem(false)]
|
||||
public class AutocompleteListView : UserControl, IAutocompleteListView
|
||||
{
|
||||
private readonly ToolTip toolTip = new ToolTip();
|
||||
public int HighlightedItemIndex { get; set; }
|
||||
private int oldItemCount;
|
||||
private int selectedItemIndex = -1;
|
||||
private IList<AutocompleteItem> visibleItems;
|
||||
|
||||
/// <summary>
|
||||
/// Duration (ms) of tooltip showing
|
||||
/// </summary>
|
||||
public int ToolTipDuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when user selected item for inserting into text
|
||||
/// </summary>
|
||||
public event EventHandler ItemSelected;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when current hovered item is changing
|
||||
/// </summary>
|
||||
public event EventHandler<HoveredEventArgs> ItemHovered;
|
||||
|
||||
/// <summary>
|
||||
/// Colors
|
||||
/// </summary>
|
||||
public Colors Colors { get; set; }
|
||||
|
||||
internal AutocompleteListView()
|
||||
{
|
||||
SetStyle(
|
||||
ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
|
||||
base.Font = new Font(FontFamily.GenericSansSerif, 11);
|
||||
ItemHeight = Font.Height + 2;
|
||||
VerticalScroll.SmallChange = ItemHeight;
|
||||
BackColor = Color.White;
|
||||
LeftPadding = 18;
|
||||
ToolTipDuration = 3000;
|
||||
Colors = new Colors();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
toolTip.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
||||
private int itemHeight;
|
||||
|
||||
public int ItemHeight
|
||||
{
|
||||
get { return itemHeight; }
|
||||
set {
|
||||
itemHeight = value;
|
||||
VerticalScroll.SmallChange = value;
|
||||
oldItemCount = -1;
|
||||
AdjustScroll();
|
||||
}
|
||||
}
|
||||
|
||||
public override Font Font
|
||||
{
|
||||
get { return base.Font; }
|
||||
set
|
||||
{
|
||||
base.Font = value;
|
||||
ItemHeight = Font.Height + 2;
|
||||
}
|
||||
}
|
||||
|
||||
public int LeftPadding { get; set; }
|
||||
|
||||
public ImageList ImageList { get; set; }
|
||||
|
||||
public string[] ColumnsTitle { get; set; } = null;
|
||||
public int[] ColumnsWidth { get; set; } = null;
|
||||
|
||||
public IList<AutocompleteItem> VisibleItems
|
||||
{
|
||||
get { return visibleItems; }
|
||||
set
|
||||
{
|
||||
visibleItems = value;
|
||||
SelectedItemIndex = -1;
|
||||
AdjustScroll();
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public int SelectedItemIndex
|
||||
{
|
||||
get { return selectedItemIndex; }
|
||||
set
|
||||
{
|
||||
AutocompleteItem item = null;
|
||||
if (value >= 0 && value < VisibleItems.Count)
|
||||
item = VisibleItems[value];
|
||||
|
||||
selectedItemIndex = value;
|
||||
|
||||
OnItemHovered(new HoveredEventArgs() { Item = item });
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
ShowToolTip(item, this.Parent);
|
||||
ScrollToSelected();
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemHovered(HoveredEventArgs e)
|
||||
{
|
||||
if (ItemHovered != null)
|
||||
ItemHovered(this, e);
|
||||
}
|
||||
|
||||
private void AdjustScroll()
|
||||
{
|
||||
if (VisibleItems == null)
|
||||
return;
|
||||
if (oldItemCount == VisibleItems.Count)
|
||||
return;
|
||||
|
||||
int needHeight = ItemHeight*VisibleItems.Count + 1;
|
||||
if (HasColumn) needHeight += ItemHeight;
|
||||
Height = Math.Min(needHeight, MaximumSize.Height);
|
||||
AutoScrollMinSize = new Size(0, needHeight);
|
||||
oldItemCount = VisibleItems.Count;
|
||||
}
|
||||
|
||||
|
||||
private void ScrollToSelected()
|
||||
{
|
||||
int y;
|
||||
if(HasColumn)
|
||||
y = (SelectedItemIndex + 1)*ItemHeight - VerticalScroll.Value;
|
||||
else
|
||||
y = SelectedItemIndex * ItemHeight - VerticalScroll.Value;
|
||||
//if (HasColumn) y += ItemHeight;
|
||||
|
||||
if (y < 0)
|
||||
if(HasColumn)
|
||||
VerticalScroll.Value = (SelectedItemIndex + 1) * ItemHeight;
|
||||
else
|
||||
VerticalScroll.Value = SelectedItemIndex * ItemHeight;
|
||||
if (y > ClientSize.Height - ItemHeight)
|
||||
VerticalScroll.Value = Math.Min(VerticalScroll.Maximum,
|
||||
(HasColumn ? (SelectedItemIndex*ItemHeight) + ItemHeight : SelectedItemIndex * ItemHeight) - ClientSize.Height + ItemHeight);
|
||||
|
||||
//Show headers if index 0 is selected.
|
||||
if (HasColumn && SelectedItemIndex == 0)
|
||||
VerticalScroll.Value = 0;
|
||||
|
||||
//some magic for update scrolls
|
||||
AutoScrollMinSize -= new Size(1, 0);
|
||||
AutoScrollMinSize += new Size(1, 0);
|
||||
}
|
||||
|
||||
public Rectangle GetItemRectangle(int itemIndex)
|
||||
{
|
||||
var y = itemIndex * ItemHeight - VerticalScroll.Value;
|
||||
return new Rectangle(0, y, ClientSize.Width - 1, ItemHeight - 1);
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs e)
|
||||
{
|
||||
e.Graphics.Clear(Colors.BackColor);
|
||||
}
|
||||
|
||||
private bool HasColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return ColumnsTitle != null && ColumnsTitle.Length != 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
bool rtl = RightToLeft == RightToLeft.Yes;
|
||||
AdjustScroll();
|
||||
int startI = VerticalScroll.Value/ItemHeight - 1;
|
||||
int finishI = (VerticalScroll.Value + ClientSize.Height)/ItemHeight + 1;
|
||||
int y = 0;
|
||||
|
||||
var sf = new StringFormat();
|
||||
if (rtl)
|
||||
sf.FormatFlags = StringFormatFlags.DirectionRightToLeft;
|
||||
|
||||
if (HasColumn && VerticalScroll.Value == 0)
|
||||
{
|
||||
var textRect = new Rectangle(LeftPadding, y, ClientSize.Width - 1 - LeftPadding, ItemHeight - 1);
|
||||
if (rtl)
|
||||
textRect = new Rectangle(1, y, ClientSize.Width - 1 - LeftPadding, ItemHeight - 1);
|
||||
|
||||
int[] columnWidth = ColumnsWidth;
|
||||
if (columnWidth == null)
|
||||
{
|
||||
columnWidth = new int[ColumnsTitle.Length];
|
||||
float step = textRect.Width / ColumnsTitle.Length;
|
||||
for (int i = 0; i < ColumnsTitle.Length; i++)
|
||||
columnWidth[i] = (int)step;
|
||||
}
|
||||
|
||||
float x = textRect.X;
|
||||
sf.Alignment = StringAlignment.Center;
|
||||
for (int i = 0; i < ColumnsTitle.Length; i++)
|
||||
{
|
||||
using (var brush = new SolidBrush(Colors.ForeColor))
|
||||
{
|
||||
var width = columnWidth[i];
|
||||
var rect = new RectangleF(x, textRect.Top, width, textRect.Height);
|
||||
e.Graphics.DrawLine(Pens.Silver, new PointF(x, textRect.Top), new PointF(x, textRect.Bottom));
|
||||
e.Graphics.DrawString(ColumnsTitle[i], Font, brush, rect, sf);
|
||||
x += width;
|
||||
}
|
||||
}
|
||||
if (rtl)
|
||||
sf.FormatFlags = StringFormatFlags.DirectionRightToLeft;
|
||||
else
|
||||
sf = new StringFormat();
|
||||
}
|
||||
|
||||
startI = Math.Max(startI, 0);
|
||||
finishI = Math.Min(finishI, VisibleItems.Count);
|
||||
|
||||
for (int i = startI; i < finishI; i++)
|
||||
{
|
||||
if(HasColumn)
|
||||
y = (i * ItemHeight - VerticalScroll.Value) + ItemHeight;
|
||||
else
|
||||
y = i * ItemHeight - VerticalScroll.Value;
|
||||
|
||||
if (ImageList != null && VisibleItems[i].ImageIndex >= 0)
|
||||
if (rtl)
|
||||
e.Graphics.DrawImage(ImageList.Images[VisibleItems[i].ImageIndex], Width - 1 - LeftPadding, y);
|
||||
else
|
||||
e.Graphics.DrawImage(ImageList.Images[VisibleItems[i].ImageIndex], 1, y);
|
||||
|
||||
var textRect = new Rectangle(LeftPadding, y, ClientSize.Width - 1 - LeftPadding, ItemHeight - 1);
|
||||
if (rtl)
|
||||
textRect = new Rectangle(1, y, ClientSize.Width - 1 - LeftPadding, ItemHeight - 1);
|
||||
|
||||
if (i == SelectedItemIndex)
|
||||
{
|
||||
Brush selectedBrush = new LinearGradientBrush(new Point(0, y - 3), new Point(0, y + ItemHeight),
|
||||
Colors.SelectedBackColor2, Colors.SelectedBackColor);
|
||||
e.Graphics.FillRectangle(selectedBrush, textRect);
|
||||
using(var pen = new Pen(Colors.SelectedBackColor2))
|
||||
e.Graphics.DrawRectangle(pen, textRect);
|
||||
}
|
||||
|
||||
if (i == HighlightedItemIndex)
|
||||
using (var pen = new Pen(Colors.HighlightingColor))
|
||||
e.Graphics.DrawRectangle(pen, textRect);
|
||||
|
||||
var args = new PaintItemEventArgs(e.Graphics, e.ClipRectangle)
|
||||
{
|
||||
Font = Font,
|
||||
TextRect = new RectangleF(textRect.Location, textRect.Size),
|
||||
StringFormat = sf,
|
||||
IsSelected = i == SelectedItemIndex,
|
||||
IsHovered = i == HighlightedItemIndex,
|
||||
Colors = Colors
|
||||
};
|
||||
//call drawing
|
||||
VisibleItems[i].OnPaint(args);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnScroll(ScrollEventArgs se)
|
||||
{
|
||||
base.OnScroll(se);
|
||||
Invalidate(true);
|
||||
}
|
||||
|
||||
protected override void OnMouseClick(MouseEventArgs e)
|
||||
{
|
||||
int i = PointToItemIndex(e.Location);
|
||||
if (i >= 0)
|
||||
{
|
||||
base.OnMouseClick(e);
|
||||
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
SelectedItemIndex = i;
|
||||
ScrollToSelected();
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Point mouseEnterPoint;
|
||||
|
||||
protected override void OnMouseEnter(EventArgs e)
|
||||
{
|
||||
base.OnMouseEnter(e);
|
||||
mouseEnterPoint = Control.MousePosition;
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
if (mouseEnterPoint != Control.MousePosition)
|
||||
{
|
||||
int i = PointToItemIndex(e.Location);
|
||||
if (i >= 0)
|
||||
{
|
||||
HighlightedItemIndex = i;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseDoubleClick(MouseEventArgs e)
|
||||
{
|
||||
int i = PointToItemIndex(e.Location);
|
||||
if (i >= 0)
|
||||
{
|
||||
base.OnMouseDoubleClick(e);
|
||||
SelectedItemIndex = i;
|
||||
Invalidate();
|
||||
OnItemSelected();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemSelected()
|
||||
{
|
||||
if (ItemSelected != null)
|
||||
ItemSelected(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
private int PointToItemIndex(Point p)
|
||||
{
|
||||
var t = (p.Y + VerticalScroll.Value);
|
||||
if (HasColumn)
|
||||
t -= ItemHeight;
|
||||
return (float)t / ItemHeight < 0 ? -1 : t / ItemHeight;
|
||||
}
|
||||
|
||||
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||
{
|
||||
var host = Parent as AutocompleteMenuHost;
|
||||
if (host != null)
|
||||
if (host.Menu.ProcessKey((char) keyData, Keys.None))
|
||||
return true;
|
||||
|
||||
return base.ProcessCmdKey(ref msg, keyData);
|
||||
}
|
||||
|
||||
public void SelectItem(int itemIndex)
|
||||
{
|
||||
SelectedItemIndex = itemIndex;
|
||||
ScrollToSelected();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void SetItems(List<AutocompleteItem> items)
|
||||
{
|
||||
VisibleItems = items;
|
||||
SelectedItemIndex = -1;
|
||||
AdjustScroll();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void ShowToolTip(AutocompleteItem autocompleteItem, Control control = null)
|
||||
{
|
||||
string title = autocompleteItem.ToolTipTitle;
|
||||
string text = autocompleteItem.ToolTipText;
|
||||
if (control == null)
|
||||
control = this;
|
||||
|
||||
if (string.IsNullOrEmpty(title))
|
||||
{
|
||||
toolTip.ToolTipTitle = null;
|
||||
toolTip.SetToolTip(control, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
toolTip.ToolTipTitle = null;
|
||||
toolTip.Show(title, control, Width + 3, 0, ToolTipDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
toolTip.ToolTipTitle = title;
|
||||
toolTip.Show(text, control, Width + 3, 0, ToolTipDuration);
|
||||
}
|
||||
}
|
||||
|
||||
public void HideToolTip(Control control)
|
||||
{
|
||||
toolTip.Hide(control);
|
||||
}
|
||||
|
||||
public Control GetParentControl()
|
||||
{
|
||||
return this.Parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,903 @@
|
||||
//
|
||||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
||||
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
||||
// PURPOSE.
|
||||
//
|
||||
// License: GNU Lesser General Public License (LGPLv3)
|
||||
//
|
||||
// Email: p_torgashov@ukr.net.
|
||||
//
|
||||
// Copyright (C) Pavel Torgashov, 2012-2015.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
[ProvideProperty("AutocompleteMenu", typeof(Control))]
|
||||
public class AutocompleteMenu : Component, IExtenderProvider
|
||||
{
|
||||
private static readonly Dictionary<Control, AutocompleteMenu> AutocompleteMenuByControls =
|
||||
new Dictionary<Control, AutocompleteMenu>();
|
||||
private static readonly Dictionary<Control, ITextBoxWrapper> WrapperByControls =
|
||||
new Dictionary<Control, ITextBoxWrapper>();
|
||||
|
||||
private ITextBoxWrapper targetControlWrapper;
|
||||
private readonly Timer timer = new Timer();
|
||||
|
||||
private IEnumerable<AutocompleteItem> sourceItems = new List<AutocompleteItem>();
|
||||
[Browsable(false)]
|
||||
public IList<AutocompleteItem> VisibleItems { get { return Host.ListView.VisibleItems; } private set { Host.ListView.VisibleItems = value;} }
|
||||
private Size maximumSize;
|
||||
|
||||
/// <summary>
|
||||
/// Duration (ms) of tooltip showing
|
||||
/// </summary>
|
||||
[Description("Duration (ms) of tooltip showing")]
|
||||
[DefaultValue(3000)]
|
||||
public int ToolTipDuration
|
||||
{
|
||||
get { return Host.ListView.ToolTipDuration; }
|
||||
set { Host.ListView.ToolTipDuration = value; }
|
||||
}
|
||||
|
||||
public AutocompleteMenu()
|
||||
{
|
||||
Host = new AutocompleteMenuHost(this);
|
||||
Host.ListView.ItemSelected += new EventHandler(ListView_ItemSelected);
|
||||
Host.ListView.ItemHovered += new EventHandler<HoveredEventArgs>(ListView_ItemHovered);
|
||||
VisibleItems = new List<AutocompleteItem>();
|
||||
Enabled = true;
|
||||
AppearInterval = 500;
|
||||
timer.Tick += timer_Tick;
|
||||
MaximumSize = new Size(180, 200);
|
||||
AutoPopup = true;
|
||||
|
||||
SearchPattern = @"[\w\.]";
|
||||
MinFragmentLength = 2;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
timer.Dispose();
|
||||
Host.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
void ListView_ItemSelected(object sender, EventArgs e)
|
||||
{
|
||||
OnSelecting();
|
||||
}
|
||||
|
||||
void ListView_ItemHovered(object sender, HoveredEventArgs e)
|
||||
{
|
||||
OnHovered(e);
|
||||
}
|
||||
|
||||
public void OnHovered(HoveredEventArgs e)
|
||||
{
|
||||
if (Hovered != null)
|
||||
Hovered(this, e);
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public int SelectedItemIndex { get { return Host.ListView.SelectedItemIndex; }
|
||||
internal set { Host.ListView.SelectedItemIndex = value; }
|
||||
}
|
||||
|
||||
internal AutocompleteMenuHost Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when user selected the control and needed wrapper over it.
|
||||
/// You can assign own Wrapper for target control.
|
||||
/// </summary>
|
||||
[Description("Called when user selected the control and needed wrapper over it. You can assign own Wrapper for target control.")]
|
||||
public event EventHandler<WrapperNeededEventArgs> WrapperNeeded;
|
||||
|
||||
protected void OnWrapperNeeded(WrapperNeededEventArgs args)
|
||||
{
|
||||
if (WrapperNeeded != null)
|
||||
WrapperNeeded(this, args);
|
||||
if (args.Wrapper == null)
|
||||
args.Wrapper = TextBoxWrapper.Create(args.TargetControl);
|
||||
}
|
||||
|
||||
ITextBoxWrapper CreateWrapper(Control control)
|
||||
{
|
||||
if (WrapperByControls.ContainsKey(control))
|
||||
return WrapperByControls[control];
|
||||
|
||||
var args = new WrapperNeededEventArgs(control);
|
||||
OnWrapperNeeded(args);
|
||||
if (args.Wrapper != null)
|
||||
WrapperByControls[control] = args.Wrapper;
|
||||
|
||||
return args.Wrapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current target control wrapper
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public ITextBoxWrapper TargetControlWrapper
|
||||
{
|
||||
get { return targetControlWrapper; }
|
||||
set {
|
||||
targetControlWrapper = value;
|
||||
if (value != null && !WrapperByControls.ContainsKey(value.TargetControl))
|
||||
{
|
||||
WrapperByControls[value.TargetControl] = value;
|
||||
SetAutocompleteMenu(value.TargetControl, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum size of popup menu
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(Size), "180, 200")]
|
||||
[Description("Maximum size of popup menu")]
|
||||
public Size MaximumSize
|
||||
{
|
||||
get { return maximumSize; }
|
||||
set {
|
||||
maximumSize = value;
|
||||
(Host.ListView as Control).MaximumSize = maximumSize;
|
||||
(Host.ListView as Control).Size = maximumSize;
|
||||
Host.CalcSize();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Font
|
||||
/// </summary>
|
||||
public Font Font
|
||||
{
|
||||
get { return (Host.ListView as Control).Font; }
|
||||
set { (Host.ListView as Control).Font = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left padding of text
|
||||
/// </summary>
|
||||
[DefaultValue(18)]
|
||||
[Description("Left padding of text")]
|
||||
public int LeftPadding
|
||||
{
|
||||
get {
|
||||
if (Host.ListView is AutocompleteListView)
|
||||
return (Host.ListView as AutocompleteListView).LeftPadding;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
set {
|
||||
if (Host.ListView is AutocompleteListView)
|
||||
(Host.ListView as AutocompleteListView).LeftPadding = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Colors of foreground and background
|
||||
/// </summary>
|
||||
[Browsable(true)]
|
||||
[Description("Colors of foreground and background.")]
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public Colors Colors
|
||||
{
|
||||
get { return (Host.ListView as IAutocompleteListView).Colors; }
|
||||
set { (Host.ListView as IAutocompleteListView).Colors = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AutocompleteMenu will popup automatically (when user writes text). Otherwise it will popup only programmatically or by Ctrl-Space.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[Description("AutocompleteMenu will popup automatically (when user writes text). Otherwise it will popup only programmatically or by Ctrl-Space.")]
|
||||
public bool AutoPopup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AutocompleteMenu will capture focus when opening.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[Description("AutocompleteMenu will capture focus when opening.")]
|
||||
public bool CaptureFocus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the component should draw right-to-left for RTL languages.
|
||||
/// </summary>
|
||||
[DefaultValue(typeof(RightToLeft), "No")]
|
||||
[Description("Indicates whether the component should draw right-to-left for RTL languages.")]
|
||||
public RightToLeft RightToLeft {
|
||||
get { return Host.RightToLeft; }
|
||||
set { Host.RightToLeft = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image list
|
||||
/// </summary>
|
||||
public ImageList ImageList {
|
||||
get { return Host.ListView.ImageList; }
|
||||
set { Host.ListView.ImageList = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fragment
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Range Fragment { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Regex pattern for serach fragment around caret
|
||||
/// </summary>
|
||||
[Description("Regex pattern for serach fragment around caret")]
|
||||
[DefaultValue(@"[\w\.]")]
|
||||
public string SearchPattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum fragment length for popup
|
||||
/// </summary>
|
||||
[Description("Minimum fragment length for popup")]
|
||||
[DefaultValue(2)]
|
||||
public int MinFragmentLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows TAB for select menu item
|
||||
/// </summary>
|
||||
[Description("Allows TAB for select menu item")]
|
||||
[DefaultValue(false)]
|
||||
public bool AllowsTabKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Interval of menu appear (ms)
|
||||
/// </summary>
|
||||
[Description("Interval of menu appear (ms)")]
|
||||
[DefaultValue(500)]
|
||||
public int AppearInterval { get; set; }
|
||||
|
||||
[DefaultValue(null)]
|
||||
public string[] Items
|
||||
{
|
||||
get
|
||||
{
|
||||
if (sourceItems == null)
|
||||
return null;
|
||||
var list = new List<string>();
|
||||
foreach (AutocompleteItem item in sourceItems)
|
||||
list.Add(item.ToString());
|
||||
return list.ToArray();
|
||||
}
|
||||
set { SetAutocompleteItems(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The control for menu displaying.
|
||||
/// Set to null for restore default ListView (AutocompleteListView).
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public IAutocompleteListView ListView
|
||||
{
|
||||
get { return Host.ListView; }
|
||||
set
|
||||
{
|
||||
if (ListView != null)
|
||||
{
|
||||
var ctrl = value as Control;
|
||||
value.ImageList = ImageList;
|
||||
ctrl.RightToLeft = RightToLeft;
|
||||
ctrl.Font = Font;
|
||||
ctrl.MaximumSize = MaximumSize;
|
||||
}
|
||||
Host.ListView = value;
|
||||
Host.ListView.ItemSelected += new EventHandler(ListView_ItemSelected);
|
||||
Host.ListView.ItemHovered += new EventHandler<HoveredEventArgs>(ListView_ItemHovered);
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultValue(true)]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates size of the menu
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
Host.CalcSize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns rectangle of item
|
||||
/// </summary>
|
||||
public Rectangle GetItemRectangle(int itemIndex)
|
||||
{
|
||||
return Host.ListView.GetItemRectangle(itemIndex);
|
||||
}
|
||||
|
||||
#region IExtenderProvider Members
|
||||
|
||||
bool IExtenderProvider.CanExtend(object extendee)
|
||||
{
|
||||
//find AutocompleteMenu with lowest hashcode
|
||||
if (Container != null)
|
||||
foreach (object comp in Container.Components)
|
||||
if (comp is AutocompleteMenu)
|
||||
if (comp.GetHashCode() < GetHashCode())
|
||||
return false;
|
||||
//we are main autocomplete menu on form ...
|
||||
//check extendee as TextBox
|
||||
if (!(extendee is Control))
|
||||
return false;
|
||||
var temp = TextBoxWrapper.Create(extendee as Control);
|
||||
return temp!=null;
|
||||
}
|
||||
|
||||
public void SetAutocompleteMenu(Control control, AutocompleteMenu menu)
|
||||
{
|
||||
if (menu != null)
|
||||
{
|
||||
if (WrapperByControls.ContainsKey(control))
|
||||
{
|
||||
var wrapper = WrapperByControls[control];
|
||||
if (wrapper == null) return;
|
||||
//
|
||||
if (control.IsHandleCreated)
|
||||
menu.SubscribeForm(wrapper);
|
||||
else
|
||||
control.HandleCreated += (o, e) => menu.SubscribeForm(wrapper);
|
||||
//
|
||||
AutocompleteMenuByControls[control] = this;
|
||||
//
|
||||
wrapper.LostFocus += menu.control_LostFocus;
|
||||
wrapper.Scroll += menu.control_Scroll;
|
||||
wrapper.KeyDown += menu.control_KeyDown;
|
||||
wrapper.MouseDown += menu.control_MouseDown;
|
||||
}
|
||||
else
|
||||
{
|
||||
var wrapper = menu.CreateWrapper(control);
|
||||
if (wrapper == null) return;
|
||||
//
|
||||
if (control.IsHandleCreated)
|
||||
menu.SubscribeForm(wrapper);
|
||||
else
|
||||
control.HandleCreated += (o, e) => menu.SubscribeForm(wrapper);
|
||||
//
|
||||
AutocompleteMenuByControls[control] = this;
|
||||
//
|
||||
wrapper.LostFocus += menu.control_LostFocus;
|
||||
wrapper.Scroll += menu.control_Scroll;
|
||||
wrapper.KeyDown += menu.control_KeyDown;
|
||||
wrapper.MouseDown += menu.control_MouseDown;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AutocompleteMenuByControls.TryGetValue(control, out menu);
|
||||
AutocompleteMenuByControls.Remove(control);
|
||||
ITextBoxWrapper wrapper = null;
|
||||
WrapperByControls.TryGetValue(control, out wrapper);
|
||||
WrapperByControls.Remove(control);
|
||||
if (wrapper != null && menu != null)
|
||||
{
|
||||
wrapper.LostFocus -= menu.control_LostFocus;
|
||||
wrapper.Scroll -= menu.control_Scroll;
|
||||
wrapper.KeyDown -= menu.control_KeyDown;
|
||||
wrapper.MouseDown -= menu.control_MouseDown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// User selects item
|
||||
/// </summary>
|
||||
[Description("Occurs when user selects item.")]
|
||||
public event EventHandler<SelectingEventArgs> Selecting;
|
||||
|
||||
/// <summary>
|
||||
/// It fires after item was inserting
|
||||
/// </summary>
|
||||
[Description("Occurs after user selected item.")]
|
||||
public event EventHandler<SelectedEventArgs> Selected;
|
||||
|
||||
/// <summary>
|
||||
/// It fires when item was hovered
|
||||
/// </summary>
|
||||
[Description("Occurs when user hovered item.")]
|
||||
public event EventHandler<HoveredEventArgs> Hovered;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when popup menu is opening
|
||||
/// </summary>
|
||||
public event EventHandler<CancelEventArgs> Opening;
|
||||
|
||||
private void timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
timer.Stop();
|
||||
if(TargetControlWrapper!=null)
|
||||
ShowAutocomplete(false);
|
||||
}
|
||||
|
||||
private Form myForm;
|
||||
|
||||
void SubscribeForm(ITextBoxWrapper wrapper)
|
||||
{
|
||||
if (wrapper == null) return;
|
||||
var form = wrapper.TargetControl.FindForm();
|
||||
if (form == null) return;
|
||||
if (myForm != null)
|
||||
{
|
||||
if (myForm == form)
|
||||
return;
|
||||
UnsubscribeForm(wrapper);
|
||||
}
|
||||
|
||||
myForm = form;
|
||||
|
||||
form.LocationChanged += new EventHandler(form_LocationChanged);
|
||||
form.ResizeBegin += new EventHandler(form_LocationChanged);
|
||||
form.FormClosing += new FormClosingEventHandler(form_FormClosing);
|
||||
form.LostFocus += new EventHandler(form_LocationChanged);
|
||||
}
|
||||
|
||||
void UnsubscribeForm(ITextBoxWrapper wrapper)
|
||||
{
|
||||
if (wrapper == null) return;
|
||||
var form = wrapper.TargetControl.FindForm();
|
||||
if (form == null) return;
|
||||
|
||||
form.LocationChanged -= new EventHandler(form_LocationChanged);
|
||||
form.ResizeBegin -= new EventHandler(form_LocationChanged);
|
||||
form.FormClosing -= new FormClosingEventHandler(form_FormClosing);
|
||||
form.LostFocus -= new EventHandler(form_LocationChanged);
|
||||
}
|
||||
|
||||
private void form_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void form_LocationChanged(object sender, EventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void control_MouseDown(object sender, MouseEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
ITextBoxWrapper FindWrapper(Control sender)
|
||||
{
|
||||
while (sender != null)
|
||||
{
|
||||
if (WrapperByControls.ContainsKey(sender))
|
||||
return WrapperByControls[sender];
|
||||
|
||||
sender = sender.Parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void control_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
TargetControlWrapper = FindWrapper(sender as Control);
|
||||
|
||||
bool backspaceORdel = e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete;
|
||||
|
||||
if (Host.Visible)
|
||||
{
|
||||
if (ProcessKey((char)e.KeyCode, Control.ModifierKeys))
|
||||
e.SuppressKeyPress = true;
|
||||
else
|
||||
if (!backspaceORdel)
|
||||
ResetTimer(1);
|
||||
else
|
||||
ResetTimer();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Host.Visible)
|
||||
{
|
||||
switch (e.KeyCode)
|
||||
{
|
||||
case Keys.Up:
|
||||
case Keys.Down:
|
||||
case Keys.PageUp:
|
||||
case Keys.PageDown:
|
||||
case Keys.Left:
|
||||
case Keys.Right:
|
||||
case Keys.End:
|
||||
case Keys.Home:
|
||||
case Keys.ControlKey:
|
||||
case Keys.Escape:
|
||||
case Keys.Tab:
|
||||
{
|
||||
timer.Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Control.ModifierKeys== Keys.Alt)
|
||||
{
|
||||
timer.Stop();
|
||||
return;
|
||||
}
|
||||
if (Control.ModifierKeys == Keys.Control && e.KeyCode == Keys.Space)
|
||||
{
|
||||
ShowAutocomplete(true);
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResetTimer();
|
||||
}
|
||||
|
||||
void ResetTimer()
|
||||
{
|
||||
ResetTimer(-1);
|
||||
}
|
||||
|
||||
void ResetTimer(int interval)
|
||||
{
|
||||
if (interval <= 0)
|
||||
timer.Interval = AppearInterval;
|
||||
else
|
||||
timer.Interval = interval;
|
||||
timer.Stop();
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void control_Scroll(object sender, ScrollEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void control_LostFocus(object sender, EventArgs e)
|
||||
{
|
||||
if (!Host.Focused) Close();
|
||||
}
|
||||
|
||||
public AutocompleteMenu GetAutocompleteMenu(Control control)
|
||||
{
|
||||
if (AutocompleteMenuByControls.ContainsKey(control))
|
||||
return AutocompleteMenuByControls[control];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
bool forcedOpened = false;
|
||||
|
||||
internal void ShowAutocomplete(bool forced)
|
||||
{
|
||||
if (forced)
|
||||
forcedOpened = true;
|
||||
|
||||
if (TargetControlWrapper != null && TargetControlWrapper.Readonly)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Enabled)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!forcedOpened && !AutoPopup)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
//build list
|
||||
BuildAutocompleteList(forcedOpened);
|
||||
|
||||
//show popup menu
|
||||
if (VisibleItems.Count > 0)
|
||||
{
|
||||
if (forced && VisibleItems.Count == 1 && Host.ListView.SelectedItemIndex == 0)
|
||||
{
|
||||
//do autocomplete if menu contains only one line and user press CTRL-SPACE
|
||||
OnSelecting();
|
||||
Close();
|
||||
}
|
||||
else
|
||||
ShowMenu();
|
||||
}
|
||||
else
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ShowMenu()
|
||||
{
|
||||
if (!Host.Visible)
|
||||
{
|
||||
var args = new CancelEventArgs();
|
||||
OnOpening(args);
|
||||
if (!args.Cancel)
|
||||
{
|
||||
//calc screen point for popup menu
|
||||
Point point = TargetControlWrapper.TargetControl.Location;
|
||||
point.Offset(2, TargetControlWrapper.TargetControl.Height + 2);
|
||||
point = TargetControlWrapper.GetPositionFromCharIndex(Fragment.Start);
|
||||
point.Offset(2, TargetControlWrapper.TargetControl.Font.Height + 2);
|
||||
//
|
||||
Host.Show(TargetControlWrapper.TargetControl, point);
|
||||
if (CaptureFocus)
|
||||
{
|
||||
(Host.ListView as Control).Focus();
|
||||
//ProcessKey((char) Keys.Down, Keys.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
(Host.ListView as Control).Invalidate();
|
||||
}
|
||||
|
||||
private void BuildAutocompleteList(bool forced)
|
||||
{
|
||||
var visibleItems = new List<AutocompleteItem>();
|
||||
|
||||
bool foundSelected = false;
|
||||
int selectedIndex = -1;
|
||||
//get fragment around caret
|
||||
Range fragment = GetFragment(SearchPattern);
|
||||
string text = fragment.Text;
|
||||
//
|
||||
if (sourceItems != null)
|
||||
if (forced || (text.Length >= MinFragmentLength /* && tb.Selection.Start == tb.Selection.End*/))
|
||||
{
|
||||
Fragment = fragment;
|
||||
//build popup menu
|
||||
foreach (AutocompleteItem item in sourceItems)
|
||||
{
|
||||
item.Parent = this;
|
||||
CompareResult res = item.Compare(text);
|
||||
if (res != CompareResult.Hidden)
|
||||
visibleItems.Add(item);
|
||||
if (res == CompareResult.VisibleAndSelected && !foundSelected)
|
||||
{
|
||||
foundSelected = true;
|
||||
selectedIndex = visibleItems.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
VisibleItems = visibleItems;
|
||||
|
||||
if (foundSelected)
|
||||
Host.ListView.SelectedItemIndex = selectedIndex;
|
||||
else
|
||||
Host.ListView.SelectedItemIndex = 0;
|
||||
|
||||
Host.ListView.HighlightedItemIndex = -1;
|
||||
|
||||
Host.CalcSize();
|
||||
}
|
||||
|
||||
internal void OnOpening(CancelEventArgs args)
|
||||
{
|
||||
if (Opening != null)
|
||||
Opening(this, args);
|
||||
}
|
||||
|
||||
private Range GetFragment(string searchPattern)
|
||||
{
|
||||
var tb = TargetControlWrapper;
|
||||
|
||||
if (tb.SelectionLength > 0) return new Range(tb);
|
||||
|
||||
string text = tb.Text;
|
||||
var regex = new Regex(searchPattern);
|
||||
var result = new Range(tb);
|
||||
|
||||
int startPos = tb.SelectionStart;
|
||||
//go forward
|
||||
int i = startPos;
|
||||
while (i >= 0 && i < text.Length)
|
||||
{
|
||||
if (!regex.IsMatch(text[i].ToString()))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
result.End = i;
|
||||
|
||||
//go backward
|
||||
i = startPos;
|
||||
while (i > 0 && (i - 1) < text.Length)
|
||||
{
|
||||
if (!regex.IsMatch(text[i - 1].ToString()))
|
||||
break;
|
||||
i--;
|
||||
}
|
||||
result.Start = i;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Host.ListView.HideToolTip(Host.ListView.GetParentControl());
|
||||
Host.Close();
|
||||
forcedOpened = false;
|
||||
}
|
||||
|
||||
public void SetAutocompleteItems(IEnumerable<string> items)
|
||||
{
|
||||
var list = new List<AutocompleteItem>();
|
||||
if (items == null)
|
||||
{
|
||||
sourceItems = null;
|
||||
return;
|
||||
}
|
||||
foreach (string item in items)
|
||||
list.Add(new AutocompleteItem(item));
|
||||
SetAutocompleteItems(list);
|
||||
}
|
||||
|
||||
public void SetAutocompleteItems(IEnumerable<AutocompleteItem> items)
|
||||
{
|
||||
sourceItems = items;
|
||||
}
|
||||
|
||||
public void AddItem(string item)
|
||||
{
|
||||
AddItem(new AutocompleteItem(item));
|
||||
}
|
||||
|
||||
public void AddItem(AutocompleteItem item)
|
||||
{
|
||||
if (sourceItems == null)
|
||||
sourceItems = new List<AutocompleteItem>();
|
||||
|
||||
if (sourceItems is IList)
|
||||
(sourceItems as IList).Add(item);
|
||||
else
|
||||
throw new Exception("Current autocomplete items does not support adding");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows popup menu immediately
|
||||
/// </summary>
|
||||
/// <param name="forced">If True - MinFragmentLength will be ignored</param>
|
||||
public void Show(Control control, bool forced)
|
||||
{
|
||||
SetAutocompleteMenu(control, this);
|
||||
this.TargetControlWrapper = FindWrapper(control);
|
||||
ShowAutocomplete(forced);
|
||||
}
|
||||
|
||||
internal virtual void OnSelecting()
|
||||
{
|
||||
if (SelectedItemIndex < 0 || SelectedItemIndex >= VisibleItems.Count)
|
||||
return;
|
||||
|
||||
AutocompleteItem item = VisibleItems[SelectedItemIndex];
|
||||
var args = new SelectingEventArgs
|
||||
{
|
||||
Item = item,
|
||||
SelectedIndex = SelectedItemIndex
|
||||
};
|
||||
|
||||
OnSelecting(args);
|
||||
|
||||
if (args.Cancel)
|
||||
{
|
||||
SelectedItemIndex = args.SelectedIndex;
|
||||
(Host.ListView as Control).Invalidate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.Handled)
|
||||
{
|
||||
Range fragment = Fragment;
|
||||
ApplyAutocomplete(item, fragment);
|
||||
}
|
||||
|
||||
Close();
|
||||
//
|
||||
var args2 = new SelectedEventArgs
|
||||
{
|
||||
Item = item,
|
||||
Control = TargetControlWrapper.TargetControl
|
||||
};
|
||||
item.OnSelected(args2);
|
||||
OnSelected(args2);
|
||||
}
|
||||
|
||||
private void ApplyAutocomplete(AutocompleteItem item, Range fragment)
|
||||
{
|
||||
string newText = item.GetTextForReplace();
|
||||
//replace text of fragment
|
||||
fragment.Text = newText;
|
||||
fragment.TargetWrapper.TargetControl.Focus();
|
||||
}
|
||||
|
||||
internal void OnSelecting(SelectingEventArgs args)
|
||||
{
|
||||
if (Selecting != null)
|
||||
Selecting(this, args);
|
||||
}
|
||||
|
||||
public void OnSelected(SelectedEventArgs args)
|
||||
{
|
||||
if (Selected != null)
|
||||
Selected(this, args);
|
||||
}
|
||||
|
||||
|
||||
public void SetColumns(string[] columns, int[] columnsWidth = null)
|
||||
{
|
||||
ListView.ColumnsTitle = columns;
|
||||
ListView.ColumnsWidth = columnsWidth;
|
||||
}
|
||||
|
||||
public void SelectNext(int shift)
|
||||
{
|
||||
SelectedItemIndex = Math.Max(0, Math.Min(SelectedItemIndex + shift, VisibleItems.Count - 1));
|
||||
//
|
||||
(Host.ListView as Control).Invalidate();
|
||||
}
|
||||
|
||||
public bool ProcessKey(char c, Keys keyModifiers)
|
||||
{
|
||||
var page = Host.Height / (Font.Height + 4);
|
||||
if (keyModifiers == Keys.None)
|
||||
switch ((Keys) c)
|
||||
{
|
||||
case Keys.Down:
|
||||
SelectNext(+1);
|
||||
return true;
|
||||
case Keys.PageDown:
|
||||
SelectNext(+page);
|
||||
return true;
|
||||
case Keys.Up:
|
||||
SelectNext(-1);
|
||||
return true;
|
||||
case Keys.PageUp:
|
||||
SelectNext(-page);
|
||||
return true;
|
||||
case Keys.Enter:
|
||||
OnSelecting();
|
||||
return true;
|
||||
case Keys.Tab:
|
||||
if (!AllowsTabKey)
|
||||
break;
|
||||
OnSelecting();
|
||||
return true;
|
||||
case Keys.Left:
|
||||
case Keys.Right:
|
||||
Close();
|
||||
return false;
|
||||
case Keys.Escape:
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Menu is visible
|
||||
/// </summary>
|
||||
public bool Visible
|
||||
{
|
||||
get { return Host != null && Host.Visible; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
[System.ComponentModel.ToolboxItem(false)]
|
||||
internal class AutocompleteMenuHost : ToolStripDropDown
|
||||
{
|
||||
private IAutocompleteListView listView;
|
||||
public ToolStripControlHost Host { get; set; }
|
||||
public readonly AutocompleteMenu Menu;
|
||||
|
||||
public IAutocompleteListView ListView
|
||||
{
|
||||
get { return listView; }
|
||||
set {
|
||||
|
||||
if(listView != null)
|
||||
(listView as Control).LostFocus -= new EventHandler(ListView_LostFocus);
|
||||
|
||||
if (value == null)
|
||||
listView = new AutocompleteListView();
|
||||
else
|
||||
{
|
||||
if (!(value is Control))
|
||||
throw new Exception("ListView must be derived from Control class");
|
||||
|
||||
listView = value;
|
||||
}
|
||||
|
||||
Host = new ToolStripControlHost(ListView as Control);
|
||||
Host.Margin = new Padding(2, 2, 2, 2);
|
||||
Host.Padding = Padding.Empty;
|
||||
Host.AutoSize = false;
|
||||
Host.AutoToolTip = false;
|
||||
|
||||
(ListView as Control).MaximumSize = Menu.MaximumSize;
|
||||
(ListView as Control).Size = Menu.MaximumSize;
|
||||
(ListView as Control).LostFocus += new EventHandler(ListView_LostFocus);
|
||||
|
||||
CalcSize();
|
||||
base.Items.Clear();
|
||||
base.Items.Add(Host);
|
||||
(ListView as Control).Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
public AutocompleteMenuHost(AutocompleteMenu menu)
|
||||
{
|
||||
AutoClose = false;
|
||||
AutoSize = false;
|
||||
Margin = Padding.Empty;
|
||||
Padding = Padding.Empty;
|
||||
|
||||
Menu = menu;
|
||||
ListView = new AutocompleteListView();
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs e)
|
||||
{
|
||||
using (var brush = new SolidBrush(listView.Colors.BackColor))
|
||||
e.Graphics.FillRectangle(brush, e.ClipRectangle);
|
||||
}
|
||||
|
||||
internal void CalcSize()
|
||||
{
|
||||
Host.Size = (ListView as Control).Size;
|
||||
Size = new System.Drawing.Size((ListView as Control).Size.Width + 4, (ListView as Control).Size.Height + 4);
|
||||
}
|
||||
|
||||
public override RightToLeft RightToLeft
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.RightToLeft;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.RightToLeft = value;
|
||||
(ListView as Control).RightToLeft = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnLostFocus(EventArgs e)
|
||||
{
|
||||
base.OnLostFocus(e);
|
||||
if(!(ListView as Control).Focused)
|
||||
Close();
|
||||
}
|
||||
|
||||
void ListView_LostFocus(object sender, EventArgs e)
|
||||
{
|
||||
if (!Focused)
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
[Serializable]
|
||||
public class Colors
|
||||
{
|
||||
public Color ForeColor { get; set; }
|
||||
public Color BackColor { get; set; }
|
||||
public Color SelectedForeColor { get; set; }
|
||||
public Color SelectedBackColor { get; set; }
|
||||
public Color SelectedBackColor2 { get; set; }
|
||||
public Color HighlightingColor { get; set; }
|
||||
|
||||
public Colors()
|
||||
{
|
||||
ForeColor = Color.Black;
|
||||
BackColor = Color.White;
|
||||
SelectedForeColor = Color.Black;
|
||||
SelectedBackColor = Color.Orange;
|
||||
SelectedBackColor2 = Color.White;
|
||||
HighlightingColor = Color.Orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
public class SelectingEventArgs : EventArgs
|
||||
{
|
||||
public AutocompleteItem Item { get; internal set; }
|
||||
public bool Cancel { get; set; }
|
||||
public int SelectedIndex { get; set; }
|
||||
public bool Handled { get; set; }
|
||||
}
|
||||
|
||||
public class SelectedEventArgs : EventArgs
|
||||
{
|
||||
public AutocompleteItem Item { get; internal set; }
|
||||
public Control Control { get; set; }
|
||||
}
|
||||
|
||||
public class HoveredEventArgs : EventArgs
|
||||
{
|
||||
public AutocompleteItem Item { get; internal set; }
|
||||
}
|
||||
|
||||
|
||||
public class PaintItemEventArgs : PaintEventArgs
|
||||
{
|
||||
public RectangleF TextRect { get; internal set; }
|
||||
public StringFormat StringFormat { get; internal set; }
|
||||
public Font Font { get; internal set; }
|
||||
public bool IsSelected { get; internal set; }
|
||||
public bool IsHovered { get; internal set; }
|
||||
public Colors Colors { get; internal set; }
|
||||
|
||||
public PaintItemEventArgs(Graphics graphics, Rectangle clipRect):base(graphics, clipRect)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class WrapperNeededEventArgs : EventArgs
|
||||
{
|
||||
public Control TargetControl { get; private set; }
|
||||
public ITextBoxWrapper Wrapper { get; set; }
|
||||
|
||||
public WrapperNeededEventArgs(Control targetControl)
|
||||
{
|
||||
this.TargetControl = targetControl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
/// <summary>
|
||||
/// Control for displaying menu items, hosted in AutocompleteMenu.
|
||||
/// </summary>
|
||||
public interface IAutocompleteListView
|
||||
{
|
||||
/// <summary>
|
||||
/// Image list
|
||||
/// </summary>
|
||||
ImageList ImageList { get; set; }
|
||||
|
||||
string[] ColumnsTitle { get; set; }
|
||||
int[] ColumnsWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index of current selected item
|
||||
/// </summary>
|
||||
int SelectedItemIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index of current selected item
|
||||
/// </summary>
|
||||
int HighlightedItemIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of visible elements
|
||||
/// </summary>
|
||||
IList<AutocompleteItem> VisibleItems { get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Duration (ms) of tooltip showing
|
||||
/// </summary>
|
||||
int ToolTipDuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when user selected item for inserting into text
|
||||
/// </summary>
|
||||
event EventHandler ItemSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when current hovered item is changing
|
||||
/// </summary>
|
||||
event EventHandler<HoveredEventArgs> ItemHovered;
|
||||
|
||||
/// <summary>
|
||||
/// Shows tooltip
|
||||
/// </summary>
|
||||
/// <param name="autocompleteItem"></param>
|
||||
/// <param name="control"></param>
|
||||
void ShowToolTip(AutocompleteItem autocompleteItem, Control control = null);
|
||||
|
||||
/// <summary>
|
||||
/// Hides tooltip
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
void HideToolTip(Control control);
|
||||
|
||||
/// <summary>
|
||||
/// Returns rectangle of item
|
||||
/// </summary>
|
||||
Rectangle GetItemRectangle(int itemIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Colors
|
||||
/// </summary>
|
||||
Colors Colors { get; set; }
|
||||
|
||||
|
||||
Control GetParentControl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper over the control like TextBox.
|
||||
/// </summary>
|
||||
public interface ITextBoxWrapper
|
||||
{
|
||||
Control TargetControl { get; }
|
||||
string Text { get; }
|
||||
string SelectedText { get; set; }
|
||||
int SelectionLength { get; set; }
|
||||
int SelectionStart { get; set; }
|
||||
Point GetPositionFromCharIndex(int pos);
|
||||
bool Readonly { get; }
|
||||
event EventHandler LostFocus;
|
||||
event ScrollEventHandler Scroll;
|
||||
event KeyEventHandler KeyDown;
|
||||
event MouseEventHandler MouseDown;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
public class Range
|
||||
{
|
||||
public ITextBoxWrapper TargetWrapper { get; private set; }
|
||||
public int Start { get; set; }
|
||||
public int End { get; set; }
|
||||
|
||||
public Range(ITextBoxWrapper targetWrapper)
|
||||
{
|
||||
this.TargetWrapper = targetWrapper;
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
var text = TargetWrapper.Text;
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return "";
|
||||
if (Start >= text.Length)
|
||||
return "";
|
||||
if (End > text.Length)
|
||||
return "";
|
||||
|
||||
return TargetWrapper.Text.Substring(Start, End - Start);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
TargetWrapper.SelectionStart = Start;
|
||||
TargetWrapper.SelectionLength = End - Start;
|
||||
TargetWrapper.SelectedText = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using AutocompleteMenuNS;
|
||||
using ScintillaNET;
|
||||
|
||||
public class ScintillaWrapper : ITextBoxWrapper
|
||||
{
|
||||
public Scintilla target;
|
||||
|
||||
public ScintillaWrapper(Scintilla trgt)
|
||||
{
|
||||
target = trgt;
|
||||
|
||||
//Now add handler for the UpdateUI event.
|
||||
target.UpdateUI += (sender, args) =>
|
||||
{
|
||||
if (args?.Change == UpdateChange.HScroll || args?.Change == UpdateChange.VScroll)
|
||||
{
|
||||
Scroll?.Invoke(sender, new ScrollEventArgs(ScrollEventType.LargeIncrement, 0));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public bool Readonly
|
||||
{
|
||||
get{ return target.ReadOnly; }
|
||||
}
|
||||
|
||||
public string SelectedText
|
||||
{
|
||||
get { return target.SelectedText; }
|
||||
set
|
||||
{
|
||||
//Store the start of the selection.
|
||||
int start = target.SelectionStart;
|
||||
|
||||
//Delete the current text between selections.
|
||||
target.DeleteRange(target.SelectionStart, (target.SelectionEnd - target.SelectionStart));
|
||||
|
||||
//Add the text in the same postion.
|
||||
target.InsertText(start, value);
|
||||
|
||||
//Clear selection and make sure the caret is at the end.
|
||||
target.SelectionStart = (start + value.Length);
|
||||
target.SelectionEnd = (start + value.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public int SelectionLength
|
||||
{
|
||||
get { return (target.SelectionEnd - target.SelectionStart); }
|
||||
set { target.SelectionEnd = (target.SelectionStart + value); }
|
||||
}
|
||||
|
||||
public int SelectionStart
|
||||
{
|
||||
get { return target.SelectionStart; }
|
||||
set { target.SelectionStart = value; }
|
||||
}
|
||||
|
||||
public Control TargetControl
|
||||
{
|
||||
get { return target; }
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return target.Text; }
|
||||
}
|
||||
|
||||
public Point GetPositionFromCharIndex(int pos)
|
||||
{
|
||||
return new Point(target.PointXFromPosition(pos), target.PointYFromPosition(pos));
|
||||
}
|
||||
|
||||
//Events
|
||||
public virtual event KeyEventHandler KeyDown
|
||||
{
|
||||
add { target.KeyDown += value; }
|
||||
remove { target.KeyDown -= value; }
|
||||
}
|
||||
public virtual event EventHandler LostFocus
|
||||
{
|
||||
add { target.LostFocus += value; }
|
||||
remove { target.LostFocus -= value; }
|
||||
}
|
||||
public virtual event MouseEventHandler MouseDown
|
||||
{
|
||||
add { target.MouseDown += value; }
|
||||
remove { target.MouseDown -= value; }
|
||||
}
|
||||
|
||||
public event ScrollEventHandler Scroll;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AutocompleteMenuNS
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper over the control like TextBox.
|
||||
/// </summary>
|
||||
public class TextBoxWrapper : ITextBoxWrapper
|
||||
{
|
||||
private Control target;
|
||||
private PropertyInfo selectionStart;
|
||||
private PropertyInfo selectionLength;
|
||||
private PropertyInfo selectedText;
|
||||
private PropertyInfo readonlyProperty;
|
||||
private MethodInfo getPositionFromCharIndex;
|
||||
private event ScrollEventHandler RTBScroll;
|
||||
|
||||
private TextBoxWrapper(Control targetControl)
|
||||
{
|
||||
this.target = targetControl;
|
||||
Init();
|
||||
}
|
||||
|
||||
protected virtual void Init()
|
||||
{
|
||||
var t = target.GetType();
|
||||
selectedText = t.GetProperty("SelectedText");
|
||||
selectionLength = t.GetProperty("SelectionLength");
|
||||
selectionStart = t.GetProperty("SelectionStart");
|
||||
readonlyProperty = t.GetProperty("ReadOnly");
|
||||
getPositionFromCharIndex = t.GetMethod("GetPositionFromCharIndex") ?? t.GetMethod("PositionToPoint");
|
||||
|
||||
if (target is RichTextBox)
|
||||
(target as RichTextBox).VScroll += new EventHandler(TextBoxWrapper_VScroll);
|
||||
}
|
||||
|
||||
void TextBoxWrapper_VScroll(object sender, EventArgs e)
|
||||
{
|
||||
if (RTBScroll != null)
|
||||
RTBScroll(sender, new ScrollEventArgs(ScrollEventType.EndScroll, 0, 1));
|
||||
}
|
||||
|
||||
public static TextBoxWrapper Create(Control targetControl)
|
||||
{
|
||||
var result = new TextBoxWrapper(targetControl);
|
||||
|
||||
if (result.selectedText == null || result.selectionLength == null || result.selectionStart == null ||
|
||||
result.getPositionFromCharIndex == null)
|
||||
return null;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual string Text
|
||||
{
|
||||
get { return target.Text; }
|
||||
set { target.Text = value; }
|
||||
}
|
||||
|
||||
public virtual string SelectedText
|
||||
{
|
||||
get { return (string) selectedText.GetValue(target, null); }
|
||||
set { selectedText.SetValue(target, value, null); }
|
||||
}
|
||||
|
||||
public virtual int SelectionLength
|
||||
{
|
||||
get { return (int) selectionLength.GetValue(target, null); }
|
||||
set { selectionLength.SetValue(target, value, null); }
|
||||
}
|
||||
|
||||
public virtual int SelectionStart
|
||||
{
|
||||
get { return (int) selectionStart.GetValue(target, null); }
|
||||
set { selectionStart.SetValue(target, value, null); }
|
||||
}
|
||||
|
||||
public virtual Point GetPositionFromCharIndex(int pos)
|
||||
{
|
||||
return (Point) getPositionFromCharIndex.Invoke(target, new object[] {pos});
|
||||
}
|
||||
|
||||
|
||||
public virtual Form FindForm()
|
||||
{
|
||||
return target.FindForm();
|
||||
}
|
||||
|
||||
public virtual event EventHandler LostFocus
|
||||
{
|
||||
add { target.LostFocus += value; }
|
||||
remove { target.LostFocus -= value; }
|
||||
}
|
||||
|
||||
public virtual event ScrollEventHandler Scroll
|
||||
{
|
||||
add {
|
||||
if(target is RichTextBox)
|
||||
RTBScroll += value;
|
||||
else
|
||||
if(target is ScrollableControl)(target as ScrollableControl).Scroll += value;
|
||||
|
||||
}
|
||||
remove {
|
||||
if (target is RichTextBox)
|
||||
RTBScroll -= value;
|
||||
else
|
||||
if(target is ScrollableControl)(target as ScrollableControl).Scroll -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual event KeyEventHandler KeyDown
|
||||
{
|
||||
add { target.KeyDown += value; }
|
||||
remove { target.KeyDown -= value; }
|
||||
}
|
||||
|
||||
public virtual event MouseEventHandler MouseDown
|
||||
{
|
||||
add { target.MouseDown += value; }
|
||||
remove { target.MouseDown -= value; }
|
||||
}
|
||||
|
||||
public virtual Control TargetControl
|
||||
{
|
||||
get { return target; }
|
||||
}
|
||||
|
||||
|
||||
public bool Readonly
|
||||
{
|
||||
get { return (bool) readonlyProperty.GetValue(target, null); }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user