上記サイトでは ObjectDataProvider で候補を絞っていますが、データの再取得はしないので、今回は CollectionView で絞る方法で作りました。
一応、文字色も変えれるようにしています。カテゴリ別に色分けするとかで使えそうですね。
[XAML側]
<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.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal" 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="{StaticResource HoverBrush}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{StaticResource SelectedBackgroundBrush}" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Popup>
[Code側]
/// <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 = "*";
// 項目設定
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>
/// <param name="sender"></param>
/// <param name="e"></param>
private void queryTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
try
{
// インテリセンス
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(() =>
{
try
{
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);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, TITLE, MessageBoxButton.OK, MessageBoxImage.Warning);
}
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, TITLE, MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
/// <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.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();
}
Popup と ListBox をコードから生成させてテンプレート内の任意の位置に追加する感じで。
0 件のコメント:
コメントを投稿