高仿QQ即时聊天软件开发系列之三登录窗口用户选择下拉框
上一篇高仿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即时聊天软件开发系列之三登录窗口用户选择下拉框的更多相关文章
- 高仿QQ即时聊天软件开发系列之二登录窗口界面
继上一篇高仿QQ即时聊天软件开发系列之一开端之后,开始做登录窗口 废话不多说,先看效果,只有界面 可能还有一些细节地方没有做,例如那个LOGO嘛,不要在意这些细节 GIF虽短,可是这做起来真难,好吧因 ...
- 高仿QQ即时聊天软件开发系列之一开端
前段时间在园子里看到一个大神做了一个GG2014IM软件,仿QQ的,那感觉···,赶快下载源码过来试试,还真能直接跑起来,效果也不错.但一看源码,全都给封装到了ESFramework里面了,音视频那部 ...
- SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)
SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...
- 仿QQ局域网聊天软件
1 目的 想复习一下TCP/IP协议,再结合一下以前学的Qt的知识,加上前段时间学的MySQL数据库操作,所以写了个"仿QQ局域网聊天软件"小项目,只实现了一部分功能,还没写完 ...
- Selenium系列(十) - 针对Select下拉框的操作和源码解读
如果你还想从头学起Selenium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1680176.html 其次,如果你不懂前端基础知识, ...
- Android 高仿微信即时聊天 百度云为基础的推
转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38799363 ,本文出自:[张鸿洋的博客] 一直在仿微信界面,今天最终有幸利用百 ...
- “仿QQ局域网聊天软件”项目-常用编程技巧总结
1 信号槽篇 qqLogin loginDialog; QQ mainDialog; loginDialog.show(); //连接登陆窗口和主窗口 QObject::connect(&lo ...
- 高仿QQ的即时通讯应用带服务端软件安装
Android 基于xmpp协议,smack包,openfire服务端(在下面)的高仿QQ的即时通讯实现.实现了注册,登录,读取好友列表,搜索好友,添加分组,添加好友,删除好友,修改心情,两个客户端之 ...
- 高仿qq聊天界面
高仿qq聊天界面,给有需要的人,界面效果如下: 真心觉得做界面非常痛苦,给有需要的朋友. chat.xml <?xml version="1.0" encoding=&quo ...
随机推荐
- Oracle数据库之PL/SQL程序设计基础
PL/SQL程序设计基础 一.PL/SQL块结构 前边我们已经介绍了PL/SQL块的结构,再来回顾一下: DECLARE /* * 声明部分——定义常量.变量.复杂数据类型.游标.用户自定义异常 */ ...
- thinkphp 开发的获取用户信息
<?php namespace Home\Controller; use Think\Controller; use Com\Wechat; use Com\WechatAuth; class ...
- JBPM4.4GPD设计器中文乱码问题的另一种解决方法
在eclipse中使用JBPM4.4的设计器时,输入中文后直接查看Source发现xml里中文全都乱码了,这时候大约整个人都不好了!赶紧百度.谷歌,搜到的多数结果都是要你在eclipse.ini或my ...
- CentOS安装MySQL问题汇总
遇到的错误 ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) denied for ...
- 【原创】CLEVO P157SM外接鼠标键盘失灵解决:更换硅脂(附带最新跑分数据)
作者批注:本文允许转载,并且希望给搜索未来人类.蓝天.CLEVO.更换硅脂或者任何有关关键字的朋友提供帮助. 原文地址:http://www.cnblogs.com/c4isr/p/3514140.h ...
- Sicily shortest path in unweighted graph
题目介绍: 输入一个无向图,指定一个顶点s开始bfs遍历,求出s到图中每个点的最短距离. 如果不存在s到t的路径,则记s到t的距离为-1. Input 输入的第一行包含两个整数n和m,n是图的顶点 ...
- strnclmp和strlen函数的用法
一.strncmp 函数 函数原型: 1.函数原型:int strncmp (const char *s1, const char *s2, size_t n) 2.头文件: <string. ...
- java批量转换图片格式
废话不多直接上代码,代码其实也不多.... package com.qiao.testImage; import java.awt.image.BufferedImage; import java.i ...
- C写的扫描器源码
Title:C写的扫描器源码 --2010-10-27 20:02 无意间看见的一个源代码,弄回来读下. ----------------------------------------------- ...
- 51单片机C语言学习笔记4:keil C51绝对地址访问
在利用keil进行8051单片机编程的时,常常需要进行绝对地址进行访问.特别是对硬件操作,如DA AD 采样 ,LCD 液晶操作,打印操作.等等.C51提供了三种访问绝对地址的方法: 1. 绝对宏: ...