c#智能提示

   近段时间在帮朋友做一个短信发送管理的软件,其中有一个常用短语的功能。大家都知道用手机发送短信的时候一般都有常用短语的功能,朋友的意思也是按着手机那样传统的形式做就算了。但我觉得其中手机的常用短语功能其实并不常用,因为在手机上这功能比较鸡肋。但如果在电脑上,发挥的空间就大了很多,于是我便打算做成像IDE的智能提示(或叫提示补全)的形式。

       在百度和Google上搜索了一下,竟然没发现多少有用的资料。不过我觉得也没必要做到像IDE的智能提示那样的完美,因此按自己的想法做估计也不会太复杂。

       首先建立一个TipsListBox类,其作用是显示提示的信息,此类继承了ListBox,以便我们可以自己控制。将DrawMode属性设为OwnerDrawFixed。添加一个类型为string的属性Prefix。此属性的作用以后会提到。最后我们重写DrawItem事件。代码如下:

private void TipsListBox_DrawItem(object sender, DrawItemEventArgs e)

        {

            if (e.Index < 0)

                return;

            //是否选中了该项

            bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected ? true : false;

 

            e.DrawBackground();

 

            System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();

            if (selected)

            {

                Image img = Image.FromStream(asm.GetManifestResourceStream("MM.App.Resources.focus.gif"));

                Brush b = new TextureBrush(img);

                //参数中,e.Bounds 表示当前选项在整个listbox中的区域

                e.Graphics.FillRectangle(b, e.Bounds);

            }

            else

            {

                //在背景上画空白

                e.Graphics.FillRectangle(Brushes.White, e.Bounds);

                Image img = Image.FromStream(asm.GetManifestResourceStream("MM.App.Resources.line.bmp"));

                Brush b = new TextureBrush(img);

                //底下线的图片,参数中,23和是根据图片来的,因为需要在最下面显示线条

                e.Graphics.FillRectangle(b, e.Bounds.X, e.Bounds.Y + 23, e.Bounds.Width, 1);

            }

 

            //最后把要显示的文字画在背景图片上

            e.Graphics.DrawString(this.Items[e.Index].ToString(), this.Font, Brushes.Black, e.Bounds.X + 15, e.Bounds.Y + 6, StringFormat.GenericDefault);

 

            //再画一下边框

            ControlPaint.DrawBorder(e.Graphics,this.ClientRectangle,

                                        Color.Beige, 2, ButtonBorderStyle.Solid,

                                        Color.Beige, 2, ButtonBorderStyle.Solid,

                                        Color.Beige, 2, ButtonBorderStyle.Solid,

                                        Color.Beige, 2, ButtonBorderStyle.Solid);

 

        }

发送短信的界面是这样的:

在发送内容的输入框里输入要发送的短信,系统应该能提取用户最后输入的字串,然后将此字串放到预定义的常用短语库里匹配,将匹配到的短语列表显示在一个ListBox中。我这里暂时采取的规则比较简单,只提取以空格切分的最后一串的字符,然后匹配常用短语库中以这字串开头的短语。以后再根据客户需要进行扩展修改。

       首先重写短信内容的文本框(RichTextBox)的事件:

private void txtMessageContent_TextChanged(object sender, EventArgs e)

        {

            //提示框的name

            const string controlKey = "lstTips";

            RichTextBox tb = ((RichTextBox)sender);

            //以空格切分

            string[] array = tb.Text.Split(" ".ToCharArray());

            if (array != null && array.Length > 0)

            {

                TipsListBox lstTips = null;

                if(tb.Controls.ContainsKey(controlKey))

                {

                    lstTips = (TipsListBox)tb.Controls[controlKey];

                }

                else

                {

                    lstTips = new TipsListBox();

                    lstTips.Name = "lstTips";

                    //我们要重写这两个事件

                    lstTips.KeyDown += new KeyEventHandler(lstTips_KeyDown);

                    lstTips.Click += new EventHandler(lstTips_Click);

                }

                //这个前缀就是放到常用短语库中去匹配的

                string prefix = array[array.Length - 1];

                if (string.IsNullOrEmpty(prefix))

                {

                    lstTips.Hide();

                    return;

                }

                //从常用短语库中查找

                List<GeneralPhraseInfo> list = GeneralPhrasePool.Search(prefix);

                if (list == null)

                    return;

                //将此前缀保存起来

                lstTips.Prefix = prefix;

                lstTips.Items.Clear();

                foreach (GeneralPhraseInfo p in list)

                {

                    lstTips.Items.Add(p.Phrase);

                }

                lstTips.Show();

 

                lstTips.Width = 200;

                lstTips.TabIndex = 100;

                //让提示框跟随光标

                lstTips.Location = tb.GetPositionFromCharIndex(tb.SelectionStart);

                lstTips.Left += 10;

                lstTips.SelectedIndex = 0;

                if (!tb.Controls.ContainsKey(controlKey))

                    tb.Controls.Add(lstTips);

            }

        }

用户在短信输入文本框里按中了键盘的下方向键的话,就将焦点移到ListBox提示框里。

private void txtMessageContent_KeyDown(object sender, KeyEventArgs e)

        {

            RichTextBox tb = ((RichTextBox)sender);

            const string controlKey = "lstTips";

            if (e.KeyCode == Keys.Down && tb.Controls.ContainsKey(controlKey))

            {

                TipsListBox lstTips = (TipsListBox)tb.Controls[controlKey];

                if(lstTips.Visible)

                {

                    lstTips.Focus();

                }

            }

                

        }

然后重写ListBox的两个事件,比较简单,直接上代码:

void lstTips_Click(object sender, EventArgs e)

        {

            TipsListBox lstTips = (TipsListBox)sender;

            if(lstTips.SelectedIndex > -1)

            {

                string tips = lstTips.SelectedItem.ToString();

                txtMessageContent.AppendText(tips = tips.Substring(lstTips.Prefix.Length, tips.Length - lstTips.Prefix.Length));

                lstTips.Hide();

                txtMessageContent.Focus();

            }

        }

 

        void lstTips_KeyDown(object sender, KeyEventArgs e)

        {

            if (e.KeyCode == Keys.Enter)

            {

                //如果敲的是回车,就选定短语

                TipsListBox lstTips = (TipsListBox)sender;

                if (lstTips.SelectedIndex > -1)

                {

                    string tips = lstTips.SelectedItem.ToString();

                    txtMessageContent.AppendText(tips = tips.Substring(lstTips.Prefix.Length, tips.Length - lstTips.Prefix.Length));

                    lstTips.Hide();

                    txtMessageContent.Focus();

                }

                return;

            }

            //只允许在ListBox上操作上键和下键,其它键都使焦点返回到短信输入框

            if (e.KeyCode != Keys.Down && e.KeyCode != Keys.Up)

                txtMessageContent.Focus();

        }

到了这里,大家该差不多明白其中的流程了。不过可能对这一句有点疑惑:List<GeneralPhraseInfo> list = GeneralPhrasePool.Search(prefix);

为了提高性能,我预先将常用短语提取出来排好序,然后放到内存中。排序非常简单,用一条sql就可以搞定:Select * from dbo.GeneralPhrase order by Phrase

GeneralPhrase表里只有两个字段,一个是自增型主键,另一个就是Phrase类型为varchar。

既然已经排好序了,那当然用二分查找法。

public static List<GeneralPhraseInfo> Search(string prefix)

        {

            if (list == null || list.Count == 0)

                return null;

            int start = 0;

            int end = list.Count - 1;

            int middle = (start + end) / 2;

            int first = 0;

            int last = 0;

            while (start <= end)

            {

                if (list[middle].Phrase.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))

                {

                    //好,找到了一个

                    first = middle;

                    if(middle > start)

                    {

                        --middle;

                        //只要这个之前的也符合

                        while (list[middle].Phrase.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))

                        {

                            first = middle;

                            if (middle > -1)

                                --middle;

                            else

                                break;

                        }

                    }

                    //重置索引

                    middle = (start + end) / 2;

                    last = middle;

                    if(middle < end)

                    {

                        ++middle;

                        //只要这个之后的也符合

                        while (list[middle].Phrase.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))

                        {

                            last = middle;

                            if (middle < list.Count)

                                ++middle;

                            else

                                break;

                        }

                    }

                   

                    return list.GetRange(first, last - first + 1);

                }

                else if (list[middle].Phrase.ToLower().CompareTo(prefix) < 0 )

                {

                    start = middle + 1;

                }

                else

                {

                    end = middle - 1;

                }

                middle = (start + end) / 2;

            }

            //找不到

            return null;

        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值