上一篇高仿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. 基础知识 mfc

    句柄 资源的标示  图标句柄(HICON) 光标句柄(HCURSOR) 窗口句柄(HWND) 类似于指针 wm_keydown表示键盘上的按键按下了数值 WPARAM ||LPARAM  两个整形数据 ...

  2. 百度touch的手势框架,touch.js

    今天,随便搜搜看到了一个新的手势库,也许能让我为现在使用者的hammer.js的手势库带来的烦恼而消除. 它是百度团队开发的,现在由百度云Clouda进行维护. 官网   http://touch.c ...

  3. DIRECTORY_SEPARATOR

    定义 php的内置变量DIRECTORY_SEPARATOR是一个显示系统分隔符的命令,DIRECTORY_SEPARATOR是php的内部常量,不需要任何定义与包含即可直接使用. 2说明   路径分 ...

  4. C++Primer charpter1.

    一.输入输出流 endl:会刷新buffer.刷新之后你才能看到.不手动用endl的话,就只能依靠系统自动刷.程序崩溃的话,你看到的调试信息可能是错误的. >>:   两个连续的符号 ci ...

  5. 配置一个servlet程序

    <!-- 配置一个servlet程序 --> <servlet> <!-- servlet的内部名称 ,可以自定义--> <servlet-name>H ...

  6. nodejs 记入

    1. vs2015 使用最新的 nodejs refer : http://josharepoint.com/2016/05/04/how-to-configure-visual-studio-201 ...

  7. Cracking the coding interview--Q1.3

    原文 Given two strings, write a method to decide if one is a permutation of the other. 译文 给你两个字符串,写一个方 ...

  8. c++(smart pointer)

    (一)首先对智能指针有一些概念性的了解 **********本部分内容摘自开源中国社区http://my.oschina.net/u/158589/blog/28994******** 1.什么是智能 ...

  9. hackerrank:Almost sorted interval

    题目链接:https://www.hackerrank.com/challenges/almost-sorted-interval 题目大意: 定义一个“几乎单调”区间(区间最小值在最左面,最大值在最 ...

  10. JDK、JRE和JVM的区别与联系

    首先来说一下JDK JDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK). JDK是整个JAVA的核心,包括了Java运行环境(Java Runtime E ...