首页 > 技术文章 > WPF中实现类智能感知

leakeyash 2016-07-26 11:03 原文

  首先要做的事情就是定义一个popup来显示我们需要展示的东西

<Popup x:Name="ConfigPopup" Height="auto" Width="150" StaysOpen="False" Placement="Bottom" IsOpen="False" HorizontalAlignment="Left">
                <Grid Width="auto" Height="auto">
                    <ListBox x:Name="MailConfigSelection" KeyDown="MailConfigSelection_OnKeyDown" IsTextSearchEnabled="True" ItemsSource="{Binding KeysCollection}"></ListBox>
                </Grid>
</Popup>

  这里使用的是用一个Listbox来做演示,也可以使用其他的控件或者改listbox的template。popup的staysopen代表失去焦点后还显不显示。listbox绑定到一个ObservableCollection来实时改变。

  后台代码

public ObservableCollection<string> KeysCollection { get; set; } = new ObservableCollection<string>(); 

...

private void MailConfigSelection_OnKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                ConfigPopup.IsOpen = false;
                System.Windows.Controls.ListBox lb = sender as System.Windows.Controls.ListBox;
                if (lb == null) return;

                string mailConfig = lb.SelectedItem.ToString();

                //Popup pp = (lb.Parent as Grid).Parent as Popup;
                TextBox tb = ConfigPopup.PlacementTarget as TextBox;
                int i = tb.CaretIndex;//获取呼出这个popup的textbox的当前光标位置
                tb.Text = tb.Text.Insert(i, mailConfig) + ">";//插入选择的字符串
                tb.CaretIndex = i + mailConfig.Length + 1;//移动光标
                tb.Focus();
            }
            else if (e.Key == Key.Escape)
            {
                ConfigPopup.IsOpen = false;
            }
        }

  给textbox加上keyup响应事件

private void TextBox_OnKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key != Key.OemComma) return;

            TextBox tbm = e.OriginalSource as TextBox;
            if (tbm.Text.EndsWith("<") && KeysCollection.Count != 0)//这里是这样的条件,可以根据需求来改变
            {
                ShowPopUp(tbm.GetRectFromCharacterIndex(tbm.CaretIndex), tbm);
            }
}

  弹出popup时,我们希望popup出现在正在输入的textbox下面而且在输入的光标附近

private void ShowPopUp(Rect placementRect, TextBox tb)
        {
            ConfigPopup.PlacementTarget = tb;
            ConfigPopup.PlacementRectangle = placementRect;
            ConfigPopup.IsOpen = true;
            MailConfigSelection.Focus();
            MailConfigSelection.SelectedIndex = 0;
            var listBoxItem = (ListBoxItem)MailConfigSelection.ItemContainerGenerator.ContainerFromItem(MailConfigSelection.SelectedItem);
            listBoxItem.Focus();
        }

  我们先获取到当前光标的位置Rect,tbm.GetRectFromCharacterIndex(tbm.CaretIndex)来获取rect并传给popup的PlacementRectangle,这样popup出现的位置就是我们想要的了。

  在这个过程中发现弹出窗口后选择候选项时,按上下键有时候不管用,因为我们focus在popup上,上下键相应的框内所有内容的上下翻,我们希望的是在各个选项之间的切换,那么我们需要focus在里面的listitem上,通过MailConfigSelection.ItemContainerGenerator.ContainerFromItem(MailConfigSelection.SelectedItem)来获取到选中的listboxitem并把焦点给他即可。

  到此,一个类智能感知就完成了,可以通过其他的各种方式来进行优化。

推荐阅读