http://challengeandresponse.blogspot.jp/2014/04/wpfctextbox.html
こちらのサイトでWPFでIntelliSenseを使う方法が例示されているが、いくつか不満点があるため何箇所か改良を行いました。
- フィルターでToUpperしていなかったので、すべての要素が正しく表示されない
→ToUpperを挿入した - 表示されている要素の幅次第でポップアップの幅も変わる(次図)
→ItemsPanelTemplateを用いることで解決した
変更後
<Window x:Class="IntelliSenseTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase" xmlns:local="clr-namespace:IntelliSenseTest" Title="MainWindow" Height="300" Width="500"> <Window.DataContext> <local:ViewModel x:Name="vm"/> </Window.DataContext> <Window.Resources> <Style TargetType="local:IntelliSenseTextBox"> <Style.Triggers> <Trigger Property="IsKeyboardFocusWithin" Value="True"> <Setter Property="TextBackground" Value="Ivory" /> </Trigger> </Style.Triggers> </Style> </Window.Resources> <Grid> <TextBox AcceptsReturn="True" AcceptsTab="True" Name="queryTextBox" Margin="5" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" PreviewKeyDown="queryTextBox_PreviewKeyDown"/> <Popup Name="popup" IsOpen="False" StaysOpen="False" MaxHeight="300" FocusVisualStyle="{x:Null}"> <ListBox IsTextSearchEnabled="False" Name="intellisenseListBox" PreviewKeyDown="intellisenseListBox_PreviewKeyDown" MouseDoubleClick="intellisenseListBox_MouseDoubleClick" DisplayMemberPath="Key" SelectedValuePath="Key" ItemsSource="{Binding}" FocusVisualStyle="{x:Null}" > <ListBox.ItemsPanel> <ItemsPanelTemplate> <StackPanel IsItemsHost="True" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> <ListBox.ItemContainerStyle> <Style TargetType="{x:Type ListBoxItem}"> <Setter Property="FocusVisualStyle" Value="{x:Null}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <StackPanel Background="{TemplateBinding Background}"> <TextBlock Text="{Binding Key}" Foreground="{Binding Path=ForeColor}"/> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Aqua" /> </Trigger> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background" Value="AliceBlue" /> </Trigger> </Style.Triggers> </Style> </ListBox.ItemContainerStyle> </ListBox> </Popup> </Grid> </Window> namespace IntelliSenseTest { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 項目設定 var items = typeof(Brushes).GetProperties().Select(brush => new CandidateKeyItem() { Key = brush.Name, ForeColor = (Brush)brush.GetValue(null, null) }).ToList(); intellisenseListBox.DataContext = items; this.intellisenseView = CollectionViewSource.GetDefaultView(items); this.intellisenseView.Filter = filterIntellisense; } /// <summary> /// 候補キーアイテム /// </summary> public class CandidateKeyItem { /// <summary> /// 候補キー /// </summary> public string Key { get; set; } /// <summary> /// 文字色 /// </summary> public Brush ForeColor { get; set; } } /// <summary> /// インテリセンスコレクションビュー /// </summary> private ICollectionView intellisenseView; /// <summary> /// カレント単語 /// </summary> private string currentWord = "*"; /// <summary> /// クエリテキストボックスプレビューキーダウンイベント /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void queryTextBox_PreviewKeyDown(object sender, KeyEventArgs e) { // インテリセンス if (e.Key == Key.Back || e.Key == Key.Escape) { popup.IsOpen = false; return; } if (popup.IsOpen) { if (e.Key == Key.Tab || e.Key == Key.Down) { intellisenseListBox.Focus(); return; } if (e.Key == Key.Enter && intellisenseListBox.Items.Count != 0) { if (intellisenseListBox.SelectedIndex == -1) { intellisenseListBox.SelectedIndex = 0; } selectWord(); e.Handled = true; return; } } // フィルタ対象外判定 int key = (int)e.Key; if ((key < 34) || (85 < key) && (key < 140) || (154 < key)) { return; } this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => { currentWord = getCurrentWord(queryTextBox); if (string.IsNullOrEmpty(currentWord)) { popup.IsOpen = false; return; } intellisenseView.Refresh(); // Popupを現在のキャレットのある位置へ表示 popup.PlacementTarget = queryTextBox; popup.PlacementRectangle = queryTextBox.GetRectFromCharacterIndex(queryTextBox.CaretIndex); // 候補が0個の時は表示しない popup.IsOpen = intellisenseListBox.Items.Count != 0; if (popup.IsOpen && intellisenseListBox.SelectedItem != null) { // 既に選択したものがあればそこにスクロール intellisenseListBox.ScrollIntoView(intellisenseListBox.SelectedItem); } })); } /// <summary> /// リストボックスプレビューキーダウンイベント /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void intellisenseListBox_PreviewKeyDown(object sender, KeyEventArgs e) { // ESCAPE押下でポップアップキャンセル if (e.Key == Key.Escape) { popup.IsOpen = false; queryTextBox.Focus(); return; } // 文字選択判定 if (e.Key == Key.Enter || e.Key == Key.Tab || e.Key == Key.Space) { selectWord(); e.Handled = true; } } /// <summary> /// リストビューダブルクリックイベント /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void intellisenseListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e) { selectWord(); } /// <summary> /// カレント文字取得 /// </summary> /// <param name="textBox"></param> /// <returns></returns> private string getCurrentWord(TextBox textBox) { if (string.IsNullOrEmpty(textBox.Text)) { return string.Empty; } if (textBox.CaretIndex == 0) { return string.Empty; } int index = textBox.CaretIndex - 1; int last = textBox.Text.LastIndexOfAny(new[] { ' ', '\r', '\n', '\t' }, index) + 1; return textBox.Text.Substring(last, textBox.CaretIndex - last).ToUpper(); } /// <summary> /// インテリセンスフィルター /// </summary> /// <param name="args"></param> /// <returns></returns> private bool filterIntellisense(object args) { bool result = false; var word = args as CandidateKeyItem; if (word != null) { result = word.Key.ToUpper().Contains(currentWord); } return result; } /// <summary> /// ワード選択 /// </summary> private void selectWord() { if (intellisenseListBox.SelectedValue == null) { return; } var caretIndex = queryTextBox.CaretIndex; var selectedText = intellisenseListBox.SelectedValue as string; var topIndex = caretIndex - currentWord.Length; // 選択されたものを挿入 var tmpText = queryTextBox.Text.Remove(topIndex, currentWord.Length); queryTextBox.Text = tmpText.Insert(topIndex, selectedText); queryTextBox.CaretIndex = topIndex + selectedText.Length; popup.IsOpen = false; queryTextBox.Focus(); } } }