下拉神器是一款专业的下拉词平台,提供各种下拉功能:百度下拉丶360搜索下拉丶必应搜索下拉丶哔哩哔哩下拉丶抖音下拉等等,里面还有批量做词教程技术,可以一次性做10-50个词,省心高效!批量做词,批量出词,可以实现霸屏效果!
有的客户想删除下拉,但是百度不受理,就可以使用批量做词霸屏技术,实现把那些负面信息顶下去,顶掉的效果=删除效果!欢迎您前来使用!新手不懂使用,请多看2遍视频教程哦!下拉神器100%有效果的!
给大家看一个下拉神器介绍的视频,看完后,点击下面的按钮进入”下拉神器“
欢迎使用下拉神器,下拉行业老品牌,如果下拉神器都不好使,整个行业其他平台一样不好使,但是大家一定要多学习多看教程,先学会做词出词的技巧!
下一篇文章内容预览:
在此处写入自定义目录标题
目标
首先百度搜索联想词每个人一样吗★64xl.com轻松做下拉词,百度反馈删除下拉词,网上已经有很多比较高级的方法可以实现标题所描述的功能,比如自定义样式、自定义控件(应该就是这么叫的),这里就只记录一下我的个人使用经验,毕竟我花了很长时间才明白其中的一些原理。
1. 实现搜索功能
搜索功能已经可用,你只需打开它即可。但这个搜索功能相对简单,它可以帮助你找到第一个(如果有的话)匹配的项目。我想要实现的是根据搜索文本更新列表内容,因为这样更酷。下面是如何实现它(以及一些陷阱)。
1.定义
首先!我们需要关闭它,因为它不仅能帮我们定位匹配项,还会将其设置为此项,这会给我们的代码带来逻辑混乱。具体解释如下:
从属性上看, 和 Text 的更新顺序是从前到后的,查找逻辑应该是根据 Text 来更新源,但是更新源的操作会导致 的更新,从而导致 Text 再次更新,那么逻辑就变得未知不可控了(甚至有断点和没有断点时程序行为也会不一样)
从事件的角度,我观察到一个很奇怪的情况,就是里面的控件导致的更新优先级高于用户的输入行为。比如我输入“六”,代码会自动选中“六六六”,并开始更新Text等属性。等这些更新完成后,用户输入的“六”又会开始更新Text(甚至多次),最终导致界面显示的内容和代码选中的内容不一致,而且由于逻辑混乱,这种情况下不一致的问题很难解决。
我们用的大概是这样的:
<ComboBox ItemsSource="{Binding FilterItems}" Text="{Binding SearchText}" IsEditable="True" IsTextSearchEnable="False"/>
基于这个定义,我们在set方法中实现搜索逻辑。
2..cs代码
在.cs文件中(这里不需要使用MVVM,特别是如果您以后想将其设为独立控件)带搜索功能的下拉选择框,我们定义上面绑定的属性如下:
private List<T> ItemsSource;//完整的数据源,记得找地方初始化public List<T> FilterItems {get;set;}//记得找地方初始化private string mSearchText;public string SearchText{get{return mSearchText;}set{ mSearchText=value; FilterItems=ItemsSource.Where(x=>x.Contains(value)).ToList();//这里的.Where用到了System.Linq,报错的话添加一下using就好}}
至此,我们就实现了一个搜索功能,效果如下:在里面输入文字,点击展开列表查看匹配项,点击选中。这篇文章到这里当然还没结束,下面的内容才是锦上添花。如果有兴趣,可以继续看下去。
2.实现符合你习惯的弹窗功能
它提供了一个选项,让用户在输入时保持下拉框弹出,以便用户可以直观地看到搜索结果。但是,它有两个问题:
并不是,就是说如果用户输入的时候弹出了下拉框,输入行为不会导致下拉框缩回;但是如果输入的时候下拉框还没弹出来,就不会自动弹出,当我们选中其中一项,想删除一些词再搜索的时候,它不会响应,或者说选中之后的删除不算是编辑行为。
如果您想要自定义弹出行为,则需要进行一点修改:
<ComboBox ItemsSource="{Binding FilterItems}" Text="{Binding SearchText}" IsEditable="True" IsTextSearchEnable="False" x:Name = "CBName" GotFocus="GotFocus"/><!--加上这两个-->
首先我们给它起个名字,这样我们就可以直接控制它了;然后当我们点击它获取焦点的时候(在这之前它的下拉框应该是收起来的),执行函数让它自动展开,这样我们就不用在输入之前点击下拉按钮让它展开了。有了这两个带搜索功能的下拉选择框,我们就可以定义符合使用习惯的弹出行为了。下面先贴代码:
private List<T> ItemsSource;public List<T> FilterItems {get;set;}private string mSearchText;public string SearchText{get{return mSearchText;}set{ mSearchText=value; FilterItems=ItemsSource.Where(x=>x.Contains(value)).ToList(); CBName.IsDropDownOpen = true;}}//注意,GotFocus属性不是依赖属性,不能使用Bindingpublic void GotFocus(object sender, RoutedEventArgs e){ CBName.IsDropDownOpen = true;}
这样,当我们点击搜索,或者在输入或者删除的时候,下拉框就会一直弹出来。当然,当我们选中其中一项的时候,下拉框就会自动缩回,这时候如果继续输入或者删除,下拉框就会再次弹出来。不过这时候代码中有一个小bug,后面会介绍。
3. 添加占位符并实现提示功能
利用这个,我们可以在输入框为空且失去焦点时添加一个占位符,提示用户输入,在获得焦点时清除占位符以供用户输入。代码如下:
<ComboBox ItemsSource="{Binding FilterItems}" Text="{Binding SearchText}" IsEditable="True" IsTextSearchEnable="False" x:Name = "CBName" IsDropDownOpen="{Binding IsDrop}" GotFocus="GotFocus" LostFocus="LostFocus"/><!--加上这一个-->
public void GotFocus(object sender, RoutedEventArgs e){ CBName.IsDropDownOpen = true;if(CBName.Text.Equals("占位符")) CBName.Text="";}public void LostFocus(object sender, RoutedEventArgs e){ CBName.IsDropDownOpen = false;if(CBName.Text.IsNullOrEmpty()) CBName.Text="占位符";//IsNullOrEmpty需要用到websocket-sharp包,没有的话手动判断一下就行}
这样,当焦点离开时,如果文本为空,就会出现一个占位符作为提示,再次点击时会自动清除占位符。不过要注意的是,这两个事件在程序刚开始运行时不会触发。单就上述代码而言,程序刚开始运行时会发现为空,没有占位符,所以需要赋予(而不是,以免更新错误)一个初始值“”。
但是我们可以看到,当失去焦点时,下拉框是关闭的,赋值给 .Text 会(尝试)展开它,但实际上(在我的电脑上,.NET 4.6.2,2017)并没有展开。原因不清楚,大家可以自行验证。不过这个问题会带来一个小问题,就是“占位符”的存在可能会造成错误的更新,我们需要修改它的更新逻辑:
public string SearchText{get{return mSearchText;}set{ mSearchText=value;if(value=="占位符"){ FilterItems=ItemsSource.ToList();}else{ FilterItems=ItemsSource.Where(x=>x.Contains(value)).ToList(); CBName.IsDropDownOpen = true;}}}
咦!现在问题没有了,内容也正确了(当然,如果电脑失去焦点时下拉框不展开,其实这个问题就不成问题了,也不用再做上面的分类判断了)。到此,占位符的功能就实现了。
4. 消除最后一个小错误
看得仔细的都知道我前面提到过一个bug,不记的话我就抽你10次屁股惩罚你。具体来说,就是我们选中某项(比如“”)之后,如果用户很叛逆,坚持要先删除“”这个词,然后在搜索结果中重新选择“”,就会发现显示的文字还是一个字,看上去好像这个项是不能选中的。当然对于代码来说,在这个过程中,选中的项一直都是“”,并没有发生改变,因为删除行为只是更新了绑定的数据源,在更新后的源中选中的项还是存在的。代码能做的只是改变它,因为当我们选中“”的时候,也更新了源只包含这一项,此时自然是0,而删除的时候,源可能包含更多的内容,其中“”的位置可能就会发生改变,需要更新。
解决这个问题的逻辑其实很简单,就是需要识别用户是否后悔、是否故意的行为,代码如下:
<ComboBox ItemsSource="{Binding FilterItems}" Text="{Binding SearchText}" IsEditable="True" IsTextSearchEnable="False" x:Name = "CBName" IsDropDownOpen="{Binding IsDrop}" GotFocus="GotFocus" LostFocus="LostFocus" SelectedIndex="{Binding SelectedIndex}"/><!--加上这一个-->
public int SelectedIndex {get;set;}public string SearchText{get{return mSearchText;}set{if(value.Length < mSearchText.Length){ SelectedIndex = -1;} mSearchText=value;if(value=="占位符"){ FilterItems=ItemsSource.ToList();}else{ FilterItems=ItemsSource.Where(x=>x.Contains(value)).ToList(); CBName.IsDropDownOpen = true;}}}
我们再次进行绑定,当识别到用户的任性行为后,就将其改为-1,也就是取消之前的选择,然后在下拉框中重新选择“”。
5. 最后一个错误
上面的代码其实还有最后一个bug,就是:当我们删除的时候(假设文本是“abcv”),不管 Back 还是 ,删除一个字符后,默认会选中剩余的所有字符。即使我们用鼠标点击取消选中,下次删除后还是会选中所有字符。除非用方向键移动一次换光器,否则没办法一个字一个字地删除。我觉得这个还是比较严重的问题,尤其是我想删除一个搜索“abc”的时候,还得重新输入,很影响体验。写到这里的时候应该快下班了,这个bug还是没解决。结果随便问了小机器人,它就给了个半吊子的答案。调试了好一阵子终于解决了,所以把这个解决方案贴出来,供大家参考和扩展。
注:同时特别感谢免费提供小机器人的大佬(因为不知道是谁,只能口头感谢了)。希望大家不要白白拿走!捐一点,就当数据费吧。或许大佬可以免费给我们一个GPT-4,每天问一两次问题()
<ComboBox others Loaded="ComboBox_Loaded"/><!--加上这一个-->
//在加载控件时为其绑定按键处理事件private void ComboBox_Loaded(object sender, RoutedEventArgs e){//FindChild是自定义函数,见下var textBox = FindChild<TextBox>(sender as DependencyObject);if(textBox != null){ textBox.PreviewKeyDown += previewKeyDown;}}//另一个写法private void ComboBox_Loaded(object sender, RoutedEventArgs e){var textBox = FindChild<TextBox>(sender as DependencyObject);if(textBox != null){ textBox.PreviewKeyDown += (s, args) =>{//处理逻辑,同下};}}//递归查找ComboBox子控件中的TextBoxprivate T FindChild<T>(DependencyObject parent) where T : DependencyObject{if(parent == null) return null;for(int i = 0; i < VisualTreeHelper.GetChildCount(parent); i++){var child = VisualTreeHelper.GetChild(parent, i);if(child is T found){return found;}else{var descendant = FindChild<T>(child);if(descendant != null) return descendant;}}}//之所以要在preview事件中处理,是因为我发现Delete和Back两个按键似乎会被系统默认处理完了,用户自定义的处理事件不会被调用,而preview则可以避免这个问题private void previewKeyDown(object sender, KeyEventArgs args){var textBox = sender as TextBox;if(textBox.Text.Length <= 0) return;var index = textBox.CaretIndex;//获取当前光标位置var text = textBox.Text;if(args.KeyboardDevice.IsKeyDown(Key.Back)){if(textBox.SelectionLength == 0)//未选中文字时删除单个文字{if(index > 0)//若光标在开头则不处理{ textBox.Text = text.Remove(index - 1, 1);//先更新文本再执行后面的操作,否则更新文本会导致后面的操作被覆盖 textBox.CaretIndex = index - 1; textBox.SelectionLength = 0;//不选中任何文字 args.Handled = true;//阻止后续对Back按键的处理}}else//删除选中的多个(>=1)文字{ textBox.Text = text.Remove(index, textBox.SelectionLength); textBox.CaretIndex = index; textBox.SelectionLength = 0; args.Handled = true;}}else if(args.KeyboardDevice.IsKeyDown(Key.Delete)){ textBox.Text = text.Remove(index, textBox.SelectionLength == 0? 1 : textBox.SelectionLength); textBox.CaretIndex = index; textBox.SelectionLength = 0; args.Handled = true;//阻止后续对Delete按键的处理}//若需要重写其他按键的操作也可以继续添加}
这样我们就可以正常逐字删除了。当然,Got的时候还是默认选中所有字符,这样可以方便的全部删除。如果不想全部选中,只要再次点击鼠标或者用方向键调整光标位置,就可以开始逐字调整文本了。
最后的想法
上面的实现方法可能不伦不类,但对我个人来说还是比较简单的,效果也满足我个人的需求。网上还有很多其他更高级或者说比较常见的实现方法,我只是作为参考而已。
更新
5.11更新:Back 的处理功能没有考虑选择多个单词删除的情况
6.5更新:之前对MVVM的概念理解有误,对上述地方做了修正