5 people like it.
Like the snippet!
Auto-completion in any winforms text box
The standard windows.forms text box supports auto-completion, but only for single-line text boxes.
The code below can be used to add auto-completion against a fixed set of words to any text box that inherits from TextBoxBase.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
|
#r "System.Windows.Forms"
module Trie =
type Impl<'Char> when 'Char : comparison =
| Multi of Map<'Char, Node<'Char>>
and Node<'Char> when 'Char : comparison =
{ impl : Impl<'Char>
someWordEndsHere : bool }
let leaf = { impl = Multi Map.empty; someWordEndsHere = false }
let rec insert node (word : 'Char seq) =
if Seq.isEmpty word then
{ node with someWordEndsHere = true }
else
let first, rest = Seq.head word, Seq.skip 1 word
match node.impl with
| Multi m ->
let c, next =
match Map.tryFind first m with
| Some next ->
(first, insert next rest)
| None ->
(first, insert leaf rest)
let m =
Map.add c next m
{ node with impl = Multi m }
let rec tryFindNode node (prefix : 'Char seq) =
if not <| Seq.isEmpty prefix then
let first, rest = Seq.head prefix, Seq.skip 1 prefix
match node.impl with
| Multi m ->
match Map.tryFind first m with
| Some next ->
tryFindNode next rest
| None ->
None
else
Some node
let rec getSuffixes node =
seq {
if node.someWordEndsHere then
yield []
match node.impl with
| Multi m ->
for (c, next) in Map.toSeq m do
yield!
getSuffixes next
|> Seq.map (fun s -> c :: s)
}
let findSuffixes node prefix =
let results =
tryFindNode node prefix
|> Option.bind(fun node ->
getSuffixes node
|> Some)
match results with
| None -> Seq.empty
| Some s -> s
module Extensions =
open System.Windows.Forms
type System.String with
member this.ReverseAt(idx) =
seq {
for i in (idx - 1) .. -1 .. 0 -> this.[i]
}
static member Reverse(s : char seq) =
let s = Seq.toArray s
let sb = System.Text.StringBuilder()
for i in s.Length - 1 .. -1 .. 0 do
sb.Append(s.[i])
|> ignore
sb.ToString()
static member ofSeq(s : char seq) =
let sb = System.Text.StringBuilder()
for c in s do
sb.Append(c)
|> ignore
sb.ToString()
type System.Windows.Forms.TextBoxBase with
member this.GetWordBeforeCarret(isValidInWord) =
this.Text.ReverseAt(this.SelectionStart)
|> Seq.takeWhile isValidInWord
|> System.String.Reverse
member this.AddAutoCompletion(words) =
let isValidInWord = System.Char.IsLetterOrDigit
let trie =
words
|> Seq.fold Trie.insert Trie.leaf
let handleKey (kp : KeyEventArgs) =
if kp.Control && kp.KeyCode = Keys.Space then
kp.SuppressKeyPress <- true
let prefix = this.GetWordBeforeCarret(isValidInWord)
let pos = this.SelectionStart
let before = this.Text.[0 .. pos - 1]
let after = this.Text.[pos .. ]
let insertSuffix s =
this.Text <- before + s + after
this.SelectionStart <- pos
this.SelectionLength <- s.Length
let suffixes =
Trie.findSuffixes trie prefix
|> Seq.map (System.String.ofSeq)
|> Seq.sort
|> Seq.truncate 10
|> Seq.toArray
match suffixes with
| [||] -> ()
| [|s|] ->
insertSuffix s
| _ ->
let cms = new ContextMenuStrip()
for s in suffixes do
cms.Items.Add(prefix + s, null, fun _ _ -> insertSuffix s)
|> ignore
let pos = this.GetPositionFromCharIndex(pos)
let pos = System.Drawing.Point(pos.X, pos.Y + cms.Font.Height)
cms.Show(this, pos)
this.KeyDown.Subscribe handleKey
open System.Windows.Forms
open Extensions
let form = new Form()
let tb = new RichTextBox(Dock = DockStyle.Fill)
tb.AddAutoCompletion(["C#" ; "F#" ; "C++"; "C"; "Ocaml"; "Haskell"])
form.Controls.Add tb
form.Show()
|
type Impl<'Char (requires comparison)> = | Multi of Map<'Char,Node<'Char>>
Full name: Script.Trie.Impl<_>
union case Impl.Multi: Map<'Char,Node<'Char>> -> Impl<'Char>
Multiple items
module Map
from Microsoft.FSharp.Collections
--------------------
type Map<'Key,'Value (requires comparison)> =
interface IEnumerable
interface IComparable
interface IEnumerable<KeyValuePair<'Key,'Value>>
interface ICollection<KeyValuePair<'Key,'Value>>
interface IDictionary<'Key,'Value>
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
member Add : key:'Key * value:'Value -> Map<'Key,'Value>
member ContainsKey : key:'Key -> bool
override Equals : obj -> bool
member Remove : key:'Key -> Map<'Key,'Value>
...
Full name: Microsoft.FSharp.Collections.Map<_,_>
--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
type Node<'Char (requires comparison)> =
{impl: Impl<'Char>;
someWordEndsHere: bool;}
Full name: Script.Trie.Node<_>
Node.impl: Impl<'Char>
Node.someWordEndsHere: bool
type bool = System.Boolean
Full name: Microsoft.FSharp.Core.bool
val leaf : Node<'a> (requires comparison)
Full name: Script.Trie.leaf
val empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Map.empty
val insert : node:Node<'Char> -> word:seq<'Char> -> Node<'Char> (requires comparison)
Full name: Script.Trie.insert
val node : Node<'Char> (requires comparison)
val word : seq<'Char> (requires comparison)
Multiple items
val seq : sequence:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Core.Operators.seq
--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
Full name: Microsoft.FSharp.Collections.seq<_>
module Seq
from Microsoft.FSharp.Collections
val isEmpty : source:seq<'T> -> bool
Full name: Microsoft.FSharp.Collections.Seq.isEmpty
val first : 'Char (requires comparison)
val rest : seq<'Char> (requires comparison)
val head : source:seq<'T> -> 'T
Full name: Microsoft.FSharp.Collections.Seq.head
val skip : count:int -> source:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Collections.Seq.skip
val m : Map<'Char,Node<'Char>> (requires comparison)
val c : 'Char (requires comparison)
val next : Node<'Char> (requires comparison)
val tryFind : key:'Key -> table:Map<'Key,'T> -> 'T option (requires comparison)
Full name: Microsoft.FSharp.Collections.Map.tryFind
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Map.add
val tryFindNode : node:Node<'Char> -> prefix:seq<'Char> -> Node<'Char> option (requires comparison)
Full name: Script.Trie.tryFindNode
val prefix : seq<'Char> (requires comparison)
val not : value:bool -> bool
Full name: Microsoft.FSharp.Core.Operators.not
val getSuffixes : node:Node<'a> -> seq<'a list> (requires comparison)
Full name: Script.Trie.getSuffixes
val node : Node<'a> (requires comparison)
Node.impl: Impl<'a>
val m : Map<'a,Node<'a>> (requires comparison)
val c : 'a (requires comparison)
val next : Node<'a> (requires comparison)
val toSeq : table:Map<'Key,'T> -> seq<'Key * 'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Map.toSeq
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
Full name: Microsoft.FSharp.Collections.Seq.map
val s : 'a list (requires comparison)
val findSuffixes : node:Node<'a> -> prefix:seq<'a> -> seq<'a list> (requires comparison)
Full name: Script.Trie.findSuffixes
val prefix : seq<'a> (requires comparison)
val results : seq<'a list> option (requires comparison)
module Option
from Microsoft.FSharp.Core
val bind : binder:('T -> 'U option) -> option:'T option -> 'U option
Full name: Microsoft.FSharp.Core.Option.bind
val empty<'T> : seq<'T>
Full name: Microsoft.FSharp.Collections.Seq.empty
val s : seq<'a list> (requires comparison)
namespace System
namespace System.Windows
namespace System.Windows.Forms
Multiple items
type String =
new : value:char -> string + 7 overloads
member Chars : int -> char
member Clone : unit -> obj
member CompareTo : value:obj -> int + 1 overload
member Contains : value:string -> bool
member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
member EndsWith : value:string -> bool + 2 overloads
member Equals : obj:obj -> bool + 2 overloads
member GetEnumerator : unit -> CharEnumerator
member GetHashCode : unit -> int
...
Full name: System.String
--------------------
System.String(value: nativeptr<char>) : unit
System.String(value: nativeptr<sbyte>) : unit
System.String(value: char []) : unit
System.String(c: char, count: int) : unit
System.String(value: nativeptr<char>, startIndex: int, length: int) : unit
System.String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
System.String(value: char [], startIndex: int, length: int) : unit
System.String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: System.Text.Encoding) : unit
val this : System.String
member System.String.ReverseAt : idx:int -> seq<char>
Full name: Script.Extensions.ReverseAt
val idx : int
val i : int
static member System.String.Reverse : s:seq<char> -> string
Full name: Script.Extensions.Reverse
val s : seq<char>
Multiple items
val char : value:'T -> char (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.char
--------------------
type char = System.Char
Full name: Microsoft.FSharp.Core.char
val s : char []
val toArray : source:seq<'T> -> 'T []
Full name: Microsoft.FSharp.Collections.Seq.toArray
val sb : System.Text.StringBuilder
namespace System.Text
Multiple items
type StringBuilder =
new : unit -> StringBuilder + 5 overloads
member Append : value:string -> StringBuilder + 18 overloads
member AppendFormat : format:string * arg0:obj -> StringBuilder + 4 overloads
member AppendLine : unit -> StringBuilder + 1 overload
member Capacity : int with get, set
member Chars : int -> char with get, set
member Clear : unit -> StringBuilder
member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
member EnsureCapacity : capacity:int -> int
member Equals : sb:StringBuilder -> bool
...
Full name: System.Text.StringBuilder
--------------------
System.Text.StringBuilder() : unit
System.Text.StringBuilder(capacity: int) : unit
System.Text.StringBuilder(value: string) : unit
System.Text.StringBuilder(value: string, capacity: int) : unit
System.Text.StringBuilder(capacity: int, maxCapacity: int) : unit
System.Text.StringBuilder(value: string, startIndex: int, length: int, capacity: int) : unit
property System.Array.Length: int
System.Text.StringBuilder.Append(value: char []) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: obj) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: uint64) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: uint32) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: uint16) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: decimal) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: float) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: float32) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: int64) : System.Text.StringBuilder
(+0 other overloads)
System.Text.StringBuilder.Append(value: int) : System.Text.StringBuilder
(+0 other overloads)
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
System.Text.StringBuilder.ToString() : string
System.Text.StringBuilder.ToString(startIndex: int, length: int) : string
static member System.String.ofSeq : s:seq<char> -> string
Full name: Script.Extensions.ofSeq
val c : char
type TextBoxBase =
inherit Control
member AcceptsTab : bool with get, set
member AppendText : text:string -> unit
member AutoSize : bool with get, set
member BackColor : Color with get, set
member BackgroundImage : Image with get, set
member BackgroundImageLayout : ImageLayout with get, set
member BorderStyle : BorderStyle with get, set
member CanUndo : bool
member Clear : unit -> unit
member ClearUndo : unit -> unit
...
Full name: System.Windows.Forms.TextBoxBase
val this : TextBoxBase
member TextBoxBase.GetWordBeforeCarret : isValidInWord:(char -> bool) -> string
Full name: Script.Extensions.GetWordBeforeCarret
val isValidInWord : (char -> bool)
property TextBoxBase.Text: string
member System.String.ReverseAt : idx:int -> seq<char>
property TextBoxBase.SelectionStart: int
val takeWhile : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Collections.Seq.takeWhile
static member System.String.Reverse : s:seq<char> -> string
member TextBoxBase.AddAutoCompletion : words:seq<#seq<char>> -> System.IDisposable
Full name: Script.Extensions.AddAutoCompletion
val words : seq<#seq<char>>
type Char =
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 1 overload
static val MaxValue : char
static val MinValue : char
static member ConvertFromUtf32 : utf32:int -> string
static member ConvertToUtf32 : highSurrogate:char * lowSurrogate:char -> int + 1 overload
static member GetNumericValue : c:char -> float + 1 overload
...
end
Full name: System.Char
System.Char.IsLetterOrDigit(c: char) : bool
System.Char.IsLetterOrDigit(s: string, index: int) : bool
val trie : Trie.Node<char>
val fold : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> 'State
Full name: Microsoft.FSharp.Collections.Seq.fold
module Trie
from Script
val insert : node:Trie.Node<'Char> -> word:seq<'Char> -> Trie.Node<'Char> (requires comparison)
Full name: Script.Trie.insert
val leaf : Trie.Node<'a> (requires comparison)
Full name: Script.Trie.leaf
val handleKey : (KeyEventArgs -> unit)
val kp : KeyEventArgs
Multiple items
type KeyEventArgs =
inherit EventArgs
new : keyData:Keys -> KeyEventArgs
member Alt : bool
member Control : bool
member Handled : bool with get, set
member KeyCode : Keys
member KeyData : Keys
member KeyValue : int
member Modifiers : Keys
member Shift : bool
member SuppressKeyPress : bool with get, set
Full name: System.Windows.Forms.KeyEventArgs
--------------------
KeyEventArgs(keyData: Keys) : unit
property KeyEventArgs.Control: bool
property KeyEventArgs.KeyCode: Keys
type Keys =
| KeyCode = 65535
| Modifiers = -65536
| None = 0
| LButton = 1
| RButton = 2
| Cancel = 3
| MButton = 4
| XButton1 = 5
| XButton2 = 6
| Back = 8
...
Full name: System.Windows.Forms.Keys
field Keys.Space = 32
property KeyEventArgs.SuppressKeyPress: bool
val prefix : string
member TextBoxBase.GetWordBeforeCarret : isValidInWord:(char -> bool) -> string
val pos : int
val before : string
val after : string
val insertSuffix : (string -> unit)
val s : string
property TextBoxBase.SelectionLength: int
property System.String.Length: int
val suffixes : string []
val findSuffixes : node:Trie.Node<'a> -> prefix:seq<'a> -> seq<'a list> (requires comparison)
Full name: Script.Trie.findSuffixes
static member System.String.ofSeq : s:seq<char> -> string
val sort : source:seq<'T> -> seq<'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Seq.sort
val truncate : count:int -> source:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Collections.Seq.truncate
val cms : ContextMenuStrip
Multiple items
type ContextMenuStrip =
inherit ToolStripDropDownMenu
new : unit -> ContextMenuStrip + 1 overload
member SourceControl : Control
Full name: System.Windows.Forms.ContextMenuStrip
--------------------
ContextMenuStrip() : unit
ContextMenuStrip(container: System.ComponentModel.IContainer) : unit
property ToolStrip.Items: ToolStripItemCollection
ToolStripItemCollection.Add(value: ToolStripItem) : int
ToolStripItemCollection.Add(image: System.Drawing.Image) : ToolStripItem
ToolStripItemCollection.Add(text: string) : ToolStripItem
ToolStripItemCollection.Add(text: string, image: System.Drawing.Image) : ToolStripItem
ToolStripItemCollection.Add(text: string, image: System.Drawing.Image, onClick: System.EventHandler) : ToolStripItem
val pos : System.Drawing.Point
TextBoxBase.GetPositionFromCharIndex(index: int) : System.Drawing.Point
namespace System.Drawing
Multiple items
type Point =
struct
new : sz:Size -> Point + 2 overloads
member Equals : obj:obj -> bool
member GetHashCode : unit -> int
member IsEmpty : bool
member Offset : p:Point -> unit + 1 overload
member ToString : unit -> string
member X : int with get, set
member Y : int with get, set
static val Empty : Point
static member Add : pt:Point * sz:Size -> Point
...
end
Full name: System.Drawing.Point
--------------------
System.Drawing.Point()
System.Drawing.Point(sz: System.Drawing.Size) : unit
System.Drawing.Point(dw: int) : unit
System.Drawing.Point(x: int, y: int) : unit
property System.Drawing.Point.X: int
property System.Drawing.Point.Y: int
property ToolStripDropDown.Font: System.Drawing.Font
property System.Drawing.Font.Height: int
ToolStripDropDown.Show() : unit
ToolStripDropDown.Show(screenLocation: System.Drawing.Point) : unit
ToolStripDropDown.Show(x: int, y: int) : unit
ToolStripDropDown.Show(position: System.Drawing.Point, direction: ToolStripDropDownDirection) : unit
ToolStripDropDown.Show(control: Control, position: System.Drawing.Point) : unit
ToolStripDropDown.Show(control: Control, x: int, y: int) : unit
ToolStripDropDown.Show(control: Control, position: System.Drawing.Point, direction: ToolStripDropDownDirection) : unit
event Control.KeyDown: IEvent<KeyEventHandler,KeyEventArgs>
member System.IObservable.Subscribe : callback:('T -> unit) -> System.IDisposable
System.IObservable.Subscribe(observer: System.IObserver<KeyEventArgs>) : System.IDisposable
module Extensions
from Script
val form : Form
Full name: Script.form
Multiple items
type Form =
inherit ContainerControl
new : unit -> Form
member AcceptButton : IButtonControl with get, set
member Activate : unit -> unit
member ActiveMdiChild : Form
member AddOwnedForm : ownedForm:Form -> unit
member AllowTransparency : bool with get, set
member AutoScale : bool with get, set
member AutoScaleBaseSize : Size with get, set
member AutoScroll : bool with get, set
member AutoSize : bool with get, set
...
nested type ControlCollection
Full name: System.Windows.Forms.Form
--------------------
Form() : unit
val tb : RichTextBox
Full name: Script.tb
Multiple items
type RichTextBox =
inherit TextBoxBase
new : unit -> RichTextBox
member AllowDrop : bool with get, set
member AutoSize : bool with get, set
member AutoWordSelection : bool with get, set
member BackgroundImage : Image with get, set
member BackgroundImageLayout : ImageLayout with get, set
member BulletIndent : int with get, set
member CanPaste : clipFormat:Format -> bool
member CanRedo : bool
member DetectUrls : bool with get, set
...
Full name: System.Windows.Forms.RichTextBox
--------------------
RichTextBox() : unit
type DockStyle =
| None = 0
| Top = 1
| Bottom = 2
| Left = 3
| Right = 4
| Fill = 5
Full name: System.Windows.Forms.DockStyle
field DockStyle.Fill = 5
member TextBoxBase.AddAutoCompletion : words:seq<#seq<char>> -> System.IDisposable
property Control.Controls: Control.ControlCollection
Control.ControlCollection.Add(value: Control) : unit
Control.Show() : unit
Form.Show(owner: IWin32Window) : unit
More information