在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
近段时间在帮朋友做一个短信发送管理的软件,其中有一个常用短语的功能。大家都知道用手机发送短信的时候一般都有常用短语的功能,朋友的意思也是按着手机那样传统的形式做就算了。但我觉得其中手机的常用短语功能其实并不常用,因为在手机上这功能比较鸡肋。但如果在电脑上,发挥的空间就大了很多,于是我便打算做成像IDE的智能提示(或叫提示补全)的形式。 在百度和Google上搜索了一下,竟然没发现多少有用的资料。不过我觉得也没必要做到像IDE的智能提示那样的完美,因此按自己的想法做估计也不会太复杂。 首先建立一个TipsListBox类,其作用是显示提示的信息,此类继承了ListBox,以便我们可以自己控制。将DrawMode属性设为OwnerDrawFixed。添加一个类型为string的属性Prefix。此属性的作用以后会提到。最后我们重写DrawItem事件。代码如下: privatevoid TipsListBox_DrawItem(objectsender, DrawItemEventArgse) { if (e.Index < 0) return; //是否选中了该项 bool selected = (e.State &DrawItemState.Selected) ==DrawItemState.Selected ?true : false;
e.DrawBackground();
System.Reflection.Assemblyasm = 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)的事件: privatevoid txtMessageContent_TextChanged(objectsender, EventArgse) { //提示框的name const stringcontrolKey = "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 = newTipsListBox(); 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 (GeneralPhraseInfop 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提示框里。 privatevoid txtMessageContent_KeyDown(objectsender, KeyEventArgse) { RichTextBox tb = ((RichTextBox)sender); const stringcontrolKey = "lstTips"; if (e.KeyCode ==Keys.Down && tb.Controls.ContainsKey(controlKey)) { TipsListBox lstTips = (TipsListBox)tb.Controls[controlKey]; if(lstTips.Visible) { lstTips.Focus(); } }
} 然后重写ListBox的两个事件,比较简单,直接上代码: voidlstTips_Click(objectsender, EventArgse) { 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。 既然已经排好序了,那当然用二分查找法。 publicstatic 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; } 最后的效果如下: |
请发表评论