高仿QQ即时聊天软件开发系列之三登录窗口用户选择下拉框

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

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

先上最终效果图

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

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

 1  <Style TargetType="{x:Type ComboBox}">
 2             <Setter Property="Template">
 3                 <Setter.Value>
 4                     <ControlTemplate TargetType="ComboBox">
 5                         <Grid Margin="0">
 6                             <Border 
 7                                     BorderThickness="1"
 8                                 BorderBrush="{TemplateBinding BorderBrush}"
 9                                     CornerRadius="4,4,0,0" >
10                                 <Grid>
11                                     <Grid.ColumnDefinitions>
12                                         <ColumnDefinition></ColumnDefinition>
13                                         <ColumnDefinition Width="20" x:Name="colArrow"></ColumnDefinition>
14                                     </Grid.ColumnDefinitions>
15                                     <ToggleButton 
16                                 Name="ToggleButton" 
17                                 Grid.Column="1" 
18                                 Focusable="false"
19                                     Style="{StaticResource ComboBoxToggleButton}" 
20                                 IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
21                                 ClickMode="Press">
22                                     </ToggleButton>
23                                     <TextBox x:Name="PART_EditableTextBox"
24                                          BorderThickness="0"
25                                 VerticalAlignment="Center">
26                                         <TextBox.Resources>
27                                             <VisualBrush x:Key="tbPlaceHolder" Stretch="None" AlignmentX="Left">
28                                                 <VisualBrush.Visual>
29                                                     <Label Content="CC号码/用户名/邮箱" Foreground="Gray" Padding="5,0,0,0"></Label>
30                                                 </VisualBrush.Visual>
31                                             </VisualBrush>
32                                         </TextBox.Resources>
33                                         <TextBox.Style>
34                                             <Style TargetType="TextBox">
35                                                 <Style.Triggers>
36                                                     <Trigger Property="Text" Value="{x:Null}">
37                                                         <Setter Property="Background" Value="{DynamicResource tbPlaceHolder}">
38                                                         </Setter>
39                                                     </Trigger>
40                                                     <Trigger Property="Text" Value="">
41                                                         <Setter Property="Background" Value="{DynamicResource tbPlaceHolder}">
42                                                         </Setter>
43                                                     </Trigger>
44                                                 </Style.Triggers>
45                                             </Style>
46                                         </TextBox.Style>
47                                     </TextBox>
48                                 </Grid>
49                             </Border>
50                             <Popup 
51                                 Placement="Bottom"
52                                 IsOpen="{TemplateBinding IsDropDownOpen}"
53                                 AllowsTransparency="True" 
54                                 Focusable="False"
55                                 PopupAnimation="Slide">
56                                 <Grid
57                                   Name="DropDown"
58                                   Width="{TemplateBinding ActualWidth}"
59                                   MaxHeight="{TemplateBinding MaxDropDownHeight}">
60                                     <Border 
61                                         x:Name="DropDownBorder"
62                                         BorderThickness="1"
63                                         BorderBrush="{TemplateBinding BorderBrush}">
64                                         <ScrollViewer>
65                                             <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
66                                         </ScrollViewer>
67                                     </Border>
68                                 </Grid>
69                             </Popup>
70                         </Grid>
83                     </ControlTemplate>
84                 </Setter.Value>
85             </Setter>
86         </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

 1 <Style x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
 2             <Setter Property="Template">
 3                 <Setter.Value>
 4                     <ControlTemplate TargetType="ToggleButton">
 5                         <Grid>
 6                             <Grid.ColumnDefinitions>
 7                                 <ColumnDefinition />
 8                             </Grid.ColumnDefinitions>
 9                             <Path 
10                                 Panel.ZIndex="1"
11                               x:Name="Arrow"
12                               Grid.Column="1"     
13                               Fill="#B1B1B1"
14                               Stroke="#B1B1B1"
15                               HorizontalAlignment="Center"
16                               VerticalAlignment="Center"
17                               Data="M0,0L3,3 6,0z">
18                                 <Path.RenderTransform>
19                                     <ScaleTransform x:Name="stfArrow" CenterY="2"></ScaleTransform>
20                                 </Path.RenderTransform>
21                             </Path>
22                             <TextBlock Panel.ZIndex="0"></TextBlock>
23                         </Grid>
37                         <!--</Grid>-->
38                         <ControlTemplate.Triggers>
45                             <Trigger Property="IsMouseOver" Value="True">
46                                 <Setter TargetName="Arrow" Property="Fill" Value="Black"></Setter>
47                                 <Setter TargetName="Arrow" Property="Stroke" Value="Black"></Setter>
48                             </Trigger>
49                             <EventTrigger RoutedEvent="Checked">
50                                 <EventTrigger.Actions>
51                                     <BeginStoryboard>
52                                         <Storyboard>
53                                             <DoubleAnimation  
54                                          Duration="0:0:0.2"  
55                                                         Storyboard.TargetName="stfArrow"
56                                          Storyboard.TargetProperty="ScaleY"  From="1" 
57                                          To="-1"  />
58                                         </Storyboard>
59                                     </BeginStoryboard>
60                                 </EventTrigger.Actions>
61                             </EventTrigger>
62                             <EventTrigger RoutedEvent="Unchecked">
63                                 <EventTrigger.Actions>
64                                     <BeginStoryboard>
65                                         <Storyboard>
66                                             <DoubleAnimation  
67                                          Duration="0:0:0.2"  
68                                                         Storyboard.TargetName="stfArrow"
69                                          Storyboard.TargetProperty="ScaleY"  From="-1" 
70                                          To="1"  />
71                                             <DoubleAnimation  
72                                          Duration="0:0:0.2"  
73                                                         Storyboard.TargetName="stfArrow"
74                                          Storyboard.TargetProperty="ScaleY"  From="-1" 
75                                          To="1"  />
76                                         </Storyboard>
77                                     </BeginStoryboard>
78                                 </EventTrigger.Actions>
79                             </EventTrigger>
80                         </ControlTemplate.Triggers>
81                     </ControlTemplate>
82                 </Setter.Value>
83             </Setter>
84         </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,下一部分

 1 <Style TargetType="{x:Type ComboBoxItem}">
 2             <Setter Property="RenderTransform">
 3                 <Setter.Value>
 4                     <ScaleTransform x:Name="stItem"></ScaleTransform>
 5                 </Setter.Value>
 6             </Setter>
 7             <Setter Property="Template">
 8                 <Setter.Value>
 9                     <ControlTemplate TargetType="ComboBoxItem">
10                         <Grid Background="White" x:Name="spItem" Height="23">
11                             <Grid.ColumnDefinitions>
12                                 <ColumnDefinition x:Name="colImage" Width="20"></ColumnDefinition>
13                                 <ColumnDefinition></ColumnDefinition>
14                                 <ColumnDefinition Width="30"></ColumnDefinition>
15                             </Grid.ColumnDefinitions>
16                             <Image Grid.Column="0" Margin="0,1" Source="{Binding Image}"></Image>
17                             <Label Grid.Column="1" HorizontalAlignment="Stretch" Content="{Binding CC}" Name="lblItem" VerticalContentAlignment="Center" FontSize="11" HorizontalContentAlignment="Left" Margin="0,1">
18                             </Label>
19                             <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>
20                         </Grid>
21                         <ControlTemplate.Triggers>
22                             <EventTrigger RoutedEvent="ComboBoxItem.MouseEnter" SourceName="spItem">
23                                 <EventTrigger.Actions>
24                                     <BeginStoryboard>
25                                         <Storyboard>
26                                             <DoubleAnimation Storyboard.TargetName="spItem" Storyboard.TargetProperty="(Height)" From="23" To="40" Duration="0:0:0.20"></DoubleAnimation>
27                                             <DoubleAnimation Storyboard.TargetName="lblItem" Storyboard.TargetProperty="FontSize" From="11" To="14" Duration="0:0:0.20"></DoubleAnimation>
28                                         </Storyboard>
29                                     </BeginStoryboard>
30                                 </EventTrigger.Actions>
31                             </EventTrigger>
32                             <EventTrigger RoutedEvent="ComboBoxItem.MouseLeave" SourceName="spItem">
33                                 <EventTrigger.Actions>
34                                     <BeginStoryboard>
35                                         <Storyboard>
36                                             <DoubleAnimation Storyboard.TargetName="spItem" Storyboard.TargetProperty="(Height)" From="40" To="23" Duration="0:0:0.20"></DoubleAnimation>
37                                             <DoubleAnimation Storyboard.TargetName="lblItem" Storyboard.TargetProperty="FontSize" From="14" To="11" Duration="0:0:0.20"></DoubleAnimation>
38                                         </Storyboard>
39                                     </BeginStoryboard>
40                                 </EventTrigger.Actions>
41                             </EventTrigger>
42                             <Trigger Property="IsMouseOver" Value="true">
44                                 <Setter TargetName="spItem" Property="Background" Value="#378FCF"></Setter>
45                                 <Setter TargetName="lblItem" Property="Foreground" Value="White"></Setter>
46                                 <Setter TargetName="colImage" Property="Width" Value="40"></Setter>
47                                 <Setter TargetName="delImage" Property="Visibility" Value="Visible"></Setter>
48                             </Trigger>
49                         </ControlTemplate.Triggers>
50                     </ControlTemplate>
51                 </Setter.Value>
52             </Setter>
53         </Style>

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

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

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

 

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

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

转载于:https://www.cnblogs.com/wzxinchen/p/4266152.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/263823.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nokia7610彩信设置

1. 进入功能表—工具—设置—连接设置 2. 进入接入点&#xff0c;按“选项”—“新增接入点”—“使用默认设置”。 3. 自定义连接名称&#xff0c;可为&#xff1a;中国移动彩信&#xff0c;数据承载方式&#xff1a;GPRS 接入点名称&#xff1a;cmwap 4. 用户名、提示输入…

matlab guidata两个,Matlab

%在控件本身函数中用hObject调用%在别的函数中&#xff0c;需要使用handles调用function varargout TestGUI(varargin)% TESTGUI MATLAB code for TestGUI.fig% TESTGUI, by itself, creates a new TESTGUI or raises the existing% singleton*.%% H TESTGUI …

spring boot jar包替换报错之Unable to open nested entry 'BOOT-INF/lib/cache-api-0.4.jar'.

spring boot用layout ZIP打出来的包能够支持外部classpath&#xff0c;但是当用rar/7zip替换其中的jar后&#xff0c;报下列错误&#xff1a; Unable to open nested entry BOOT-INF/lib/cache-api-0.4.jar. It has been compressed and nested jar files must be stored witho…

hadoop博客 oschina

http://my.oschina.net/Xiao629/blog?catalog449279

php用json交换二维数组,PHP和Javascript的JSON交互(处理一个二维数组)

我不得不承认&#xff1a;我是一个彻彻底底的JS白痴。但根据项目需要&#xff0c;不得不使用JSON&#xff0c;不管怎么说&#xff0c;经过一个晚上的学习&#xff0c;已经略有所成&#xff0c;记录下来。PHP的JSON类库我使用的是Services_JSON&#xff0c;没什么特别的优点&…

RoRoWoBlog 开源博客系统介绍

萝萝窝个人博客开源项目 以Asp.net MVC 2.0 ADO.Net Entity Framework 4.0 Unity 2.0 MvcPager JQuery 等技术框架&#xff0c;开发的个人博客系统。 支持MetaWeblog接口 通过MetaWeblog接口&#xff0c;可以将您个人博客系统中的博文&#xff0c;直接同步到您其它网站的博…

Python基础:模块化来搭项目

简单模块化 import 最好在最顶端sys.path.append("..")表示把当前程序所在位置向上提了一级在python3规范中&#xff0c;__init__.py并不是必须的。文件结构&#xff1a; . ├── utils │ ├── util.py │ └── class_util.py ├── src │ └── sub_…

(原)离开,只为更好的活着

序&#xff09;经过长时间的失眠&#xff0c;辗转反侧&#xff0c;开始默默的写下一篇文章&#xff0c;我不知道以后是怎样的方向&#xff0c;不过明天依旧会天亮。 入职&#xff09;那最初的梦想 有位朋友说&#xff0c;找工作一定不要找初创公司&#xff0c;那样你会疯狂的加…

Flask入门到放弃(四)—— 数据库

转载请在文章开头附上原文链接地址&#xff1a;https://www.cnblogs.com/Sunzz/p/10979970.html 数据库操作 ORM ORM 全拼Object-Relation Mapping&#xff0c;中文意为 对象-关系映射。主要实现模型对象到关系数据库数据的映射 优点 : 只需要面向对象编程, 不需要面向数据库编…

virtualbox安装centos6.5碰到的问题

今天无聊用virtualbox安装centos6.5 , 自己笔记本vm撑不住, 用公司的试试virtualbox先 安装快完成时 没有足够的内存配置kdump”&#xff08;在英文界面下提示的是“insufficient memory to configure kdump”&#xff09; 出现这个提示, 解决办法, 按这篇博客可以解决, 简单点…

matlab som聚类算法,使用SOM对数据进行聚类

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼6.7 2.5 5.8 1.8 37.2 3.6 6.1 2.5 36.5 3.2 5.1 2 36.4 2.7 5.3 1.9 36.8 3 5.5 2.1 35.7 2.5 5 2 35.8 2.8 5.1 …

程序从技术到管理:思维转变是关键

IT公司研发部门的管理人员大多是从公司内部的技术人员中提拔的。在快速发展的公司里&#xff0c;这样的机会更多。然而这种“半路出家”的转型也给我们带来了很多挑战&#xff0c;其中最关键的部分在于思维方式的转变。 从个人成就到团队成就。 无论是做管理还是做技术&#xf…

javascript技巧

1、作用域安全的构造函数 function Person(name,age){ if(this instanceof Person){ this.namename; this.ageage; this.getInfofunction (){}; }else{ new Person(name,age); } } 2、函数柯里化//使用闭包返回一个函数&#xff0c;函数的参数是外部函数传递内部自身函数的参数…

VLC 学习计划---文档阅读

一 videolan-howto-en-html 该文档完全描述了VideoLAN "流"的解决方法. VideoLAN 项目包括两个软件. 1) VLC:以前是视频流接收的客户端,但是现在也可以作为服务端工作.2) VLS:视频服务端,能发送 MPEG-1, MPEG-2 and MPEG-4 files, DVDs, digital satellite channels,…

php 重定向到https,php - 如何从HTTPS重定向到HTTP? - SO中文参考 - www.soinside.com

如果我了解您&#xff0c;以下代码将解决此问题&#xff1a;RewriteEngine OnRewriteCond %{HTTPS} offRewriteCond %{SCRIPT_FILENAME} !\/index\.php [NC]#the above line will exclude https://www.hellomysite.com/index.php# from the following rulesRewriteCond %{SCRIP…

JAVA 面试知识点

主要包括以下几个部分&#xff1a; Java 基础知识点Java 常见集合高并发编程&#xff08;JUC 包&#xff09;JVM 内存管理Java 8 知识点网络协议相关数据库相关MVC 框架相关大数据相关Linux 命令相关面试&#xff0c;是大家从学校走向社会的第一步。 互联网公司的校园招聘&…

rails中weill_paginate的paginate方法中不能使用额外参数的解决办法

我们知道高版本中的rails中的分页功能已经放在will_paginate这个gem中&#xff0c;我们在控制器方法中往往需要调用其paginate方法来实现分页数据集控制&#xff0c;举个例子&#xff1a;正常的情况我们想要每页显示10条记录可以这么写&#xff1a; Item.paginate(page:params[…

企业管理软件开发不能割裂各系统的功能

现今企业管理软件分类比较多&#xff0c;但在一个企业中可能随着自己的发展以及管理的需要&#xff0c;在不同时期会购买不同阶段的管理软件&#xff0c;出于各种考虑可能会买入不同厂商的软件系统&#xff0c;这样就带来各软件间的无缝接口问题&#xff0c;这个问题如不能及时…

sqlserver 导出mysql,sqlserver数据(表)导出到mysql

这里说明我的工具&#xff1a; Navicat Premium1 首先 navicat 连接到 sqlserver 数据库&#xff0c;也就是我要从这里导出那个 170 万条数据的表&#xff0c;然后选中表右键单击&#xff0c;选择导出向导2 然后选择文本文件&#xff0c;下一步3 核对下我们要导出的表&#xff…

Android ListView避免多线程加载一个同一资源

当我们的ListView中的Item包含图片&#xff0c;而且这些图片是同一资源&#xff0c;我们用多线程去加载图片&#xff0c;这时候可能就发生了这种情况。 比如线程是人&#xff0c;第一个人去做加载图片到缓存的工作&#xff0c;还没做好时第二个人要这同一张张图&#xff0c;结果…