インテリセンスみたいなのを作ろうということで挑戦。
登場人物はPopupとTextBoxとObjectDataProviderの三人。
Popupは、コンボボックスのドロップダウンリストの部分みたいなものを作るときに使うらしい。
PlacementTargetで指定したコントロールの上下左右好きな位置に出すことができるみたい。
上下左右はPlacementプロパティで設定できる。
デフォは下になってるみたいだ。さらに、PlacementRectangleを指定すると、PlacementTargetで指定したコントロールの任意の矩形の上下左右あたりに出すこともできる。
ということで、これにListBoxを乗せてCtrl + Spaceを押したときにひょっこり出してやろうと思う。
まずは、候補として出す文字列を返すものを作る。Windowsシリーズの中から候補を絞り込むようにした。
using System.Collections.Generic;
using System.Linq;
namespace AutoComplete
{
public class ItemsSource
{
private static readonly string[] ITEMS = new[]
{
"Windows98",
"Windows2000SP4",
"WindowsCE",
"WindowsMillenniumEdition",
"WindowsMobileForPocketPC",
"WindowsMobileForSmartphone",
"WindowsServer2003",
"WindowsXPMediaCenterEdition",
"WindowsXPProfessionalx64Edition",
"WindowsXPSP2",
"WindowsXPStarterEdition"
};
public IEnumerable<string> GetItems(string text)
{
if (string.IsNullOrEmpty(text))
{
return ITEMS;
}
return from s in ITEMS
where s.StartsWith(text)
select s;
}
}
}
んで、WindowのResourcesにObjectDataProviderを登録。
<Window.Resources>
<ObjectDataProvider x:Key="itemsProvider"
ObjectType="{x:Type AutoComplete:ItemsSource}"
MethodName="GetItems" />
</Window.Resources>
WindowにPopupとTextBoxを配置する。
<DockPanel>
<Popup Name="popup" IsOpen="False" StaysOpen="False">
<ListBox Name="listBoxTarget"
ItemsSource="{Binding Source={StaticResource itemsProvider}}"
IsTextSearchEnabled="True"
KeyDown="listBoxTarget_KeyDown">
</ListBox>
</Popup>
<TextBox
KeyDown="TextBox_KeyDown"
AcceptsReturn="True"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible" />
</DockPanel>
TextBoxのKeyDownイベントでCtrl+Spaceが押された時に、今キャレットのある場所の下にPopupを出すようなコードを仕込む。
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == ModifierKeys.Control &&
e.Key == Key.Space)
{
var textBox = sender as TextBox;
// ObjectDataProviderの中身を更新
var provider = FindResource("itemsProvider") as ObjectDataProvider;
provider.MethodParameters.Clear();
provider.MethodParameters.Add(GetCurrentWord(textBox));
provider.Refresh();
// Popupを現在のキャレットのある位置へ表示
popup.PlacementTarget = textBox;
popup.PlacementRectangle =
textBox.GetRectFromCharacterIndex(textBox.CaretIndex);
// 候補が0個の時は表示しない
if (listBoxTarget.Items.Count != 0)
{
popup.IsOpen = true;
listBoxTarget.Focus();
}
e.Handled = true;
}
}
続いて、ListBoxでEnterが押された時に、選択されているものをTextBoxに挿入する。おまけで、Escapeキーのときは閉じるようにしている。
private void listBoxTarget_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
popup.IsOpen = false;
return;
}
if (e.Key == Key.Enter)
{
if (listBoxTarget.SelectedItem == null)
{
return;
}
var textBox = popup.PlacementTarget as TextBox;
var caretIndex = textBox.CaretIndex;
var currentWord = GetCurrentWord(textBox);
var selectedText = listBoxTarget.SelectedItem as string;
// 選択されたものを挿入
var tmpText = textBox.Text.Remove(caretIndex - currentWord.Length, currentWord.Length);
textBox.Text = tmpText.Insert(caretIndex - currentWord.Length, selectedText);
textBox.CaretIndex = caretIndex + selectedText.Length;
popup.IsOpen = false;
textBox.Focus();
}
}
こんな感じで実行すると下のような感じに動く。
何も入力しない状態で
Ctrl+Spaceを押すとリストが出てくる
適当に選択して
Enterキーで入力される
途中まで入力してCtrl+Spaceで
絞り込まれた内容が表示される
適当に選択して
Enterキーで入力される
VC# 2008 Expressで作成したプロジェクトはここに置いてあります。