上一篇高仿QQ即时聊天软件开发系列之二登录窗口界面写了一个大概的布局和原理

这一篇详细说下拉框的实现原理

先上最终效果图

一开始其实只是想给下拉框加一个placeholder效果,让下拉框在未选择未输入时显示一个提示字符串。由于Background对ComboBox无效,所以直接通过Background来实现是不行了。需要重新写ComboBox的模板,也就是Template,自定义一个模板来实现这个结果。又看了一下QQ的下拉框,这玩意不自定义也难以实现,所以就干脆自定义了。

先上代码,先是ComboBox,再是ComboBoxToggleButton,最后是ComboBoxItem

  <Style TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid Margin="0">
<Border
BorderThickness="1"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="4,4,0,0" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="20" x:Name="colArrow"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ToggleButton
Name="ToggleButton"
Grid.Column="1"
Focusable="false"
Style="{StaticResource ComboBoxToggleButton}"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<TextBox x:Name="PART_EditableTextBox"
BorderThickness="0"
VerticalAlignment="Center">
<TextBox.Resources>
<VisualBrush x:Key="tbPlaceHolder" Stretch="None" AlignmentX="Left">
<VisualBrush.Visual>
<Label Content="CC号码/用户名/邮箱" Foreground="Gray" Padding="5,0,0,0"></Label>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Resources>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{DynamicResource tbPlaceHolder}">
</Setter>
</Trigger>
<Trigger Property="Text" Value="">
<Setter Property="Background" Value="{DynamicResource tbPlaceHolder}">
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</Border>
<Popup
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid
Name="DropDown"
Width="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border
x:Name="DropDownBorder"
BorderThickness="1"
BorderBrush="{TemplateBinding BorderBrush}">
<ScrollViewer>
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Border>
</Grid>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

有没有觉得头晕?反正我是感觉有点晕吧。我也不是凭空重写出来的,是找的别人写好的示例代码改的,别人的示例代码那可叫真看着晕。现在改成这样已经简单很多了

从上到下,一点点来

第4行,给ComboBox重新定义控件模板,只在当前页面生效,然后下面是模板的内容

第6行,Border用于实现圆角

第10行,好戏开始。这个Grid定义了两列,第一列用于存放下拉框选择的文本,第二列用于存放下拉的箭头

第15行,开始定义下拉的箭头。ToggleButton是用来作为点击后弹出下拉框的按钮,但具体是什么东东?其实自己建一个wpf项目拉一个ToggleButton出来看看就明白了。它跟复选框类似,一般情况有两个值,一个是Checked,一个是UnChecked,但跟复选框不同的是,它只是一个按钮,没有对勾,并且你点击它之后,如果它的状态是Checked,那么它就不会弹起来了,再点一下才能弹起来,想想这个是不是跟下拉框的功能有点相似?下面的Style暂时不说,因为那个是属于ComboBoxToggleButton那块的

第20行,IsChecked,关键的地方。这个值绑定到了一个叫IsDropDownOpen的属性,是双向绑定,绑定的对象是使用这个控件的控件。这什么意思呢?谁会使用这个控件呢?那其实就是ComboBox(TemplatedParent这个属性其实我没太理解透,可能一时间也难以理解透),而如果下拉框是打开的,那么也就意味着isChecked会为True,那么ToggleButton就会是选中的状态。反过来,如果下拉框未打开,那么IsChecked就是未选中状态,这个时候点击ToggleButton,那么IsChecked会为True,由于是双向绑定,所以IsDropDownOpen也会为True,下拉框就自然打开了。不得不说WPF真心强大。

第23行,这个TextBox是用于存放选择后的结果的,也是用于实现可编辑下拉框的。然后嘛,placeholder效果就在这儿实现了。

第50行,Popup,用于弹出一个控件,这里的{TemplateBinding IsDropDownOpen}其实跟上面说的TemplatedParent是一个意思,把IsOpen绑定到了应用了该控件的控件的IsDropDownOpen属性

第65行,StackPanel用于存放下拉框中的每一项,IsItemsHost表示这个控件是否用于Item的容器(好吧其实这个地方我短时间理解不了,只能这么去理解)

OK,这个完毕,下一个,ComboBoxToggleButton

 <Style x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Path
Panel.ZIndex="1"
x:Name="Arrow"
Grid.Column="1"
Fill="#B1B1B1"
Stroke="#B1B1B1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M0,0L3,3 6,0z">
<Path.RenderTransform>
<ScaleTransform x:Name="stfArrow" CenterY="2"></ScaleTransform>
</Path.RenderTransform>
</Path>
<TextBlock Panel.ZIndex="0"></TextBlock>
</Grid>
<!--</Grid>-->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Arrow" Property="Fill" Value="Black"></Setter>
<Setter TargetName="Arrow" Property="Stroke" Value="Black"></Setter>
</Trigger>
<EventTrigger RoutedEvent="Checked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.2"
Storyboard.TargetName="stfArrow"
Storyboard.TargetProperty="ScaleY" From="1"
To="-1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Unchecked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.2"
Storyboard.TargetName="stfArrow"
Storyboard.TargetProperty="ScaleY" From="-1"
To="1" />
<DoubleAnimation
Duration="0:0:0.2"
Storyboard.TargetName="stfArrow"
Storyboard.TargetProperty="ScaleY" From="-1"
To="1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

很多重复的技术点就跳过

第9行,Path控件用于画出点击下拉框时的那个向下的箭头,其中的Data可能比较难以理解,推荐一篇文章:http://blog.csdn.net/johnsuna/article/details/1885597,我也是在这儿看懂的。

第18行,RenderTransform中的ScaleTransform用于旋转这个Path控件也就是箭头,ScaleTransform有一个属性ScaleY,改为-1时就会180度翻转,可以实现当下拉框被打开时箭头朝上的效果

第22行,呃··怎么不记得有这一行,可能没什么用吧,忽略掉

第45行,注册鼠标经过触发器,经过时把箭头变成黑色

第49行,注册Checked事件触发器,BeginStoryboard开始一段动画,Storyboard创建一个动画,DoubleAnimation创建两个值之间的过渡,TargetName指定给哪个控件应用这个过渡,TargetProperty指定给哪个属性应用,From和To就不用说了。下面的UnChecked跟上面类似。这个主要用于实现点击箭头时的翻转动画。

OK,下一部分

 <Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform x:Name="stItem"></ScaleTransform>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Grid Background="White" x:Name="spItem" Height="23">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="colImage" Width="20"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Margin="0,1" Source="{Binding Image}"></Image>
<Label Grid.Column="1" HorizontalAlignment="Stretch" Content="{Binding CC}" Name="lblItem" VerticalContentAlignment="Center" FontSize="11" HorizontalContentAlignment="Left" Margin="0,1">
</Label>
<Image Cursor="Hand" ToolTip="删除该账号" x:Name="delImage" Visibility="Collapsed" Margin="10,0" Grid.Column="2" Width="9" HorizontalAlignment="Right" Source="Resources/images/deleteAccountNormal.png"></Image>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ComboBoxItem.MouseEnter" SourceName="spItem">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="spItem" Storyboard.TargetProperty="(Height)" From="23" To="40" Duration="0:0:0.20"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="lblItem" Storyboard.TargetProperty="FontSize" From="11" To="14" Duration="0:0:0.20"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="ComboBoxItem.MouseLeave" SourceName="spItem">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="spItem" Storyboard.TargetProperty="(Height)" From="40" To="23" Duration="0:0:0.20"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="lblItem" Storyboard.TargetProperty="FontSize" From="14" To="11" Duration="0:0:0.20"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="spItem" Property="Background" Value="#378FCF"></Setter>
<Setter TargetName="lblItem" Property="Foreground" Value="White"></Setter>
<Setter TargetName="colImage" Property="Width" Value="40"></Setter>
<Setter TargetName="delImage" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

终于到最后一个地方了,上面的代码定义了下拉框中的每一项的模板

第10行,定义了一个Grid,三列,第一列是图片,也就是用户头像,第二列是用户名,第三列是删除用户的图标

然后其实下面的就是一堆动画,这个跟上一部分的动画原理类似的。

这些模板定义好之后,直接拖个ComboBox出来就行了,不需要任何设置

这篇文章肯定会有技术错误,毕竟我对WPF并不是太熟,如果有的话请指正

高仿QQ即时聊天软件开发系列之三登录窗口用户选择下拉框的更多相关文章

  1. 高仿QQ即时聊天软件开发系列之二登录窗口界面

    继上一篇高仿QQ即时聊天软件开发系列之一开端之后,开始做登录窗口 废话不多说,先看效果,只有界面 可能还有一些细节地方没有做,例如那个LOGO嘛,不要在意这些细节 GIF虽短,可是这做起来真难,好吧因 ...

  2. 高仿QQ即时聊天软件开发系列之一开端

    前段时间在园子里看到一个大神做了一个GG2014IM软件,仿QQ的,那感觉···,赶快下载源码过来试试,还真能直接跑起来,效果也不错.但一看源码,全都给封装到了ESFramework里面了,音视频那部 ...

  3. SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)

     SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...

  4. 仿QQ局域网聊天软件

    1 目的   想复习一下TCP/IP协议,再结合一下以前学的Qt的知识,加上前段时间学的MySQL数据库操作,所以写了个"仿QQ局域网聊天软件"小项目,只实现了一部分功能,还没写完 ...

  5. Selenium系列(十) - 针对Select下拉框的操作和源码解读

    如果你还想从头学起Selenium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1680176.html 其次,如果你不懂前端基础知识, ...

  6. Android 高仿微信即时聊天 百度云为基础的推

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38799363 ,本文出自:[张鸿洋的博客] 一直在仿微信界面,今天最终有幸利用百 ...

  7. “仿QQ局域网聊天软件”项目-常用编程技巧总结

    1 信号槽篇 qqLogin loginDialog; QQ mainDialog; loginDialog.show(); //连接登陆窗口和主窗口 QObject::connect(&lo ...

  8. 高仿QQ的即时通讯应用带服务端软件安装

    Android 基于xmpp协议,smack包,openfire服务端(在下面)的高仿QQ的即时通讯实现.实现了注册,登录,读取好友列表,搜索好友,添加分组,添加好友,删除好友,修改心情,两个客户端之 ...

  9. 高仿qq聊天界面

    高仿qq聊天界面,给有需要的人,界面效果如下: 真心觉得做界面非常痛苦,给有需要的朋友. chat.xml <?xml version="1.0" encoding=&quo ...

随机推荐

  1. CSS中常用中文字体转Unicode编码表

    中文名 英文名 Unicode Unicode 2 Mac OS 华文细黑 STHeiti Light [STXihei] \534E\6587\7EC6\9ED1 华文细黑 华文黑体 STHeiti ...

  2. iOS开发——C篇&文件操作

    今天开始C语言中的重点难点就基本上技术忘了,但是还有最后一个知识点不得不提,那就是文件操作. 我们都知道,我们每天都在使用电脑,手机,或者其他电子或者移动设备,其实我们在使用的时候每时每刻都在执行文件 ...

  3. 生产环境下Centos 6.5优化配置 (装载)

    本文 centos 6.5 优化 的项有18处: 1.centos6.5最小化安装后启动网卡 2.ifconfig查询IP进行SSH链接 3.更新系统源并且升级系统 4.系统时间更新和设定定时任 5. ...

  4. intrins.h 里面的函数都有什么,功能是什么?

    是c51中的intrins.h库 _crol_  字符循环左移 _cror_ 字符循环右移 _irol_   整数循环左移 _iror_  整数循环右移 _lrol_   长整数循环左移 _lror_ ...

  5. WebAPI 用户认证防篡改实现HMAC(二)签名验证 AbsBaseAuthenticationAttribute--转

    public class ActionFilter : ActionFilterAttribute      {          public override void OnActionExecu ...

  6. static在实例Extends、Overload中理解

    在写java程序的时候只在类内部调用的方法定义为private的是个很好的编程习惯.另外子类的static方法和父类有同名.同参数的static方法,但他们之间没什么覆盖.继承的关系,你调用的时候看是 ...

  7. 【细说Java】Java变量初始化顺序

    Java的变量初始化顺序,对这里一直似懂非懂,面试的时候也经常被问到,但答的一直不好,现在整理记录一下,以后忘记了可以来看看. 程序分为两个部分,第一个部分不考虑继承,第二个部分考虑继承: (1)不考 ...

  8. P2P网贷投资须谨防圈钱人

    摘要:P2P领域依然呈现投资热潮,甚至部分国有大行也有意涉足.这个行业到底怎么了?P2P平台上高收益的理财产品,到底能买不?     新年才刚刚过了10天,就有4家网贷平台被爆出支付危机,P2P一时被 ...

  9. [面试题总结及扩展知识]HTTP协议返回状态码的问题

    经常在网页中看到一些错误的返回信息,见一个查一个已经累感不爱,在2014年腾讯笔试题中也见到一道这样的问题,所以现在来总结一下: 腾讯2014面试题: 答案选B 附带一些http协议的错误代码: 当服 ...

  10. linux 切换用户之后变成-bash-x.x$的解决方法

    我们平时在linux下切换用户后命令行为什么会变成-bash-3.2$呢,我们来分析一下,这就是跟linux的机制有关联了,因为在linux下每次通过useradd创建新的用户时,都会将所有的配置文件 ...