精华内容
下载资源
问答
  • 测试开发笔记

    万次阅读 多人点赞 2019-11-14 17:11:58
    子模块 接口 灰盒测试 验证模块、子模块、接口是否符合 概要设计说明书 能够帮助更准确的 定位缺陷的所在,从而降低了定位缺陷的成本 定位准确快速 1接口测试有技术要求,技术实现难度大 2接口太多,数量庞大,做...

    测试开发笔记

    第一章 测试基础
    1.什么是软件测试
    2.软件测试的目的、意义(怎么做好软件测试)
    3.软件生命周期
    第二章 测试过程
    1.测试模型
    H模型
    V模型
    2.内部测试
    3外部测试
    验收测试(在系统测试之后)
    回归测试
    4.测试过程(干什么,怎么干)
    5.各阶段输入、输出标准以及入口、出口准则:(测试阶段过程要素)
    第三章 测试方法
    1.测试方法对比
    2.测试方法组合
    第四章 软件质量
    1.什么是软件质量
    2.质量要素
    3. 6大特性27个子特性ISO国际标准组织CMM/CMMI(Capability maturity model)能力程度度模型
    4.CMMI把企业分为5个等级
    5. CMM与CMMI的区别
    第五章 SQL
    约束
    1主键约束
    2 非空约束 not null
    3 外键约束 FOREIGN KEY
    4 默认约束
    5 检查约束 check
    6 唯一约束 unique
    SQL语句
    创建数据库.
    表、字段、类型
    查询
    批量处理
    视图/虚表 view
    索引
    存储过程 procedure
    事务 transaction
    触发器 trigger
    练习
    一、单表查询练习
    二、聚合函数练习
    三、分组查询练习
    四、嵌套查询练习
    五、联接查询练习
    六、外联接查询
    七、补充提高
    第六章 C语言
    C语言中的存储
    数据类型
    常量
    结构体
    条件/分支逻辑
    Switch
    If
    循环
    For
    while
    do…while
    函数
    第七章 Windows环境搭建
    一、名词注解与定义:
    C/S
    B/S
    进销存系统
    OA系统
    第八章 需求管理
    1.什么是需求
    2. 需求工程在做什么
    3. ★需求变更
    4.★需求的跟踪
    需求跟踪矩阵的作用
    需求的特点
    需求工程
    变更控制流程图
    第九章 缺陷管理
    缺陷相关概念
    缺陷管理相关概念
    BUG管理基本流程
    BUG单
    第十章 测试需求分析
    概念
    ★如何做测试需求分析
    ★UML统一建模语言(Unified Modeling Language)
    第十一章 配置管理
    1.什么是配置管理
    2.配置管理流程
    配置管理工具
    SVN操作过程手册
    一、 如何创建“project”项目版本库
    二、 如何查看创建的“project”项目版本库
    三、 在版本浏览器里面,创建文件,并进行检出
    四、 如何对该项目入基线
    五、 分支文件进行合并
    六、 分支冲突的解决
    第十二章 系统测试
    概念:
    分类:
    功能测试(Function testing中国 Feature testing国际)
    性能测试(Sercarity testing)
    安全性测试(Security Testing)
    安装测试
    GUI测试(Graphical user interface)
    可用性测试(Usability testing)
    异常性测试
    文档测试
    备份测试
    配置测试
    网络测试
    第十三章 用例设计
    等价类
    练习
    1.1年龄注册
    1.2.年龄注册
    1.3.扩充
    边界值
    2.1.年龄
    2.2.用户名注册
    2.3.变量命名
    2.4.进销存价格
    2.5.Windows文件命名
    总结
    边界值
    第十四章 系统测试执行
    测试环境搭建文档
    用例执行
    填BUG报告
    第十五章 QC(Quality Center)
    QC后台:
    QC前台:
    Requirements 需求模块
    Test Plan 测试用例模块
    Test Lab 测试执行模块
    第十六章 PYTHON
    Python的安装
    Python的集成环境
    数据类型
    运算符
    缩进
    控制语句
    IF条件
    WHILE循环
    FOR循环
    BREAK \ CONTINUE
    函数
    定义
    调用
    第十七章 单元测试
    单元测试概念
    单元测试静态测试
    单元测试动态测试
    测试评价准则
    逻辑覆盖率
    单元测试策略
    ⑴ 孤立测试
    ⑵自顶向下的单元测试策略
    ⑶自底向上的单元测试方法
    单元测试用例设计(基本路径覆盖法)
    程序控制流图
    单元测试执行
    单元测试框架
    第十八章 集成测试
    第一阶段总结
    Test platform
    Bug的其他说法
    第二阶段项目笔记
    一.建立项目JXC
    二.布置JXC
    三.配置SVN
    四.访问SVN
    进销存项目
    进销存项目总结
    测试需求分析
    1、定义测试范围
    2、建立需求项
    3、细化需求项
    4、需求覆盖率分析
    判定表
    3.1.读书选择
    3.2.Counter
    3.3:word中的判定表举例
    3.4.合并判定表
    3.4.密码修改
    3.5.进销存
    3.6.总结
    因果图
    4.1.字母判定
    4.2.自动售货机
    状态迁移
    5.1.飞机售票系统
    5.2.缺陷跟踪
    流程分析
    6.1.处理流程
    6.2.系统登录
    6.3.字母判断
    6.4.组合查询
    正交试验
    7.1.环境搭建
    7.2.Counter
    7.3.组合
    7.4.环境搭建
    其他
    输入域
    输出域
    异常分析
    错误猜测

    第一阶段
    第一章 测试基础
    1.什么是软件测试:
    两个依据(需求、测试用例),两个方法(手工、自动),一个对比(预期结果和实际结果的对比)
    2.软件测试的目的、意义:(怎么做好软件测试)
    初期: 尽量多的发现缺陷生成相关规范
    中期: 尽量早的发现缺陷
    后期: 尽量预防问题:通过以往的经验积累
    控制成本(贯穿始终)尽量少的时间和人力发现更多的缺陷
    3.软件生命周期:软件的产生直到报废或停止使用的生命周期。软件生命周期内有问题定义、可行性分析、总体描述、系统设计、编码、调试和测试、验收与运行、维护升级到废弃等阶段,也有将以上阶段的活动组合在内的迭代阶段,即迭代作为生命周期的阶段。

    如何尽量多的发现缺陷?
    沟通
    在测试前期与开发沟通 确认测试重点 确认测试的优先级
    了解开发人员技术和业务背景 业务水平 技术水平 代码质量 人员流动性
    在测试结束后
    对已发现的bug进行统计 知道高发概率bug 在新项目中要进行重点测试
    针对代码 代码复杂度
    版本管理
    针对基础测试基础版本要进行充分的测试
    验收前的最后一个版本一定要进行完全重复测试
    测试方法
    黑盒方法 功能问题 无法保证所有的代码逻辑都被执行到 用白盒测试思想补充黑盒测试
    静态测试方法 文档评审 代码走查
    测试过程
    上一阶段为下个阶段提供重点指导
    用户参与的测试或用户反映回来的错误和问题为下次测试的或测试补充的必备内容

    第二章 测试过程
    1.测试模型
    H模型:

    H模型图
    优点:
    1 介入早 与开发并行 更早的发现问题
    2 测试过程独立于开发过程 更客观 更主动
    V模型

    双V模型图
    ㈠需求阶段
    产品经理,项目经理,产品工程师写《需求规格说明书》Software Reqwirment Specaficalion(SRS)
    内容:需求项(业务,主要功能)需求子项,对子项的详细描述
    测试的工作:对需求进行测试和评审A系统测试计划《系统测试计划书》B系统测试计划《系统测试方案书》C系统测试实现《系统测试用例》
    ㈡设计阶段
    开发经理,架构师,开发工程师写出《概要设计说明书》High-level design(HLD)
    内容:系统程序中的模块,子模块和他们之间的关系和接口
    测试的工作:对HLD进行测试和评审A集成测试计划《集成测试计划书》B集成测试设计《集成测试方案书》C集成测试实现《集成测试用例》
    ㈢详细设计阶段
    开发工程师,架构师,写出《详细设计说明书》Low-level desragn(LLD)
    内容:函数 代码 逻辑
    测试工作:对LLD进行测试和评审A单元测试计划《单元测试计划书》B单元测试设计《单元测试方案书》C《单元测试用例》
    ㈣编码阶段
    开发工程师写代码
    优点:介入早,提高测试质量; 分成三个阶段,发现问题更有针对性;测试与开发并行,更好的利用项目资源。
    缺点:项目成本高;技术要求高,对人员要求高;并行工作中,一方未完成就会对整个造成延误。
    适用范围:规模大、软件成熟度高的项目。
    2.内部测试
    测试阶段 测试对象 测试方法 测试目的 经济价值 优点 缺点 必要性 资源
    系统测试
    system testing(ST) 整个系统
    (整个产品) 黑盒测试 验证产品是否符合需求规格说明书 能够保证产品以较高的的质量尽早的上市销售,从而使公司获取利润 1简单
    2技术要求低 1测试介入时间晚,修改成本高
    2有一些问题可能被遗留不会被修改 必须保证 1对被测产品
    2需求规格说明书
    3系统测试工程师
    4需求开发人员
    集成测试
    integration testing(IT) 模块
    子模块
    接口 灰盒测试 验证模块、子模块、接口是否符合
    概要设计说明书 能够帮助更准确的 定位缺陷的所在,从而降低了定位缺陷的成本 定位准确快速 1接口测试有技术要求,技术实现难度大
    2接口太多,数量庞大,做所有接口的集成测试成本高 不是必须做的,
    必须做测试的
    1公共的主要模块
    2核心模块
    3和外界软件接口模块 1被测的产品
    2概要设计说明书
    3集成测试工程师
    4概要设计人员
    单元测试
    unit testing(UT) 函数
    代码
    逻辑 白盒测试 验证函数代码逻辑是否符合详细设计说明书 能够最早的开展测试工作,降低修复成本,防止缺钱被扩大化(注意:加以重视:1公共的模块2全局性的数据结构3重要的使用频率较高的功能4以往项目经常出错的严重问题5复杂度较高的模块6当开发人员业务不熟悉编码不熟练的模块要进行单元测试) 介入时间早,发现问题早,修改成本低。 1技术难度高
    2工作量太大 不是必须的 1开发环境
    2LLD
    3单元测试工程师
    4架构师(详细设计人员)
    3外部测试:
    使用验收测试的原因
    1内部测试只能模拟用户使用却不能代替用户使用
    2由于专业不同业务背景不同无法模拟用户使用的习惯
    3测试人员和用户对产品的理解可能不同
    验收测试:(在系统测试之后)
    α测试:由用户组织一部分人在开发环境下来对产品进行测试 如网游的内侧
    β测试:所有系统使用者都可以参加的测试(在实际使用环境下) 如网游的公测
    分类 测试过程 参与人员 目的 过程主要内容
    针对项目类软件 验收测试 开发人员:提供满足验收要求的软件或系统,或用户需要的相关开发文档
    测试人员:
    1、搭建验收测试环境
    2、准备验收测试用例
    3、准备用户需要的相关测试文档
    4、组织人员进行验收演示
    用户代表:对系统进行一定的试用
    客户代表:签字确认验收是否通过
    行业:负责在验收过程中提出问题并协助用户和客户检查系统是否满足需求 1、检查软件的功能是否与用户最初需求相一致
    2、是客户回款的标志 1、进行验收前准备
    A、准备相关的资料
    B、搭建验收测试环境
    C、指定相关的验收参与人
    2、进行验收演示
    A 、对产品使用进行演示
    B、回答专家、用户的提问
    3、签署验收报告
    针对产品类软件 α测试 开发人员:
    1、提供可以进行α测试的软件
    2、负责修改用户代表发现的问题
    测试人员:
    1、检查或协助用户填写缺陷报告
    2、向用户学习相关的使用关注点
    邀请的用户或客户代表(付费)
    1、按照自己的操作习惯使用软件,提出易用性等方面的问题和改进建议 明确用户的使用体验,提高产品的适用范围和使用质量标准 1、明确进行α测试的版本
    2、邀请潜在用户进行使用体验
    3、针对用户提出的问题进行修复或改进
    β测试 潜在用户:
    1、安装软件并使用
    客服人员:
    记录并反馈用户的问题 提前占领市场 1、发布一个下载地址
    2、用户进行软件下载并使用
    回归测试:
    回归测试可以发生在任何一个阶段
    分为完全回归和选择回归
    回归范围 回归分类 特点 优点 缺点 适用范围
    完全回归 完全重复法 每次回归测试都要执行全部测试用例 回归测试充分,覆盖面广,不容遗漏 工作量大,时间长,成本高 时间充裕且测试资源较充分时,第一次和最后一次做回归测试的时候用这种方法
    选择性回归 覆盖修改法 每次回归测试时只执行发现错误的用例 时间最短,成本最低,简单效率高 回归测试不充分,漏洞较多 时间较紧且人力资源不足时,中间版本的测试轮次可以使用,关联度比较小的模块和功能
    周边影响法 每次回归除了执行发现bug的用例外,还要执行与其相关的用例 在考虑了测试成本的基础上有效提高了回归测试的质量 效率 很难确定影响的周边范围,相关用例定位较困难 适合于全局数据结构被修改或公共模块被修改,或核心算法业务被修改时,公用的模块,关系、关联复杂的模块
    指标达成法 每次回归测试达到规定的语气指标
    就可以停止测试了 所有的测试都可度量 1指标生成需要很长的周期,
    很多的项目区累计经验
    2要有比较稳定的团队这个指标才有意义 成熟度较高的测试团队应用于指标达成法
    (适用度很低,很少有公司使用)

    分类 步骤 优点
    确定周边
    范围的方法 界面检查法 1明确被修改的功能 简单
    2修改功能的上下游功能
    3调用修改功能的功能和
    修改功能调用了的功能
    4和修改功能游相同输入输出的功能
    5在测试中执行上诉关联的用例
    代码检查法 1明确被修改的函数和代码 准确,全面
    2在整个系统中检查所有
    调用了修改函数的函数
    3明确上诉所有函数对应的界面
    4测试上诉界面测试用例
    4.测试过程(干什么,怎么干)
    整个系统的内容 需求项(业务、主要功能) 需求项 测试计划 测试需求项 系统测试阶段
    需求子项 测试方案 测试需求子项
    详细内容 测试用例 具体如何进行测试
    整个系统的集成 概要设计 概要设计项 测试计划 集成测试阶段
    概要设计子项 测试方案
    具体内容 测试用例
    整个系统最小单元 详细设计 函数 测试计划 单元测试
    逻辑 测试方案
    代码 测试用例

    5.各阶段输入、输出标准以及入口、出口准则:(测试阶段过程要素)
    系统测试 入口准则 输入文档 输出文档 出口准则
    系统测试计划 开发计划通过评审并入基线
    需求规格说明书通过评审并入基线 开发计划书
    需求规格说明书 系统测试计划书 系统测试计划书通过评审并入基线
    系统测试设计 系统测试计划书通过评审并入基线 需求规格说明书
    开发计划书
    系统测试计划书 系统测试方案书 系统测试方案书通过评审并入基线
    系统测试实现 系统测试方案书通过评审并入基线 需求规格说明书
    系统测试计划书
    系统测试方案书 系统测试用例
    预测试项 系统测试用例、预测试项通过评审并入基线
    系统测试执行 系统测试用例、预测试项通过评审并入基线
    集成测试报告通过评审并入基线 需求规格说明书
    系统测试计划书
    系统测试方案书
    系统测试用例
    预测试项 缺陷报告
    预测试项报告
    系统测试报告 系统测试报告、预测试项报告、缺陷报告通过评审并入基线
    集成测试 入口准则 输入文档 输出文档 出口准则
    集成测试计划 概要设计说明书通过评审并入基线 概要设计说明书 集成测试计划书 集成测试计划书通过评审并入基线
    集成测试设计 集成测试计划书通过评审并入基线 集成测试计划书
    概要设计说明书 集成测试方案书 集成测试方案书通过评审并入基线
    集成测试实现 集成测试方案书通过评审并入基线 集成测试计划书
    集成测试方案书
    概要设计说明书 集成测试用例 集成测试用例通过评审并入基线
    集成测试执行 集成测试用例通过评审并入基线
    单元测试报告通过评审并入基线 集成测试计划书
    集成测试方案书
    集成测试用例
    概要设计说明书 集成测试报告
    缺陷报告 集成测试报告、缺陷报告通过评审并入基线
    单元测试 入口准则 输入文档 输出文档 出口准则
    单元测试计划 详细设计说明书通过评审并入基线 详细设计说明书 单元测试计划 单元测试计划通过评审并入基线
    单元测试设计 单元测试计划通过评审并入基线 详细设计说明书
    单元测试计划书 单元测试方案书 单元测试方案书通过评审并入基线
    单元测试实现 单元测试方案书通过评审并入基线 详细设计说明书
    单元测试计划书
    单元测试方案书 单元测试用例 单元测试用例通过评审并入基线
    单元测试执行 单元测试用例通过评审并入基线 详细设计说明书
    单元测试计划书
    单元测试方案书
    单元测试用例 单元测试报告
    缺陷报告 单元测试报告、缺陷报告通过评审并入基线

    第三章 测试方法
    测试方法对比
    分类方法 测试方法名称 依据 测试对象 理论上的测试目的 实际工作中的测试目的 测试评估标准 测试环境 测试工作介入点 优点 缺点 适用范围
    按照不同的测试对象划分(黑白灰盒的区别) 黑盒 SRS 整个软件产品 检查软件的功能实现是否与SRS相一致 尽早进行验收,收回开发成本 需求覆盖率 尽量与用户环境相一致 只要功能可以进行操作 简单,测试效率高 1、无法保证所有的代码逻辑都被测试到
    2、后台相关的非界面处理可能会遗漏(文件、数据库)
    3、当前功能与其他功能有联系的部分可能也会被遗漏 适合进行功能、性能等使用和外部特性的测试适用范围广泛,适用所有可见功能
    白盒 LLD 代码逻辑函数 检查代码的逻辑实现是否与LLD相一致 尽早发现问题缺陷,降低缺陷修复成本.便于定位问题 逻辑覆盖率
    语句覆盖
    分支覆盖
    条件覆盖
    分支-条件覆盖
    路径覆盖 开发环境 只要独立的函数或类代码编写完成后 覆盖充分,可以覆盖到每行代码 技术较难
    效率较低
    成本较高 针对核心业务、复杂算法、公共模块、全局数据结构、新增功能
    灰盒 HLD 模块\子模块接口 检查接口实现是否与HLD相一致 逐步集成,降低缺陷定位成本 接口覆盖率 子系统集成尽可能和用户环境一致,模块内部接口以及模块间接口可以在开发环境下进行
    子系统间的接口最后要在与用户环境下测试 进行测试的接口模块已完成 可以提早定位和发现问题 技术最难
    成本最高 公共模块之间的调用,复杂度较高的模块调用、使用频率较高的模块调用

    特点 分类 优点 缺点 适用范围
    按照是否运行程序划分 静态 不执行程序 1、文档评审
    A、正规检视
    B、技术评审
    C、同行评审
    2、静态分析技术
    A、控制流分析
    可以发现以下缺陷
    1、死循环
    2、执行不到的语句
    3、不存在的语句
    B、数据流分析
    可以发现以下缺陷
    1、变量未定义被使用
    2、变量已定义未使用
    C、信息流分析
    可以帮助开发人员定位缺陷
    1、输入变量与语句的关系
    2、输出变量与语句的关系
    3、输入变量与输出变量的关系 较动态测试时间早,不用写代码 工作量大 重要的功能模块、核心的业务、算法
    公共模块
    动态 执行程序 黑和测试
    动态白盒:插装—在代码中加入print打印语句,检查程序的中间运行结果 复杂,效率高 测试较晚,写代码 所有功能

    优点 缺点 适用范围
    按照不同的测试手段划分 手工 能够主动的发现bug 重复工作量大,容易引入疲劳缺陷,只能依靠见到的界面 绝大多数的场合
    自动化 可以无限制不断重复,把人从劳动里解放出来,提高劳动效率,提高了测试质量,能发现人不能发现的错误 无法发现脚本中未写明的缺陷 GUI界面稳定
    回归阶段
    需求稳定且功能已实现时才进行脚本的编写
    性能测试工具:提取相关的系统数据,构造并发用户
    测试方法组合
    测试方法组合 典型案例 使用时机 特点
    黑盒
    黑盒静态手工      
    黑盒静态自动化      
    黑盒动态手工      
    黑盒动态自动化功能测试 Mercury的QTP:用于检测应用程序是否能够达到预期的功能及正常运行
    通过自动录制、检测和回放用户的应用操作 1、能够有效地帮助测试人员对复杂的企业级应用的不同发布版进行测试
    2、提高测试人员的工作效率和质量,确保跨平台的、复杂的企业级应用无故障发布及长期稳定运行
    IBM Rational Robot 是功能测试工具 它集成在测试人员的桌面 IBM Rational TestManager 上,在这里测试人员可以计划、组织、执行、管理和报告所有测试活动,包括手动测试报告。这种测试和管理的双重功能是自动化测试的理想开始。
    Borland SilkTest属于软件功能测试工具 是Borland公司所提出软件质量管理解决方案的套件之一。这个工具采用精灵设定与自动化执行测试,无论是程序设计新手或资深的专家都能快速建立功能测试,并分析功能错误。
    基于Java语言的功能和性能测试工具 JMeter是Apache组织的开放源代码项目 主要针对Java语言 它是功能和性能测试的工具,100%的用java实现
    黑盒动态自动化性能测试 Mercury的LoadRunner:是一种预测系统行为和性能的负载测试工具。 通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题 能够对整个企业架构进行测试。通过使用LoadRunner ,企业能最大限度地缩短测试时间,优化性能和加速应用系统的发布周期。
    Microsoft Web Application Stress Tool 是由微软的网站测试人员所开发,专门用来进行实际网站压力测试的一套工具。 功能强大的压力测试工具 您可以使用少量的Client端计算机仿真大量用户上线对网站服务所可能造成的影响
    webload是RadView公司推出的一个性能测试和分析工具 它让web应用程序开发者自动执行压力测试; webload通过模拟真实用户的操作,生成压力负载来测试web的性能。
    白盒
    白盒静态手工      
    白盒静态自动化   检查语法规范、语法逻辑  
    白盒动态手工 目前的最流行的单元测试工具是xUnit系列框架 常用的根据语言不同分为JUnit(java),CppUnit(C++),DUnit(Delphi ),NUnit(.net),PhpUnit(Php )等等。 该测试框架的第一个和最杰出的应用就是由Erich Gamma (《设计模式》的作者)和Kent Beck(XP(Extreme Programming)的创始人 )提供的开放源代码的JUnit。
    白盒动态自动化 Jtest是parasoft公司推出的一款针对java语言的自动化白盒测试工具,它通过自动实现java的单元测试和代码标准校验,来提高代码的可靠性。parasoft同时出品的还有C++ test,是一款C/C++白盒测试工具    
    灰盒
    灰盒静态手工      
    灰盒静态自动化      
    灰盒动态手工      
    灰盒动态自动化 BMC的APPSight 系统会将问题发生的相关信息完整录制下来,包括问题发生的现场场景、信息及分析等,从而快速切入到问题根源  
    测试管理工具 是业界第一个基于Web的测试管理系统,它可以在您公司内部或外部进行全球范围内测试的管理。通过在一个整体的应用系统中集成了测试管理的各个部分,包括需求管理,测试计划,测试执行以及错误跟踪等功能,TestDirector极大地加速了测试过程。

    1自动化测试就是用程序驱动程序的测试
    2黑白灰测试的区别
    测试的对象不一样,对于代码实现逻辑程度不一样(黑盒不需要了解代码实现,白盒需要完全了解代码实现,灰盒需要部分了解代码实现)
    3静态与动态测试的区别
    被测程序执行与否 静态不执行程序包括文档评审静态分析技术代码走读,动态包括黑盒测试和动态分析技术
    4自动化合手工测试的不同
    测试手段不同

    第四章 软件质量
    1.什么是软件质量
    质量:确定一个实体的特性满足需求的程度
    内部质量:软件研发过程中,评价的软件质量
    外部质量:软件上市后,用户评价的质量
    过程质量:评价软件研发中每个过程的质量
    软件质量的三个层次
    ⑴流程质量,领导关注 ⑵产品质量 测试工程师关注 ⑶使用质量 用户关注
    2.质量要素
    质量铁三角 : 技术 过程 组织
    3. 6大特性27个子特性ISO国际标准组织CMM/CMMI(Capability maturity model)能力程度度模型

    质量模型列表
    质量模型特性 子特性 特点 常见测试点 案例说明
    功能性 适合性 合适的功能(用户提出要有哪些功能)功能的必要性 验证功能是否满足需求的要求,检测做没做 打电话、听音乐、发信息
    准确性 正确的功能 需求文档中的预期动作和预期输出,做对没有 信息的发送内容是否正确
    互操作性 和其他软件的互相操作 第三方软件的交互 word文档对打印机驱动程序的操作
    保密安全性 保护信息和数据 保护得到授权的人或者系统能正常访问相关的信息或数据 1、登录的用户名和密码
    2、权限使用
    3、防止DOS攻击(拒绝访问攻击)4、系统数据的保护和加密,如密码的加密
    5、传输加密,如密码的网络传输
    6、防病毒
    7、放溢出,如char与varchar的字符数
    保证未授权的人或系统无法看到相关的信息或数据
    功能性的依从性 遵循功能性相关的标准、约定或法规 是否符合国家法律规定 如色情网站
    可靠性 成熟性 缺陷尽可能的少    
    容错性 提前考察的异常情况出错问题 整个系统的外部接口 如word打印时,打印机死机出现报错,但不影响word的使用
    易恢复性 失效后恢复原有功能、性能 系统的性能测试 如网游延迟卡死现象。系统提示内存不足。银行系统的心跳监听。灾难备份。
    可靠性的依从性 法律法规   灾难备份。
    易用性(CUI测试) 易理解性 (快速理解) 系统交互的信息是否准确、清晰、易懂,指导下一步操作。 系统提示信息是否准确 如网银密码超出位数报错
    易学性 (快速上手) 易用好学 是否有说明书、是否在线帮助、是否有提示信息 msn的帮助手册
    易操作性 (快速做完) 方便快速使用 操作的直观程度,操作步骤、操作动作多少与时间长短 鼠标、gui层数、安装过程
    易测试性 软件可控 提供工具给测试工程师,可以控制系统运行,以达到测试目的 windows的性能工具与服务管理工具
    软件可观察 通过辅助手段可  
    吸引性 外观 外观  
    易用性的依从性 法律法规    
    可移植性 适应性 (跨平台、跨语言) 软件产品无需采用有别于为考虑该软件的目的而准备的活动或手段就可能适应不同的指定环境的能力;是否适应其他系统环境 软件、硬件、外设、数据库 微软与苹果的前期竞争。主板与CPU
    易安装性 在指定环境中是否易于安装 主流平台和系统100%测试用例,非主流10% flash安装
    共存性 不同的其他系统能共同运行 1、功能是否能正常运行满足要求
    2、系统性能能满足要求 是否会抢占资源。迅雷和pplive抢占资源。杀毒软件,瑞星和金山不能共存
    易替换性 替代为其他相同功能的产品的能力 升级过后的系统是否会造成系统崩溃 软件升级补丁升级
    可移植性的依从性 法律法规    
    效率-性能 时间效率 规定条件下,软件产品执行其功能时,提供适当的响应和处理时间以及吞吐率的能力 系统的反应时间 提款机取款时间的快慢
    资源效率 在规定条件下,软件产品执行其功能时,使用合适的资源数量和类别的能力 做一件事所占用的系统资源 电器所消耗的电能多少
    效率依从性 法律法规    
    维护性-维护的难易程度与成本 易分析性 软件产品诊断软件中的缺陷或失效原因或识别待修改部分的能力 辅助工具或者日志文件或者常用问题帮助手册 qq异常退出的帮助文件
    易改变性 代码容易被修复或修改 高内聚,低耦合  
    稳定性 软件产品避免由于软件修改而造成意外结果的能力 长期的监控一个系统的运行情况和系统的资源情况 淘宝的系统监控
    维护性的依从性 法律法规

    配置管理
    配置工具 有的话 用的工具叫什么名字
    安装------B/S(浏览器)(check in /check out:原理) C/S(客户端)

    4.CMMI把企业分为5个等级

    5.CMM与CMMI的区别
    cmmi:是不同cmm的集成,集成并发扬cmm的优点,并借鉴其他模型的优点融入新理论和实际研究成果,不仅能应用于软件领域,而且能应用于系统过程和其他过程领域,Cmmi和cmm最大不同: Cmmi1.1版本包含4个成分:系统工程(SE)、软件工程(SW)、应用集成产品和过程开发(IPPD)、供应商外包管理(SS) Cmmi有2种表示方法: 阶段式 连续式

    第五章 SQL
    数据库的价值目标:
    数据库的技术(不只是界面 还要知道数据库逻辑 1.要对数据库的设计理解 2.还有数据库对象的关系3.数据库的常见命令)
    常见数据库:MySQL Access(单机) MS-SQL(交互好) Oracle Sybase DB2
    MySQL 小巧 效率高 免费
    后三种Oracle Sybase DB2是大型收费,数据安全和备份好
    数据库作用:组织、存储、处理
    关系型数据库
    第一日:
    关系型数据库
    数据库的作用

    索引
    视图
    存储过程
    触发器
    事务 对象 优缺点 使用范围
    SQL Server具体操作
    建库
    建表
    备份 恢复 操作手册: 建库 建表 备份 恢复
    第二日:
    查询命令
    单表查询
    多表查询
    查询 统计功能测试点
    第三日:
    新增功能 新增功能测试点
    更新命令 修改功能测试点
    删除命令 删除功能测试点
    补:
    存储过程 学会构造大量测试数据
    触发器 了解 看懂 如何测试

    RDBMS 关系型数据库
    SQL Structured Query Language 结构化查询语言
    C/S Client/Server 客户 服务器
    B/S Browser/Server 浏览器 服务器
    第一天:
    文件类型:文件存储位置改变 程序代码更新 对大量数据量处理不恰当
    数据库的意义”
    1数据的重用(硬盘)
    2检索速度要提高(分类存储)
    3把数据与代码的耦合度降低(数据存放位置与代码无关)
    数据库管理系统 SQLServer
    Oracle
    MySQL
    DB2
    数据库的表结构
    1.数据分类
    2.数据关联
    数据库设计评审点
    1.数据存储是否有重复现象 不同表中是否存在相同字段(该字段既不是主键也不是外键)
    2.是否符合范式要求 同一个表中存在数据重复字段不要超过两个以上 可以保证冗余数据很少
    可以考虑有适当冗余
    3.对于业务有频繁查询要求的数据表
    4.表间关系是否正确 是否按照业务要求进行了数据关联
    5.数据库字段以及表的设计是否充分 数据字段内容是否涵盖需求要求的所有数据
    数据字段类型 及长度是否符合需求

    表 基本组成单元 有记录和 字段组成
    表中每一条数据
    存储数据
    数据分类
    字段位置变化 不影响程序
    索引 建立在字段上 可以对字段进行排序 同一张表索引
    更新索引字段值的时候 新增会打乱索引的顺序时
    视图 建立在数据库上 封装查询命令 存储数据 方便查询调用 提示安全性 显示结果固定
    如果在执行事务时没有执行完就实行事务回滚
    事务:是有两条以上的数据库命令组成的原子集合.该事务中的那条命令要么一次性执行成功,要么都不执行,如果在执行过程中执行失败那么该事务就进行回滚,将数据恢复到执行之前的状态
    是否存在某些业务要封装成事务?
    优点:可以保证有关联的数据库操作所对应的关联所需要的完整性和统一性
    缺点:不要将不相关的操作放在同一个事务中,否则会降低执行成功率和效率

    存储过程 存储过程封装了多条SQL命令,必须存放在数据库服务端
    优点: 减少了网络传输SQL命令的压力
    提高了访问的安全性
    SQL 命令存放在服务器端执行效率高
    缺点:需要编写和设计调试 它是一段程序 对于大批量的数据较弱 避免分支和循环
    触发器 自动执行 并且只能对一张表中进行触发 当某一张中发生操作时需要同时对其让表的数据进行操作可以考虑创建触发器

    过程:
    检查SQL服务是否启动
    图形界面建库 建表
    企业管理器
    命令行方式
    查询分析
    主键 用一标记该表中的记录 不能重复 不能为空
    外键 通过外键与其他表中的数据进行关联 只能是以存在主键值

    当前表的数据要被其他表使用时 要有主键
    外键 有主键存在时就要设置对应的外键

    搭建测试环境时或给用户安装产品
    海达票务处理
    C/B 建立空库hdpw1
    恢复
    安装服务器端
    安装客户端
    admin admin

    约束:
    1主键约束
    1 主键约束 primary key
    –特点:约束的字段数据,不能为空、不能重复。
    如果插入或者更新的数据为空或者重复将不允许进行操作。

    –语法:
    1)不命名的主键约束
    create table student
    (
    No int primary key,
    Name char(10),
    Sex char(2),
    ruxu datetime
    );
    2)命名的主键约束
    create table student
    (
    No int,
    Name char(10),
    Sex char(2),
    ruxu datetime,
    constraint pk_001 primary key(No)
    );
    3)删除主键约束
    –语法:alter table 表名 drop constraint 约束名;
    alter table student drop constraint pk_001;
    4)改主键约束
    –语法:alter table 表名 alter column 字段名 类型 not null;
    –alter table 表名 add constraint 约束名 primary key (字段名);
    alter table student alter column No int not null;
    alter table student add constraint PK_002 primary key(No);
    2 非空约束 not null
    1)增加非空约束
    create table student1
    (
    No int not null,
    Name char(10),
    Sex char(2),
    ruxu datetime
    );

    2)改非空约束
    alter table student1 alter column Name char(10) not null;
    3 外键约束 FOREIGN KEY
    –新增外键关系
    1)非命名的外键约束
    create table grade
    (
    no int foreign key references student(No),-- 字段名 字段类型 foreign key references 主表(主键字段)
    grade float
    );
    2)命名的外键约束
    create table grade
    (
    No int,
    grade float,
    constraint fk_001 foreign key (no) references student(no)
    );
    3)删除外键约束
    alter table grade drop constraint fk_002;
    4)改外键约束
    alter table grade add constraint FK_002 foreign key(no) references student(no) on delete cascade; --当主表进行数据删除时,从表一起删除
    alter table grade add constraint FK_002 foreign key(no) references student(no) on update cascade; --当主表进行数据更新时,从表一起更新
    alter table grade add constraint FK_002 foreign key(no) references student(no) on delete no action; --当主表进行数据删除时,违反了外键约束,拒绝进行删除操作
    alter table grade add constraint FK_002 foreign key(no) references student(no) on update no action; --当主表进行数据更新时,违反了外键约束,拒绝进行更新操作
    4 默认约束
    –default 当 一个字段不填写内为空时,默认插入一条数据
    1)增加default 约束
    create table sex
    (
    id int,
    sex char(2) default ‘男’
    );
    2)改 default 约束
    5 检查约束 check
    –只有满足检查约束的数据才能添加到表中,不满足的加不进来
    1)不命名的检查约束
    create table balance
    (
    id int,
    money float check (money >= 0)
    );

    2)命名的检查约束
    create table balance
    (
    id int,
    money float,
    constraint CK_001 check (money >= 0)
    );
    3)删除检查约束
    alter table balance drop constraint ck_001;

    4)改检查约束
    alter table balance add constraint CK_002 check (money >= 0);
    6 唯一约束 unique
    –对于字段输入的内容必须是唯一的,不能重复,但可以为空,多个字段都可以输入唯一约束
    1)不命名唯一约束
    create table tiger
    (
    tig_id int,
    tig_name char(10) unique
    );

    2)命名唯一约束
    create table tiger
    (
    tig_id int,
    tig_name char(10),
    constraint UN_001 unique(tig_name)
    );

    3)删除唯一约束
    alter table tiger drop constraint UN_001;
    4)改唯一约束
    alter table tiger add constraint UN_002 unique(tig_name);
    SQL语句
    创建数据库.
    语法:create database 数据库名字;
    数据库名字不能使用数字作为开头。可以使用字母或者_作为开头
    create database _51testing;

    开辟两块空间,一个是保存日志的,一个是保存表的
    删库 DROP DATABASE 库名;
    单行注释 ——
    多行注释 /* */
    切换数据库
    语法:use 数据库名;
    use _51testing;
    转换当前使用数据库
    备份数据库
    BACKUP DATABASE 库名 TO DISK=’文件名’;
    如BACKUP DATABASE xuanke TO DISK=’d:\test.bak’;
    恢复数据库
    RESTORE DATABASE 库名 FROM DISK=’文件名’;
    如RESTORE DATABASE xuanke FROM DISK=’D:\test.bak’;
    表、字段、类型
    建表
    语法:create table 表名字 ( 字段名 字段类型 字段约束 , 字段名 字段类型 字段约束 );
    create table tmp
    (
    NO int primarykey,
    NAME char(10)
    );
    字段约束可以没有
    删除表
    语法:drop table 表名
    注意:外键约束
    drop table grade
    建立外键
    Sid INT FOREIGN KEY (sid) REFERENCES student (sid)
    CREATE DATABASE xuanke1;
    USE xuanke1;
    –DROP database xuanke1;
    CREATE TABLE student1
    (sid INT PRImary KEY,
    sname CHAR(20) NOT NULL,
    sex CHAR(2) NOT NULL CHECK(sex IN (‘男’,‘女’)),
    card_id CHAR(20) NOT NULL unique CHECK(len(card_id) = 18),
    calss ChAR(20) NOT NULL,
    major CHAR(20) NOT NULL,
    birth_year INT CHECK(birth_year BETWEEN 1900 And 3000) NOT NULL,
    enrollment DATETIME NOT NULL,
    tel CHAR(20)
    )
    CREATE TABLE course1
    (
    cid INT PRIMARY KEY,
    cname CHAR NOT NULL,
    tname CHAR(20) NOT NULL,
    ctime DATETIME NOT NULL,
    cadress CHAR(20) NOT NULL,
    pre_course_id INT,
    )
    CREATE TABLE student_course1
    (
    sid INT FOREIGN KEY (sid) REFERENCES student1 (sid),
    cid INT FOREIGN KEY (cid) REFERENCES course1 (cid) PRIMARY KEY(sid,cid),
    grade INT
    )
    插入一条记录
    在表中增加数据(insert into)
    语法:
    1对表中所有的字段添加数据:insert into 表名 values (第一个字段的值,第二个字段的值,第三个字段的值…);
    注意1:values 值得个数一定要和表中字段的个数相等,一一对应。
    注意2:values 值得数据类型,必须要和表中字段的数据类型相匹配。int fload 值是数字,如果char 值得字符要用’值’。
    注意: 英文单引号 逗号 ,日期 要加单引号

    insert into student values (5,‘郑佳祺’,‘男’,‘003’,‘2011-08-23’,‘北京’,‘1979-01-01’);
    insert into student values (6,’’,‘男’,‘003’,‘2011-08-23’,‘北京’,‘1979-01-01’);
    insert into student values (7,‘庞鹏珏’,‘男’,3,‘2011-08-23’,‘北京’,‘1979-01-01’);

    2对表中指定字段添加数据:insert into 表名(字段1,字段2,字段3) values (值1,值2,值3);
    insert into student(stuid,stname,sex,family) values (8,‘齐倩’,‘女’,‘天津’);
    insert into student(stuid,stname,sex,family,birthday) values (9,‘例子1’,‘女’,‘天津’,’’);
    insert into student(stuid,stname,sex,family,class) values (10,‘例子2’,‘女’,‘天津’,’’);
    insert into student(stuid,stname,sex,family,class) values (11,‘例子3’,‘女’,‘天津’,NULL);
    insert into student(stuid,stname,sex,family,class) values (12,‘例子4’,‘女’,‘天津’,‘NULL’);
    删除数据
    语法:1.delete from 表名
    注意: 删除表中数据时要考虑约束。
    同样用法 truncate table 表名
    delete from grade
    truncate table grade;
    2.delete from 表名 [where 条件]
    delete from student where stname = ‘例子1’;
    1)单一条件
    DELETE FROM student WHERE sid=500;
    2)复合条件
    DELETE FROM student WHERE sex=‘男’ AND major=‘计算机’;
    DELETE FROM student WHERE major=‘计算机’ OR major=‘1’;
    DELETE FROM student WHERE sid BETWEEN 1 AND 500;
    DELETE FROM student WHERE sid>=1 AND sid<=500;
    3)模糊条件
    DELETE FROM student WHERE major LIKE ‘计算机%’; ———— % 通配符(计算机后面任意位任意字符)
    DELETE FROM student WHERE major LIKE ‘%务’;
    DELETE FROM student WHERE class LIKE ‘4%期’;
    DELETE FROM student WHERE class LIKE ‘4_期’; ———— _ 通配符(4后面一位的任意字符)
    DELETE FROM student WHERE sname LIKE ‘[xw]%’; ———— [] 从括号中取任意一个值
    Truncate table student 删除student表记录内容 但是后面不能加条件(不记录日志,速度快)
    更新表数据(修改一条记录)
    更新表数据
    –语法:update 表名 set 字段 = 值,字段2=值[where 条件]
    –注意1:字段类型 和 值得类型 要匹配,主键的值可以被修改
    –注意2:为了保证表数据的完整一直,最好在更新是增加WHERE 条件。
    select * from student
    update student set sex = ‘男’ ;
    update student set sex = ‘女’ where family = ‘天津’
    update student set family = ‘广州’ where family = ‘北京’
    将学号小于50的记录的入学时间更改为2011-5-18
    UPDATE student SET enrollment=‘2011-5-18’ WHERE sid<50;
    将‘计算机’专业的学生转成“计算机科学与技术”
    UPDATE student SET major=‘计算机科学与技术’ WHERE major LIKE ‘计算机’;
    将专业为“信管”开头和“汽车”开头的专业转成“自动化控制”
    UPDATE student SET major=‘自动化控制’ WHERE major LIKE ‘信管%’ OR major LIKE ‘汽车%’;
    将所有省为“北京”和“北京市”的记录统一成“北京”,并将这些记录的班级改成49期
    UPDATE student SET province=‘北京’,class=‘49期’ WHERE province LIKE ‘北京%’;
    将入学时间大于1985-1-1记录的出生时间改为比入学年月早18年
    UPDATE student SET borth_year=year(enrollment)-18 WHERE enrollment > ‘1985-1-1’;
    year() 是求DATE类型的年份
    查询
    简单的查询记录基础语法
    基础语法 select 查询的内容(也就是字段名1,字段名2) from 表名 where 条件
    (用*号可以显示所有字段)
    查询李进峰的所有基本信息
    SELECT * FROM student WHERE sname=‘李进峰’;
    查询李进峰和菲菲的所有基本信息
    SELECT * FROM student WHERE sname=‘李进峰’ OR sname=‘菲菲’;
    SELECT * FROM student WHERE sname IN (‘李进峰’, ‘菲菲’);
    查询所有姓张的同学的所有基本信息
    SELECT * FROM student WHERE sname LIKE ‘张%’;
    查询姓名中有“宇”的同学的所有信息
    SELECT * FROM student WHERE sname LIKE ‘%宇%’;
    查询姓名长度为3,第一个字为“李”最后一个字是“照”的记录
    SELECT * FROM student WHERE sname LIKE ‘李_照’;
    查询所有姓张和姓李的同学的学号和姓名以及出生年月
    SELECT sid,sname,borth_year FROM student WHERE sname LIKE ‘张%’ OR sname LIKE ‘李%’
    SELECT sid,sname,borth_year FROM student WHERE sname LIKE [张李]%’;
    查询姓“杨”并且所在省份为“河北省”的同学的学号
    SELECT sid FROM student WHERE sname LIKE ‘杨%’ AND province=‘河北省’;
    查询”北京”、”湖南”和”河南省”同学的所有信息
    SELECT * FROM student WHERE province=’北京’ OR province=‘湖南’ OR province=‘河南省’
    SELECT sid,sname,borth_year FROM student WHERE province IN (‘北京’, ‘湖南’, ‘河南省’);
    查询姓“”李但是所在省份没有“湖南”字样的同学的省份证号码
    SELECT card_id FROM student WHERE sname LIKE ‘李%’ AND province NOT LIKE ‘%湖南%’
    查询18岁(不含18)以前上学的同学的姓名和电话
    SELECT sname,tel FROM student WHERE year(enrollment)-borth_year<18;
    查询所有大于25岁的人的所有信息
    SELECT * FROM student WHERE year(getdate())-year(enrollment)>25;
    通配符
    使用环境,用于模糊查询,连接符号用Like 而非 =

    • 代表全部,所有,没有规定数量和内容。一般用于select 后面 不作为条件
      _ 代表一个字符,一个数据位。中文要用两个__ 。作为条件使用
      % 代表任意的,没有规定数量和内容。作为条件使用
      [值1,值2,值3] 从括号内的任意值。作为条件使用
      select * from student where stname like ‘张%’
      select * from student where stname like ‘[张李]%四’
      运算符。条件
      – = 两边内容相同。
      – > 左边大于右边。
      – < 右边大于左边。
      – >= 左边大于等于右边。
      – <= 右边大于等于左边。
      – <> 或者 != 不等于
      – between A and B 介于 A 和 B 之间,并且 >=a <=b .前面的数要小于后面的数,包含边界数的。
      select * from grade where coursegrade = 80
      select * from grade where coursegrade between 80 and 100
      条件关联
      – 条件1 and 条件2 :要同时满足条件1 和 条件2 是并且的关系。
      – 条件1 or 条件2 :只要满足一个条件就可以 是或的 关系。
      – () :先去执行括号中得条件
      – 字段 in (值1,值2,值3) :字段的内容要满足 值1 或 值2 或 值3
      select * from student where family = ‘北京’ and stname like ‘张%’
      select * from student where family in (‘上海’,‘天津’)
      排序
      –对查询到的结果按照升序或者降序的方式进行排列。
      –语法 order by 字段 排序方式 (desc,asc) asc 可以省略
      –注意:order by 一定要写在所有查询sql 的最后面
      select * from grade order by coursegrade
      聚合函数
      对查询的结果中某个字段进行函数运算
      sum(字段) :求和函数 字段需为数字类型
      avg(字段) :求平均值 字段需为数字类型.不会计算为空的条目。
      min(字段) :最小的值
      max(字段) :最大的值
      count(字段) :统计字段的个数,这里可以使用 * ,统计所有结果的行数。
      distinct(字段) :去重复
      注:聚合函数,不能放在WHERE中,需要放在HAVING里
      – 显示学生的总成绩
      select sum(coursegrade) from grade
      – 显示最高的成绩是多少
      select min(coursegrade) from grade
      select avg(coursegrade) from grade
      select * from grade
      select count(*) from student
      DISTINCT 去除重复行
      SELECT DISTINCT province FROM student;

    别名AS (Alias)
    as 临时别名
    列别名在WHERE中不能使用 GROUP BY 中不能使用 ORDER BY 中可以
    表别名可以在任何地方使用
    select class, sex, count(sex) as数量 FROM student GROUP BY class,sex ORDER BY class;
    select count(*) as 学生个数 from student
    select stname as 姓名,stuid as 学号from student
    SELECT和ORDER BY 不影响表中数据 所以可以使用别名
    分组里加WHERE和ORDER BY

    对真空的处理
    查询为空的字段 : 字段名 is null
    查询 不为空的字段 : 字段名 is not null
    select * from grade
    select * from grade where coursegrade is not null

    –统计每个课程的选修人数大于2个人的信息
    对课程分组,显示课程和对应的人数
    select couid,count(stuid) from grade group by couid having count(stuid) >2
    –统计<学生选修信息表>,统计每个同学的总成绩
    统计记录条数 count?
    查多少名学生
    SELECT count(*) FROM student
    查有多少种班级
    SELECT count (DISTINCT class) FROM student;
    查入学时间在1990-1-1到2000-12-31的学生个数
    SELECT count(enrollment) FROM student WHERE enrollment BETWEEN ‘1990-1-1’ AND ‘2000-12-31’ ;
    分组查询 GROUP BY
    先对查询内容进行分组、分类,然后在对一个组中的数据进行查询或者运算
    select 查询的内容 from 表名 group by 字段名
    select stuid,sum(coursegrade) from grade group by stuid
    select * from grade
    select stuid,sum(coursegrade) from grade where coursegrade is not null group by stuid
    分组前条件
    在group by 之前使用where 条件去选取数据
    分组后条件
    在 group by 分组后 + having 条件
    select stuid,sum(coursegrade) from grade where coursegrade is not null
    group by stuid having sum(coursegrade) >=200
    –统计<学生信息表>,统计每个班级中每种性别的学生人数
    select * from student
    –统计按照性别分得学生人数
    select sex,count(stuid) from student group by sex
    –统计<学生信息表>,统计每个班级中每种性别的学生人数,两个分组条件
    –统计<grade表>,统计每门课程的平均成绩,并按照成绩降序排序
    select couid,avg(coursegrade) from grade group by couid order by avg(coursegrade) desc

    RIGHT函数
    取身份证最后5位数
    SELECT right(card_id, 5) FROM student;
    将身份证补齐20位,前面用0补
    SELECT right(‘00’+card_id, 20) FROM student;
    SELECT ‘00’+card_id FROM student;
    LEFT函数
    取身份证的前10位
    SELECT left(card_id, 10) FROM student;
    将学号小于50的电话设置成NULL
    UPDATE student SET tel = NULL WHERE sid< 50;
    将电话为NULL的学生信息查询出来
    SELECT * FROM student WHERE tel IS NULL;
    判断条件是NULL的时候不能用= 要用IS IS NOT
    HAVING
    分组查询中,如果条件中有聚合函数,不能放在WHERE中,需要放在HAVING里
    查询平均出生年>1991的班级信息
    SELECT class,avg(borth_year) FROM student GROUP BY class HAVING avg(borth_year) > 1991 ;
    统计哪些班级男生平均年龄大于20
    SELECT calss FROM student WHERE sex=’男’ GROUP BY class HAVING avg(year(getdate())-borth_year)>20;
    优先级WHERE——GROUP BY——HAVING——PRDER BY
    查询学生的姓名和出生年,年龄最大的放前面
    SELECT sname, borth_year FROM student ORDER BY borth_year ;
    TOP 查询结果去前几个结果
    查询年龄最大的学生信息 年龄最大的前10名
    SELECT TOP 1 sname, borth_year FROM student ORDER BY borth_year ;
    SELECT TOP 10 sname, borth_year FROM student ORDER BY borth_year ;
    查询年龄最小的学生信息 年龄最大的前10名
    SELECT TOP 1 sname, borth_year FROM student ORDER BY borth_year DESC;
    SELECT TOP 10 sname, borth_year FROM student ORDER BY borth_year DESC;
    查询按照学号排序,前50个学生的信息
    SELECT TOP 50 * FROM student ORDER BY sid;
    查询最近入学的10名学生信息
    SELECT TOP 10 * FROM student ORDER BY enrollment DESC;
    关联查询(等值联接)
    内连接 等值连接
    外连接 1、左连 2、右连
    多表关联
    1内连接 等值连接 inner join
    语法 select 内容 from 表1 inner join 表2 on 等值条件
    select * from student inner join grade on grade.stuid = student.stuid
    select grade.couid,couname,coursegrade from grade inner join course on grade.couid = course.couid
    2外连接 outer join
    1)左连 select 内容 from 表1 left outer join 表2 on 条件
    2)右连 select 内容 from 表1 right outer join 表2 on 条件
    select * from grade outer join student on student.stuid = grade.stuid
    注:左连接 以 outer join 左边的表为准,要显示全部的左边表的数据,如果右边表没有关联的数据显示Null 与右边表比较 当右边表没有值时 只取左边表的记录
    select * from student right outer join grade on student.stuid = grade.stuid
    注:右连接 以 outer join 右边的表为准,要显示全部的右边表的数据,如果左边没有关联数据,显示Null
    多表关联
    语法:select 内容 from 表1,表2,表3 where 条件
    select stname,couname,coursegrade from student,course,grade
    where student.stuid = grade.stuid and course.couid= grade.couid and coursegrade >80
    select * from grade
    –所有男同学的平均成绩
    select avg(coursegrade) from student,grade where student.stuid = grade.stuid and sex = ‘男’ and coursegrade is not null
    select * from student
    3子查询,嵌套查询
    –当某一个查询的条件,是在另一个查询的结果集里面时,使用嵌套查询
    –查询李四的成绩。
    select * from grade where stuid = (select stuid from student where stname =‘李四’)
    –查询 张三和李四的成绩
    select * from grade where stuid in
    (select stuid from student where stname =‘张三’ or stname = ‘李四’)
    select * from grade where stuid = (select stuid from student where stname =‘张三’)
    or stuid = (select stuid from student where stname =‘李四’)
    –有考试成绩在 70~90的学生的姓名
    select stname from student where stuid in
    (select stuid from grade where coursegrade between 70 and 90)
    批量处理?
    DECLARE @sid INT 申请一个变量
    DECLARE @card_id CHAR(18)
    SET @sid=1 赋初值
    SET @card_id = ‘000000000000’+right(‘00000’+convert(VARCHAR(6),@sid),6) convert 强制转换数据类型
    PRINT @sid
    PRINT @card_id
    INSERT INTO student2 VALUES (@sid,‘蒋艳梅’,‘女’,‘49期’,‘计算机’,@card_id,1999,‘1999-9-9’,‘1111111’,‘1111’)

    CREATE PROCEDURE btInsert AS 或者 CREATE PROC btInsert AS --(保存一个过程)
    DECLARE @sid INT
    DECLARE @card_id CHAR(18)
    SET @sid = 1
    WHILE (@sid <= 100000)
    BEGIN
    SET @card_id=’1111111111111’+right(‘00000’+convert(VARCHAR(6),@sid),6)
    INSERT INTO student2 VALUES (@sid,‘蒋艳梅’,‘女’,‘49期’,‘计算机’,@card_id,1999,‘1999-9-9’,‘1111111’,‘1111’)
    SET @sid = @sid +1
    END
    EXECUTE btInsert 或者 EXEC btInsert (执行已经保存的过程)
    视图/虚表 view
    创建视图 create view 视图名 as (SQL)
    create view tiger as (
    select stname,couname,coursegrade from student,grade,course
    where student.stuid = grade.stuid and grade.couid = course.couid)
    –注意1.视图中不保存数据,不保存表结构,不保存表,没有约束。只保存sql 语句。
    –注意2.视图可以当表来使用,可以用视图去建视图。但是,不建议做 增 删 改的操作。
    –注意3.删除视图时,不会影响到原来的基础表。可以直接在视图中使用SELECT
    create view big_tiger as (select stname,coursegrade from tiger )
    delete from big_tiger where coursegrade is null
    删视图
    –语法:drop view 视图名
    drop view big_tiger
    drop view tiger

    视图的优点
    –1.对于使用sql 不熟练来说。视图是个很简单的查询途径
    –2.对于经常重复的复杂sql,使用视图可以提高查询效率
    –3.可以辅助用于权限管理,提高数据的安全性
    –4.帮助保护表和数据的完整一致性

    视图的缺点
    –1.降低了查询效率
    –2.增加保存空间
    –3.无法对数据和表进行操作。使得操作测试不方便进行。
    create view cll as (
    select class,count(stuid) as 人数 from student group by class
    )
    select * from cll where 人数 = 2

    –视图在企业中如何应用
    1.对于测试而言,降低测试的复杂度。例如:复杂 sql 写一次保存视图,以后都直接调用视图。
    2.对于测试而言,降低工作难度。例如:新来的新人可能对表结构不熟悉,对sql 也不熟悉,使用视图可以尽快上手开始工作。
    3.对于开发而言,降低服务器的工作。提高服务器运行效率。例如:如果不用视图,查询语句要在服务器端生成传给数据库。如果使用视图,服务器直接查询视图就可以满足查询功能。
    4.对产品设计和业务人员而言,降低了工作难度,不用学习和使用复杂sql 语言。例如:如果产品或者业务人员想去查询某些指定的数据时,只要让开发或测试人员建一个视图。直接去查询视图就可以了。
    索引
    –索引就像目录,表中对于一个字段的目录结构。如果想操作表中的数据,先在索引字段中找到那一行,然后再去操作那一条数据。
    –注意1:建议索引设置在主键、外键、唯一约束,效率高
    –注意2:索引是应用于查询条件的。经常使用的查询条件字段,应该被设置为索引字段。
    –注意3:一个表中可以有多个索引,索引之间可以交叉字段,一个索引可以有多个字段。

    索引的优点:
    –1.提高查询效率
    –2.提高了排序效率

    索引的缺点:
    –1.占用数据库资源
    –2.降低了对数据库增、删、改的效率

    索引在企业中如何应用:
    –1.页面上如果有很多的查询条件并且需要排序时,建议一一创建索引。
    –2.检查是否主键和外键、唯一约束建立了索引关系。
    –3.在测试过程中应该检查,运行查询条件时是否使用了索引。
    –4.测试中检查,平凡被更新的表,不要设置太多的索引。

    –建索引
    –语法:create index 索引名 on 表(字段,字段…);
    create index suoyin1 on student(stname,family);

    –删索引
    –语法:drop index 表名.索引名
    drop index student.suoyin
    存储过程 procedure
    –把一些sql 放在一起执行,并且有逻辑的执行。存储过程。
    –语法1:简单的无参数存储过程。
    – create procedure 存储过程名 as sql语句
    create procedure aa as
    update grade set couid = 6 where couid =1;
    update course set couid = 6 ,couname = ‘生物’ where couid =1;

    执行存储过程
    –语法 exec 存储过程名
    exec aa;

    select * from grade;
    select * from course;

    语法2:有参数的存储过程
    – create procedure 存储过程名 (@参数1 参数类型,@参数2 参数类型…) as sql语句
    create procedure BB (@id int,@NewId int,@name char(10))
    as
    update grade set couid = @NewId where couid = @id;
    update course set couid = @NewId ,couname = @name where couid =@id;

    执行存储过程
    –语法 exec 存储过程名(参数1,参数2,参数3)  
    SQL SERVER 参数不加括号
    –注意1.参数的个数和 数据类型必须与定义相一致
    –注意2.存储过程中的sql 是按照顺序执行的
    –注意3.当存储过程中一个sql失败时。不会影响其他sql的执行。
    exec BB 3,13,‘SQL SERVER’
    alter table grade add constraint CH check (couid < 20)
    update grade set couid = 21 where couid = 13;
    exec BB 13,21,‘ORACLE’
    create database bank;
    use bank;
    create table bj_bank
    (
    name char(10),
    money int check(money>=0)
    constraint pk_nanme_bj primary key (name)
    事务 transaction
    把所作的操作放在一组,如果有一个失败就全失败,都成功时候才成功
    事务是存储过程的一个部分,存储过程的一个写法
    – begin transacion 事务名
    – commit transacion 事务名
    – rollback transacion 事务名
    create procedure zz1
    (
    @a_name char(10),
    @b_name char(10),
    @a_money int,
    @b_money int,
    @c_name char(10)
    )
    as
    begin transaction c1 --开始事务c1
    if (select money from bj_bank where name = @a_name) > 0 --判断a账户
    begin
    update bj_bank --从a中减去
    set money=money-@a_money
    where name=@a_name;
    update zs_bank --向c中添加
    set money=money+@a_money
    where name=@c_name;
    commit transaction c1
    end
    else
    rollback transaction c1 --如果判断失败回滚所有操作

    begin transaction c2 --开始事务c2
    if (select money from bj_bank where name = @b_name) > 0 --判断b账户
    begin
    update bj_bank --从b中减去
    set money=money-@b_money
    where name=@b_name
    update zs_bank --向c中添加
    set money=money+@b_money
    where name=@c_name
    commit transaction c2
    end
    else
    rollback transaction c2 --如果判断失败回滚所有操作

    update bj_bank set money = 5000 where name = ‘a’;
    update bj_bank set money = 1000 where name = ‘b’;
    update zs_bank set money = 0 where name = ‘c’;

    exec zz1 a,b,250,600,c
    select * from bj_bank
    union
    select * from zs_bank
    exec zz2 a,b,500,700,c
    exec zz2 a,b,1000,500,c

    存储过程 zz2 开始
    create procedure zz2
    (
    @a_name char(10),
    @b_name char(10),
    @a_money int,
    @b_money int,
    @c_name char(10)
    )
    as
    begin transaction guopeng
    if (select money - @a_money from bj_bank where name = @a_name) >= 0
    begin
    update bj_bank set money = money - @a_money where name = @a_name;
    update zs_bank set money = money + @a_money where name =@c_name;
    commit transaction guopeng;
    end
    else
    begin
    rollback transaction guopeng;
    end
    begin transaction qiqian
    if (select money - @b_money from bj_bank where name = @b_name) < 0
    begin
    rollback transaction qiqian;
    end
    else
    begin
    update bj_bank set money = money - @b_money where name = @b_name;
    update zs_bank set money = money + @b_money where name =@c_name;
    commit transaction qiqian;
    end
    存储过程zz2 结束
    存储过程循环。大批量的制造测试数据。
    create procedure ww
    (
    @name char(10),
    @money int,
    @count int
    )
    as
    declare @num int;
    set @num = 0;
    while (@num < @count)
    begin
    insert into bj_bank values (@name,@money);
    set @num = @num +1;
    end;
    drop procedure ww
    alter table bj_bank drop constraint pk_nanme_bj
    exec ww ‘王沙’,900,1000000;
    select * from bj_bank where money = 30
    truncate table bj_bank

    –存储过程在企业中如何应用:
    –1.对于开发、对于测试而言,存储过程简化了工作难度。
    –2.对于开发、测试而言,存储过是可以实现逻辑的。
    –3.对于测试而言,帮助我们大批量的生成测试数据。
    –4.对于测试而言,帮助我们去检查对数据库的操作数据是否符合预期
    –5.对于开发而言,存储过程支持事务,可以做逻辑编程

    –删除存储过程
    –语法 drop procedure 存储过程名
    drop procedure ww
    触发器 trigger
    当满足触发条件时,执行后面的触发sql,支持事务
    语法: create trigger 触发器名 on 工作表表名 for 触发方式 as sql语句
    触发条件 delete,update 触发条件因数据库而异
    create trigger trigger_student_delete
    on student
    for delete
    as
    delete grade from grade,deleted where grade.stuid = deleted.stuid;
    select * from student
    select * from grade
    delete from student where stuid = 1
    alter table grade drop constraint FK__grade__stuid__7D78A4E7
    练习
    一、单表查询练习
    1、查询<学生信息表>,查询学生"张三"的全部基本信息
    Select * from student where stname=’张三’;
    2、查询<学生信息表>,查询学生"张三"和”李四”的基本信息
    select * from student where stname in (‘张三’,‘李四’)
    3、查询<学生信息表>,查询姓"张"学生的基本信息
    select * from student where stname like ‘张%’;
    4、查询<学生信息表>,查询姓名中含有"四"字的学生的基本信息
    select * from student where stname like ‘%四%’;
    5、查询<学生信息表>,查询姓名长度为三个字,姓“李”,且最后一个字是“强”的全部学生信息。
    select * from student where stname like ‘李_强’;
    6、查询<学生信息表>,查询姓"张"或者姓”李”的学生的基本信息。
    select * from student where stname like ‘张%’ or stname like ‘李%’;
    7、查询<学生信息表>,查询姓"张"并且"所属省份"是"北京"的学生信息
    select * from student where stname like ‘张%’ and family =‘北京’;
    8、查询<学生信息表>,查询"所属省份"是"北京"、”新疆”、”山东”或者"上海"的学生的信息
    select * from student where family in (‘北京’,‘新疆’,‘山东’,‘上海’);
    9、查询<学生信息表>,查询姓"张",但是"所属省份"不是"北京"的学生信息
    select * from student where family!=‘北京’ and stname like ‘张_’;
    10、查询<学生信息表>,查询全部学生信息,并按照“性别”排序,性别相同的情况下按照“所属省份”排序,所属省份相同的情况下再按照“班级”排序
    select * from student order by sex,family,class; (多个排序条件,用逗号以此分开,先排第一个、再排第二个。。。。)
    11、查询<学生信息表>,查询现有学生都来自于哪些不同的省份
    select distinct (family) from student;
    (注意distinct使用方法)
    12、查询<学生选修信息表>,查询没有填写成绩的学生的学号、课程号和成绩
    select couid,couid,coursegrade from grade where coursegrade is null;
    13、查询<学生选修信息表>,查询全部填写了成绩的学生的选修信息,并按照“成绩”从高到低进行排序
    select * from grade where coursegrade is not null order by coursegrade desc;
    二、聚合函数练习
    1、统计<学生信息表>,统计共有多少个学生
    select count(stname) from student;
    2、统计<学生信息表>,统计年龄大于20岁的学生有多少个
    select count(stname) from student where (year(getdate())-year(birthday))>20;
    3、统计<学生信息表>,统计入学时间在1998年至2000年的学生人数
    select count(stuid) from student where year(enrollment) between 1998 and 2000;
    4、统计<学生选修信息表>,统计学号为"S001"的学生的平均成绩
    select avg(coursegrade) from grade where stuid=‘1’;
    5、统计<学生选修信息表>,统计学号为"S001"的学生的总成绩
    select sum(coursegrade) from grade where stuid=‘1’;
    6、统计<学生选修信息表>,查询课程号为”C001”的课程的最高成绩
    select max(coursegrade) from grade where couid=‘1’;
    7、统计<学生信息表>,查询所有学生中的最大年龄是多少
    select max((year(getdate())-year(birthday))) from student;
    三、分组查询练习
    1、统计<学生选修信息表>,统计每个课程的选修人数
    select count(*) from grade group by couid;
    2、统计<学生选修信息表>,统计每个同学的总成绩
    select sum(coursegrade) from grade group by stuid;
    3、统计<学生信息表>,统计每个班级中每种性别的学生人数,并按照班级排序
    select class,sex,count(stuid) from student group by sex,class order by class;
    4、统计<学生选修信息表>,统计每门课程的平均成绩,并按照成绩降序排序
    select avg(coursegrade) from grade group by couid order by avg(coursegrade) desc;
    5、统计<学生选修信息表>,显示有两门以上课程不及格的学生的学号
    select stuid from grade where coursegrade<60 group by stuid having count(stuid)>2;
    6、统计<学生信息表>,统计每个班级中的最大年龄是多少
    select max(year(getdate())-year(birthday)) from student group by class ;
    四、嵌套查询练习
    1、用子查询实现,查询选修“高等数学”课的全部学生的总成绩
    select sum(coursegrade) from grade where couid=(select couid from course where couname=‘高等数学’);
    2、用子查询实现,统计<学生选修信息表>,显示学号为"S001"的学生在其各科成绩中,最高分成绩所对应的课程
    思考:如果该学号学生有两个课程分数都为最高的100分,查询会有什么结果(显示2个结果)
    select couname from course where couid=(select couid from grade where coursegrade in (select max(coursegrade) from grade where stuid=1)
    3、用子查询实现,查询2班选修"数据库技术"课的所有学生的成绩之和
    select sum(coursegrade) from grade where stuid in(select stuid from student where class=‘002’) and couid=(select couid from course where couname=‘数据库技术’);
    4、用子查询实现,查询3班"张三"同学的"测试管理"成绩
    select coursegrade from grade where stuid in (select stuid from student where class='003’and stname=‘张三’) and couid=(select couid from course where couname=‘测试管理’);
    五、联接查询练习
    1、查询"张三"的各科考试成绩,要求显示姓名、课程号和成绩
    select stname,couid,coursegrade from student inner join grade on student.stuid=grade.stuid and stname=‘张三’;

    select stname,couid,coursegrade from student,grade where student.stuid=grade.stuid and stname=‘张三’;
    2、查询"张三"的各科考试成绩中,哪科没有记录考试成绩,要求显示姓名、课程号和成绩
    select stname,couid,coursegrade from student inner join grade on student.stuid=grade.stuid and stname='张三’and coursegrade is null;

    select stname,couid,coursegrade from student,grade where student.stuid=grade.stuid and stname='张三’and coursegrade is null;
    3、查询"张三"的各门课程成绩,要求显示姓名、课程名称和成绩
    select stname,couname,coursegrade from student,course,grade where student.stuid=grade.stuid and course.couid=grade.couid and stname=‘张三’;
    4、查询3班"张三"的"测试管理"成绩,要求显示姓名、成绩
    select stname,coursegrade from student,course,grade where student.stuid=grade.stuid and course.couid=grade.couid and couname=‘测试管理’ and stname='张三’and class=003;
    5、查询所有2000年以前入学的,各班男生的各科考试平均成绩
    select class,avg(coursegrade) from grade,student where grade.stuid=student.stuid and sex='男’and year(enrollment)<2000 group by class ,couid;
    六、外联接查询
    查询”李坚强”所有课程的成绩,并显示学号、姓名、课程号和成绩,没有成绩记录的学号包括:(‘S009’,‘S010’,‘S011’)
    1、使用右联接
    select grade.stuid,stname,couid,coursegrade from grade right outer join student on student.stuid=grade.stuid and stname=‘李坚强’;
    2、使用左联接
    select grade.stuid,stname,couid,coursegrade from student left outer join grade on student.stuid=grade.stuid and stname=‘李坚强’;
    3、对比等值连接
    select grade.stuid,stname,couid,coursegrade from grade inner join student on student.stuid=grade.stuid and stname=‘李坚强’;
    七、补充提高
    1、查询“张三”比“王三”入学早几年
    select year(enrollment)-(select year(enrollment) from student where stname=‘王三’) from student where stname='张三;
    2、查询所在班级和该班内学生的年龄之和,其中每个人的年龄都大于20岁,每个班的年龄之和大于60岁
    select class,sum(year(getdate())-year(birthday)) from student group by class;

    第六章 C语言
    ATM机
    1.取钱 2.存钱 3.查询余额 4.转账

    作业1:存钱、查询、转账的流程图

    软件模块结构图
    需求:十进制一位数加法
    XX.C ——C语言的源文件
    编译 ——转换成二进制的机器语言
    XX.exe ——可执行文件

    不需要编译的脚本语言: PHP ASP JSP PYTHON VBS PEER TCL
    需要编译的高级语言: C VC C# Delphi Java .Net

    注释: // 和/* */

    作业3:绘制流程图中所有界面

    C语言 面向过程的语言 执行过程是自上向下 函数
    F10 单步执行 但不进入函数体
    F11 单步执行 进入函数体
    C语言中的存储
    数据类型
    基本数据类型
    1浮点型
    默认小数位是6位,若输出《6位,完成四舍五入
    若赋予的值小数位《5,后边会随机补数
    vc 对于定义的浮点型变量会默认为double类型,因此会产生警告。想没有警告,强制在赋值语句中的数据后加f. 比如,
    float x;
    x=0.33f;

    数组
    1)一维数据
    定义:数据类型 数组名[长度];
    初始化:1)先定义,后赋值;
    int a[3];
    a[0]=34;
    a[1]=45;
    a[2]=345;
    2) 边定义,边初始化
    float b[2]={4556.234,45.345};
    3) 只给第0个初始化,后边补0
    double c[3]={345.345345};
    访问:下标从0开始遍历数组

    2)二维数组
    定义:数据类型 数组组[行的长度][列的长度];
    初始化: 1)先定义,后赋值

         int a[2][4];
           a[0][0]=3;
           a[0][1]=4;
           a[0][2]=5;
           a[0][3]=6;
           a[1][0]=7;
           a[1][1]=8;
           a[1][2]=9;
           a[1][3]=10;
           2)边定义,变赋值;
             int b[2][4]={{3,4,5,6},{7,8,9,10}};
    

    访问: 行和列的下标从0开始遍历数组

    2字符串
    定义:char 字符串名[长度];
    初始化:先定义,后赋值
    边定义,边赋值
    1)
    char p1[5]=“abcd”; 对 (因为自动补\0占一位)
    char p2[5]=“abcde”; 错
    2)
    char p3[5]={‘a’,‘b’,‘c’,‘d’,’\0’}; 对
    char p4[5]={‘a’,‘b’,‘c’,‘d’,‘0’}; 错
    输出:prinf("%s",p1);
    printf("%c",p1[0]);
    输入:scanf("%s",&p1); /读整个字符串/
    scanf("%c",&p1[2]); /读单个字符/
    注意:赋值的长度小于定义长度时,会逐个补空

    结构体
    边定义,边初始化: struct 结构体名 {
    数据类型 数组名[长度];
    数据类型 变量名;
    …} 结构体变量名={初始化对应值}
    struct student {
    char name[10];
    int age;
    } s1={“zhangsan”,18};
    访问:结构体变量名.结构体内部定义的变量名。
    printf("%s",s1.name);
    printf("%d",s1.age);

    结构体数组
    边定义,变初始化:
    struct 结构体名 {
    数据类型 数组名[长度]; /表示长度/
    数据类型 变量名;
    …} 结构体数组变量名[长度]={{初始化对应值1},{初始化对应值2},} /这里的长度表示多少个,相当于表中的记录数/

      struct student {
                  char name[10]; /*name这个字符串能有效存储字符数9个,还有1位补空*/
              int age;
            } s[2]={{"zhangsan",18},{"lisi",20}};   /*这里的2表示有2条记录*/
    

    3运算符
    算数运算符: + - * /(求商) %(取余)
    关系运算符: > < >= <= ==(等于) != (不等于)
    真(非0) 假0
    逻辑运算符
    && (与) || (或) ! (非)

    常量
    const 数据类型 常量名=值;
    例如,圆周率 const int pi=3.14;

    变量

    1. 变量是什么?

    2. 先定义,后使用
      标准c
      int x;
      x=3;
      int j;
      j=89;

      vc
      int x;
      int j;
      x=3;
      j=89;

    3. 使用赋值语句修改变量值

    4. 变量赋予的值由数据类型决定

    5. 变量包含名字、值和地址

    6. 不同的数据类型在计算机中存储的字节大小不一样
      16位 32位
      int 2个字节 4
      long 4 4
      float 4 4
      double 8 8
      char 1 1

    7. 变量的名字
      首字母:字母,下划线
      包含:字母,下划线,%, 数字
      不能使用c语言的保留字

    8. 全局变量和局部变量
      1)局部变量如何定义:在函数中定义的变量都成为局部变量。离开函数(调用完函数),该变量消失。
      2)全局变量如何定义:在main函数上边定义的变量成为全局变量。对所有的函数都生效。
      3)当局部变量和全局变量同名时,实际上修改的是局部变量。
      常量
      常量的定义

    define 常量名 常量值

    常量名要全用大写字母
    常量没有数据类型
    1.数组
    定义数组
    数据类型 数组名[N] ;
    N 代表数组的个数

    结构体
    定义
    Struct 结构体名
    {
    变量1
    变量2



    }
    例:
    Struct
    {
    Char name[8];
    Char sex[2];
    Int age;
    Int grade;
    } Stu_49[2]={{“张三”,”男”,18,60},{“李四”,”女”,19,100}};
    结构体的显示
    Printf(“ %s”,Stu_49[0].name);
    条件/分支逻辑
    Switch
    switch (表达式)
    {
    case 值1: 处理语句1;
    break;
    case 值2:处理语句2;
    break;
    case 值3:处理语句3;
    break;
    。。。。

    }
    注意1:值的数据类型要和表达式返回值的类型要匹配;
    注意2:不要忘了使用break退出后续执行;
    注意3:表达式要使用括号括起来;
    

    条件 只能输入固定的值
    If
    分支结构
    条件
    情况1: 对应流程图,假分支直接指向后续语句,不含有处理语句
    if (条件)
    处理语句1;
    处理语句2;

    情况2: 对应流程图,处理语句1和处理语句对应真分支,处理3和4对应假分支
    if (条件)
    处理语句1;
    处理语句2;

    else
    处理语句3;
    处理语句4;

    情况3
    If (表达式1)
    if(表达式2)
    语句2;
    Else
    语句3;
    Else
    语句1;
    循环
    For
    循环结构
    定义循环控制变量
    for (控制变量赋初始值;控制变量终止条件;累加(减)器)
    {
    循环体语句;
    }

    while (条件)
    {

    循环体语句;
    }

    do
    {
    循环体语句;
    } while (条件)

    for(表达式1;满足循环条件;表达式2)
    {
    循环体;
    }
    while
    while (表达式)
    {
    循环体
    }
    do…while
    do
    {
    循环体
    }
    While(表达式)
    申请卡号时,多申请一位 并用\0座位数据的结束
    Do while 在条件不满足时,会执行一次do中的循环体
    有参数有返回值的函数
    错误跟踪 写日志
    函数
    函数:

    1. 输出函数
      1.1 printf(控制格式,变量);
      控制格式 %d - 整形
      %c - 字符型
      %ld -长整形
      %f -浮点型 (想控制小数位 %.3f)
      %lf --双精度 (想控制小数位 %.5lf)
      1.2 printf(“你想输出的内容”);
    2. 输入函数
      scanf(“控制格式”,&变量1,&变量2,…);
      %c
      %d
      %ld
      %f
      %lf
      int x,y;
      x=234;
      y=234;
      scanf("%d%d", &x,&y);
    3. 存在的必要性
      1) 职能单一
      2)定位问题
      3)便于使用
      4) 减少代码冗余
    4. 项目中只能有一个main函数。否则移除。
    5. 函数使用
      先定义(在main函数外定义)-》调用-》申明(在main函数外边申明)
      注意1:定义的函数名、调用的函数名、申明的函数名必须同名
      5.无返回且无形参的函数
      定义:
      void 函数名()
      {
      语句1;
      语句2;
      。。。。。
      }
      调用:
      函数名();
      申明:
      void 函数名();
    6. 无返回有形参的函数
      定义:
      void 函数名(形参列表)
      {
      语句1;
      语句2;
      。。。。。
      }
      在这里,形参列表实际上就是变量定义列表,只是因为放在函数定义中就叫叫它形参。
      比如:int x,int y,int z,char m
      调用:
      函数名(实参列表);
      在这里,实参列表的个数和形参列表个数一致;类型一致;实参列表可以相同数据类型的值,也可以是相同数据类型的变量
      申明:
      void 函数名(形参列表);
    7. 有返回有形参的函数
      定义
      数据类型 函数名(形参列表)
      {
      语句1;
      语句2;
      。。。。。
      return 值
      }
      在这里,return的值必须和函数名之前的数据类型匹配。
      调用:
      定义一个和函数返回数据类型相同的变量
      变量=函数名(实参列表);
      申明
      数据类型 函数名(形参列表);
    8. 函数名在一个程序中不能同名
      程序结构
      #include 语句
      #include <stdio.h>
      函数申明
      main函数
      自己定义的函数
      其他
    9. 字节
    10. ascii
      char m;
      m=“k”;
      /* 输出k字符的ascii码*/
      printf(“m=%d\n”,m);
    11. 转义
      \ - 转义符
      \n -控制换行
      \t -控制tab键的距离
      \0 -空
    12. 注释
      单行
      跨行 /* 语句 */
      Void 函数名()
      Void 函数名(参数1,参数2…)
      Int 函数名()
      Int 函数名(参数1,参数2…)
      注:1调用函数时 传入的参数类型 要和函数参数的类型一致,返回值也是一样
      2对有参数没有返回值的函数测试
      3对没有参数 没有返回值的函数测试
      4 测试main()函数 复制了一个定义为test_main()

    补充知识:
    1个字节=8位 编码是ASCII码 又叫ANSI(美国标准码)
    用两个字节表示中文 编码是GB2312
    当16位全部占用时 编码是GB18030
    后来同意标准 全部用16位表示 编码用UTF-8 只翻译16位中的低8位
    注: C语言中main()函数默认是返回int 类型的值

    第七章 Windows环境搭建
    一、名词注解与定义:
    环境:分为{1、生产环境;2、测试环境;3、开发环境。
    生产环境(也称为:上线环境)是运维人员的工作环境,有时候测试人员也会参与运维的部署工作)。
    测试环境:测试人员为了测试软件在生产环境中的功能和性能的表现,而尽量可能的模拟了一个生产环境来进行软件测试。
    开发环境:就是开发人员编写软件代码的工作环境。
    一个软件要从开发环境——>测试环境——>生产环境这个环境流程。
    问:为什么不在开发环境中进行软件测试,要测试环境中进行?
    答:因为开发环境它具有可变性,其影响测试的准确性,所以为了保证数据独立性和其测试的准确性,软件测试最好在测试环境中进行。
    测试环境-分为:1、硬件环境;2、OS(操作系统)环境;3、软件环境。
    其硬件环境和OS(操作系统)环境都是要根据被测软件的需求来搭建;软件环境包括:被测试软件和一些用户常用软件,主要测试被测软件和常用软件之间的兼容性、冲突。
    搭建:先要根据需求架设硬件环境,再根据需求架设OS系统环境,要保证架设的OS系统是无毒的,最后架设软件环境,卸载无用的软件,确认软件的证件号来保证一些不必要的错误和冲突。
    为什么要保证架设的OS操作系统环境是无毒的?因为病毒可能产生一些和被测软件无关的BUG。解决方法:可安装杀毒软件,重装系统来防止和保证。
    被测软件-分为:1、单机版;2、C/S(client/server);3、B/S(browser/server)。三种运行模式。
    C/S(client/server):是分为客户端、服务端和数据库端(如:PPS、QQ需要用户先安装客户端)。其架设的软件会用客户端来分担一部分工作;
    优点:运行速度快、部分数据存放在本地;
    缺点:兼容性差,要根据不同的系统来开发不同的系统版本,成本高和测试成本高。
    B/S(browser/server):是可以用IE浏览器直接访问和运行的一种模式,不用预先安装客户端(如:网页游戏、网上订票系统等)。
    优点:兼容性好,数据保密性好;
    缺点:运行速度较慢。
    软件要根据不同的运行环境、性能的要求来选择使用C/S架设,还是用B/S架设。
    扩展内容:
    app:应用软件,是安装在OS(操作系统)上面的。
    光的三原色:红。绿。蓝。
    服务器(软件):
    1、应用服务器:IIS,Weblogic,JBoss;
    2、Web服务器软件:Apache,Nginx,LightHttpd;
    3、数据库服务器:SQL Server,MySQL(Strucrure Query Language),Oracle;
    4、邮件服务器:QMail,Exchange,Lotus;
    5、FTP服务器

    C/S
    海达票务系统
    1.硬件环境和操作系统(略)
    2.安装SqlServer,安装客户端和服务器端软件
    3.测试数据准备,环境初始化
    DROP DATABASE hdpw1;
    CREATE DATABASE hdpw1;
    RESTORE DATABASE hdpw1 FROM DISK=‘D:\training\doc\win\student\海达票务\hdpw1’;
    4.参数配置
    4.1 Client端配置,和Server端的链接
    4.2 服务器端配置,和数据库的链接
    4.3 数据库端配置(略)
    5.启动整个海达票务系统
    系统更新时,需要更新C和S端,而B/S架构只用更新S端
    B/S
    安装虚拟机:
    1.打开VMWare
    2.新建一个虚拟机
    3.更改光驱加载的ISO
    4.开启电源
    5.将鼠标焦点放到GuestOS里(如何到HostOS?热键)
    6.格式化磁盘
    7.后续安装的步骤
    8.配置网络
    9.安装VMWareTools

    配置共享文件夹:

    进销存系统
    1.安装IIS (通过本机IP访问和netstat查看进程是否成功启动)

    2.把jxc文件夹拷贝到guest
    3.在IIS里新建一个虚拟目录->指向jxc文件夹

    4.设置jxc文件夹的权限

    5.启动相关扩展

    6.启用虚拟目录的父路径

    7.设置默认首页

    OA系统
    1.安装JDK

    2.将Jdk的bin目录追加到环境变量的Path变量里(如:C:\jdk6\bin)
    注:放在path里可以在cmd中输入命令运行,系统将自动定位jdk所在目录

    3.新建一个系统变量 JAVA_HOME,值为jdk的安装目录(如:C:\jdk6)

    4.新建一个系统变量 CLASSPATH,(如:.;C:\jdk6\lib\tools.jar;C:\jdk6\lib\dt.jar) . 指当前目录 注:CLASSPATH (java的类库)

    注: . 表示当前目录 允许jdk查找当前目录
    5.安装Tomcat,解压后运行startup.bat
    通过127.0.0.1:8080 访问,可以看到Tomcat的欢迎页面
    也可以修改conf/server.xml文件,更改Tomcat启动的端口号

    注:在conf下server.xml中查找8080(默认端口)修改端口号 更改配置后要重启服务器
    用startup.bat启动tomcat
    6.安装MySql

    7.部署OA的程序,将程序拷贝到tomcat的webapps目录

    1)先到Mysql里把测试数据导入
    登录mysql mysql -uroot -p
    进入mysql控制命令行,显示如下
    mysql>

    从硬盘上导入一个sql文件

    mysql> source c:~~~~~\oa\setup\redmoon.sql (本质是执行Sql文件里的每一个SQL语句)
    2)修改和数据库的连接、日志和缓存参数
    oa/WEB-INF/proxool.xml 改里面的数据库密码即可

    oa/WEB-INF/log4j.properties 改日志的路径,并创建相应的路径

    oa/WEB-INF/classes/cache.ccf 改缓存的路径

    8.重新启动Tomcat
    访问 127.0.0.1/oa
    admin
    111111
    注:参考 windows环境搭建
    补充知识:
    查看服务:

    MySql相关命令
    show databases 查看有哪些库
    show tables 查看库有哪些表

    desc 表名 查看表结构

    浏览器引擎: 1.渲染页面 2.执行客户端脚本JavaScript 3.执行自由控件
    w3school.com.cn学习前端语言的网站

    Trident IE
    Gecko FireFox
    Presto Opera
    Webkit Safari

    Cookies 保存用户名 密码 根据浏览器、域名保存 一个浏览器、域名一个Cookies 唯一标识一个客户
    Session 和cookies类似 是服务器端保存的
    Session 和cookies是一一对应的
    TCP/IP分四层 OSI七层

    应用层传输协议:http pop3 smtp
    传输层协议:tcp udp
    网络层协议:ip arp
    物理层协议:mac pppo
    三次握手
    目的:建立连接
    1.给服务器发送一个包[SYN] 处于send状态
    2.服务器回复一个包[SYN,ACK] 处于receive状态
    3.给服务器返回一个包[ACK] 两边同步待命 准备发送

    注:参考 网络模型和协议.vsd
    Session/Cookie:Session是存在服务器的,关闭浏览器则删除Cookie存在客户端,可以设置生命周期

    JAVA环境搭建
    1.安装jdk程序
    2.配置环境变量
    1)增加JAVA_HOME,值为jdk的根目录
    2)修改Path,在后面追加 ;%JAVA_HOME%/bin
    3)增加CLASS_PATH,值为 .;%JAVA_HOME%/lib/tools.jar
    3.运行cmd,检查java版本
    java -version
    4.运行一个java程序
    1)编辑文件 HelloWorld.java,内容为
    public class HelloWorld {
    public static void main(String[] args) {
    System.out.println(“Hello, world!”);
    }
    }
    2) 编译java文件,生成字节码文件
    javac HelloWorld.java
    3) 运行字节码文件
    java HelloWorld

    安装Tomcat

    1. 解压至某目录
    2. 运行 bin/startup.bat
    3. 使用浏览器访问
      http://127.0.0.1:8080

    安装MySQL

    1. 运行MySQL安装程序,next
    2. MySQL的配置
      1. 编码
      2. 密码
      3. 服务
    3. mysql -uroot -p

    部署oa

    1. 将程序文件拷贝到tomcat的webapps目录
    2. 初始化测试数据,登录到mysql控制台,运行source命令
      mysql> source c:…\redmoonoa.sql
    3. 程序配置
      1. oa程序和数据库的连接配置
        WEB-INF\proxool.xml
        修改用户名和密码
      2. oa的日志配置文件
        log4j.properties
        log4j.appender.R.File=C:/apache-tomcat-7.0.6/webapps/oa/log/oa.log
      3. oa的缓存配置文件
        WEB-INF\classes\cache.ccf
        jcs.auxiliary.DC.attributes.DiskPath=C:/apache-tomcat-7.0.6/webapps/oa/CacheTemp
    4. 重启一下tomcat
    5. 访问
      http://127.0.0.1:8080/oa
      admin
      11111

    第八章 需求管理
    1.什么是需求
    明确要什么做什么
    2. 需求工程在做什么
    ㈠需求开发:需求获取 需求分析 需求格式化 需求验证
    ㈡需求管理:需求分配 需求评审 需求基线 需求变更 需求跟踪
    3. ★需求变更
    a)为什么要变更:外因:市场,客户。内因:技术不足 缺陷 人员资源
    b)变更影响了什么:SRS HLD LLD SP UI ZI CODE
    c)怎么做变更的控制(需求变更控制目标):控制项目成本,控制项目风险
    d)需求变更的越早,影响范围越小,变更越晚,影响范围越大
        原则 方法
    变更控制的目标 降低变更引起的成本 防止随意的变更 通过评审和会议让用户或者企业负责人在变更上签字来确认变更
    尽量早的发生变更 多设计一些产品原形,由用户确认,
    尽量控制变更影响的范围 尽量不变更,如果变更尽量发生在后续版本
    尽量减少变更所引起的反工 当变更的需求稳定后在介入开发和测试
    降低变更引起的风险 高内聚,低耦合 代码内部干的是一件事,函数与函数之间关联尽量小,尽量使变更只影响到局部,而不影响到整个系统
    4.★需求的跟踪
    a)目的(为什么要需求变更跟踪)将和SRS有关的文档统一管理和关联起来,从而可以从任何一点找到其他文档的相关内容
    b)★★★★★输入、输出(RTM)
    ①开发的需求跟踪:SRS HLD LLD
    ②系统的需求跟踪:SRS ST计划 ST方案 ST用例
    ③集成的需求跟踪:HLD IT计划 ITf方案 IT用例
    ④单元的需求跟踪:LLD UT计划 UT方案 UT用例
    输出(RTM)Requirement Tvace Matrix需求跟踪矩阵
    c)每个阶段,跟踪的内容和变更的跟踪.
    SRS编号 SRS名称 系统测试项ID ST描述 ST子项ID ST子项描述 系统测试用例ID 系统测试用例描述
    HLD编号 HLD名称 集成测试项ID IT描述 IT子项ID IT子项描述 集成测试用例ID 集成测试用例描述
    LLD编号 LLD名称 单元测试项ID UT描述 UT子项ID UT子项描述 单元测试用例代码声明 单元测试用例代码描述
    需求跟踪矩阵的作用:
    开发RTM: 保证所有的需求都被设计实现了
    测试RTM: 保证所有的需求都被测试了
    保证可以通过需求,确定需求变更影响的范围,找到所有的成果物(HLD、LLD、系统测试计划…)
    需求的特点:
    只关心想要什么,不关心怎么去做
    需求工程

    不同阶段的需求变更的影响范围      
           
    需求阶段需求变更影响      
    需求规格说明书 系统测试计划    
    开发RTM 系统测试RTM    
           
    概要设计需求变更影响      
    需求规格说明书 概要设计    
    系统测试计划 系统测试方案 系统测试用例  
    集成测试计划      
    开发RTM 系统测试RTM 集成测试RTM  
           
    详细设计需求变更影响      
    需求规格说明书 概要设计 详细设计  
    系统测试计划 系统测试方案 系统测试用例  
    集成测试计划 集成测试方案 集成测试用例  
    单元测试计划      
    开发RTM 系统测试RTM 集成测试RTM 单元测试RTM
           
    编码以及后期测试阶段需求变更      
    需求规格说明书 概要设计 详细设计 编码
    系统测试计划 系统测试方案 系统测试用例  
    集成测试计划 集成测试方案 集成测试用例  
    单元测试计划 单元测试方案 单元测试用例  
    开发RTM 系统测试RTM 集成测试RTM 单元测试RTM
    补充知识:
    1代码编写原则:
    1).高内聚,低耦合
    2).可续性高
    3).查阅代码编写规范
    2在公司中出现以下问题如何解决
    (1)业务背景不同,导致项目延期
    明确需求文档的格式和标准,尽可能细化需求文档
    (2)需求变化频繁
    建立变更控制
    (3)需求相关的代码,用例找不到,找不全
    建立雪球跟踪

    CR(Changes requirement)需求变更
    CCB(Changes control board)变更控制委员会
    CMO(Configuration management officer)配置管理员
    PM 项目经理
    SWE 软件开发工程师
    STE 软件测试工程师
    QA 质量保证人员
    CI 基线

    —基线变更流程
    1)项目成员提交CR
    2)CMO将CR状态标识为已提交,并将CR提交给CCB进行签发
    3)CCB召开会议对CR进行评估
    4)未通过CMO将CR状态标识为已拒绝并返回提交人
    5)通过,CMO将CR状态标识为已接受,将CR与要修改的配置项发给项目组成员并开放CI的配置库权限
    6)项目组成员执行更改并进行验证
    7)CCB召开会议对修改进行审核,如果通过将CR状态标识为已验证,发给CMO,否则返回修改人
    8)CMO检查验证CR记录,收回配置权限,将CR状态标识为已关闭,返回提交人
    变更控制流程图

    第九章 缺陷管理
    缺陷相关概念
    1什么是缺陷:被测得产品部符合需求和用户使用的实际结果,不符合法律法规
    软件:满足某个功能的逻辑体
    系统:硬件、支撑软件、人员、数据等,综合起来满足某个业务需求的集合体
    2什么可以被定义为缺陷:(缺陷的分类)
    ①缺陷(defect)产品设计与需求设计部符合
    ②错误(error)没有定义的或者未知的错误信息
    ③故障(fault)由于一些原因导致产品失效,重新启动调整后可以恢复用户使用
    ④失效(failure)由于一些原因产品失效,无法自行恢复
    3缺陷提出的目的和意义
    对开发:更好发现缺陷现象,重现和定位缺陷,查找原因,保证所有的缺陷都被修复
    对测试:记录和保证BUG完整一致,回归保证所有的 BUG都验证
    提出问题,把问题交给开发去改
    跟踪缺陷,看是否已经修改
    测试报告,统计数据
    缺陷管理相关概念
    1.BUG管理的目的:
    ①.保证每个缺陷都被修改
    ②.保证每个缺陷都被回归
    ③.缺陷的完整性和一致性
    ④.避免纠纷,降低沟通成本
    2缺陷管理的意义:
    ①提高工作效率(BUG分类,状态负责人)
    ②记录唯一的缺陷信息,保证BUG完整一致(通过设置权限实现)
    ③记录中间环节,是BUG可追溯
    ④统计为测试报告提供数据
    3.参与缺陷管理的角色:
    测试工程师:发现和回归BUG
    测试经理:判断BUG的有效性
    开发经理:分配BUG
    开发工程师:修改BUG
    评审:解决矛盾
    4.缺陷的分类(属性)
    ①按模块分类:例如:登录模块,查询模块
    ②按严重级别分类:blocker阻碍的(不修改该BUG之后的开发测试无法执行)
    Critical崩溃(系统部能用)
    major严重的(严重影响功能使用流程)
    anormal一般的(不会影响主要的功能流程)
    minor轻微的(不会2影响业务流程也不影响使用)
    trvival 界面的
    suggestion建议(可用性,易用性,侧重用户体验)
    ③按优先级别分类:P1----P5(同意 BUG可能会变)
    BUG管理基本流程:

    BUG管理基本流程及相关角色
    1缺陷管理常见流程
    1)BUG回归时没有修改好:测试工程师REOPEN——开发工程师
    2)测试经理认为BUG无效,原因:不是BGU,对需求的理解误差,描述不清楚。BUG不全,重复
    测试工程师NEW----测试经理CAN OPEN-----REJECTED-----测试工程师CLOSED
    3)开发工程师拒绝修改BUG,原因:修复提高项目风险,理解分歧,技术难度大,修复成本高,修改范围广,优先级低
    测试工程师NEW----测试经理OPEN-----开发经理ASSIGNED-----开发工程师CANFIX------开发经理
    4)开发经理拒绝修改或分配BUG,原因:开发与测试已经不同意,偶发,项目风险高,关系进度成败,技术难度大,无法实现,修改成本高,难度大,影响大,影响进度优先级别低
    测试工程师NEW----测试经理OPEN----开发经理ASSIGNED----评审委员会CAN LATER----Y(LATER)-----N开发经理
    5)一般BUG生命周期
    测试工程师NEW----测试经理OPEN—开发经理ASSIGNED----开发工程师fixed----测试工程师CLOSED
    2缺陷状态:
    New新BUG单 Open确认 Reject拒绝 Assigned已分配 Fixed已修复 Reopen回归时未修改正确重新开放 Closed关闭 Later稍后再改 Postpone延迟 Abandon放弃 duplicate重复 verify验证
    测试人员: 无 → New Fixed → Reopen Fixed → Close
    测试组长: New → Open New → Abandon
    开发经理: Open → Reject Open → Postpone Open → Assign
    开发人员: Assign → Fixed
    项目经理: Reject → Passed Reject → Faild Faild → Abandon
    BUG单
    1.BUG单写作准则(5C):
    correct(准确)每个组成部分的描述准确,不会引起误解
    clear(清晰)每个组成部分的描述清晰,易于理解
    concise(简洁)只包含必不可少的信息,不包括任何多余的内容
    complete(完整) 包含复现改缺陷的完整步骤和其他本质信息
    consistent(一致)按照一致的格式书写全部缺陷报告

    2.BUG单模板

    注意:
    1一定可以重现的BUG可以不写“重复几次操作,出现几次,我认为,标题里不能写步骤,不能用主观的话描述,我在 。。。。的,不确定语句:某些好像,禁止使用”之后”,然后之类的语句”之类的话
    2需求规格说明书以外的错误可以当建议报告,不当BUG报告,开发可以改,也可以不改
    3若是随机出现的BUG,要写出操作几次,出现几次
    4若被测软件是跨平台软件,要写上在其他平台下无误
    5禁止写冗余的操作的步骤。常识性的步骤不用写进缺陷操作步骤
    6写明环境数据,如何选择数据,数据如何被破坏
    7一定要交代清楚测试书记,明确处理对那些数据进行操作

    第十章 测试需求分析
    概念:
    1.什么是需求分析:明确做什么,明确测什么,怎么测
    2.需求分析的目的(针对测试而言):
    1)对需求进行细化和分解,从而找到所有的测试点
    2)使从测试覆盖所有的需求(方法:先覆盖业务流,然后模块,关联 非功能)
    3)更细致的需求分析有利于提高测试质量(非软件质量)
    3.测试需求分析的特征
    1)所有的需求项要通过需求分析被核实
    2)测试需求分析应明确指出满足需求的前置条件和不满足需求的前置条件
    3)测试需求分析不涉及具体的测试数据,测试数据是在测试用例中产生
    ★如何做测试需求分析
    1.明确系统框架,有多少个业务流程
    2.明确业务流中有多少个功能测试点,细化分解业务流:
    a)明确每个功能模块的输入、输出、逻辑
    b)满足功能需求的条件和不满足功能需求的条件
    3.明确每个功能的独立处理流程关系
    4.明确功能之间的处理、联系
    5.明确非功能需求和隐性需求 如:安全性、性能、界面美观、易用性等…
    6.系统运行环境包括代码 硬件、软件、外设、数据库、网络
    罗老师的答案
    1考虑非功能性需求
    2挖掘规范需求形成规范需求流程
    3仔细阅读需求规格说明书(找出问题所在)形成问题列表
    4明确该系统的子系统,模块,子模块,功能,子功能(可以借助用例图的方法)
    5明确功能,子功能的流程和逻辑(可以使用活动图,状态图或流程图)
    6挖掘隐性功能,形成隐性功能规范需求
    7找出模块与模块,功能与与功能之间的 关系,确定组合测试需求
    ★UML统一建模语言(Unified Modeling Language)
    1.用例图:被称为参与者的外部用户所能观察到的系统功能模型图
    关系:
    1.关联
    2.泛化 指向父用例 如:
    3.依赖
    a)扩展 指向被扩展者
    b)包含 大的指向小的
    2.活动图:描述了一组顺序的或并发的活动
    状态 活动 开始 结束
    状态转移 循环 集 判断
    泳道
    3包含3个因素:参与者(Actor执行者),系统(Use Case用例),关系(Association关联,Dependency依赖,Generalize继承)

    第十一章 配置管理
    1.什么是配置管理
    a)对所有配置项进行标识,解决了在不同时间周期内,这些文档贯穿整个项目的生命周期并且对配置项进行权限的控制
    b)配置管理的目的(配置管理在解决生命事情,为什么要进行配置管理):解决了保证了软件产品的完整性,一致性,共享性、权限,变更可控、可追溯性
    c)配置管理管理了什么(配置项都包括哪些):配置项 版本 状态
    配置项:项目过程中每个阶段文档产物(SRS,HLD,LLD)代码,开发工具,测试工具,环境(应用服务器,数据库服务器)第三方软件、用户手册,方案、用例等等,
    对象的版本:XX.YY.ZZ.PP
    XX 主版本号——内核程序,核心代号
    YY 子版本号——主要功能、添加功能
    ZZ 维护版本号——增加次要功能,功能改进
    PP 补丁号——SP
    对象的状态状态: 未检查 入基线 冲突 锁
    注:BUG单也算配置项,但是一般单独管理 常用管理工具:QC、Jira、Bugfree、Bugzilla
    2.配置管理流程

    角色:
    项目经理(Project Manger PM)配置管理员(Configuration Mange Officer CMO)开发经理(Development Manger)测试经理 开发工程师 测试工程师
    质量保证人员(Quality Assurance)变更控制委员会(Change Control Board CCB)
    3.SVN实战
    开发可以生成branch 测试经理可以合并branch入trunk 评审可以使trunk入基线并打tag
    启动数据库服务器subversion: 用start svnserve.exe –d –r

    为每个文档建立trunk 然后再为单独的文档建立branch 测试后合并入trunk生成新版本的该文档
    优点:节省空间 缺点:tag版本不配套

    为所有文档建立trunk 每次建立branch都包括所有文档
    优点:所有文档版本相同 缺点:浪费空间
    配置管理工具SVN操作过程手册
    一、如何创建“project”项目版本库
    第一步:在D盘根目录下创建文件夹:“SVNROOT”如图所示:

    第二步:在“SVNROOT”文件夹内创建两个文件夹分别为:“project”,“project1”两个项目。

    第三步:对“project”创建版本库;
    A.选中“project”文件夹,点击右键;

    B.点击“在此创建版本库”。

    C.版本库创建成功后,“project”文件内自动生产以下文件;

    D.打开“conf”文件夹

    备注:对上述三个文件进行解释
    “authz”文件设置“project”操作人员的权限“read、write”;
    “passwd”文件设置操作的用户名和密码;
    “svnserve.conf”文件是“系统配置文件”
    E.对上述3个文件分别进行修改;
    修改“svnserve.conf”

    未修改
    修改处:

    修改方法:将“#”及空格,去掉即可。

    修改后
    修改后对文件另存,存储过程中格式选择“UTF-无BOM”然后点击“保存”。

    点击“保存后”在原文件夹内生产如图文件,这个是ultra edit 的备份文件

    修改完成。
    

    修改“authz”文件

    未修改前
    添加内容:
    p1_group_a=p1_a1
    p1_group_d=p1_d1,p1_d2
    p1_group_t=p1_t1

    [/]
    *=r
    root=rw

    [project:/]
    @p1_group_a=rw
    @p1_group_d=rw
    @p1_group_t=rw

    修改后
    修改后对文件另存,存储过程中格式选择“UTF-无BOM”然后点击“保存”。
    点击“保存后”在原文件夹内生产如图文件

    修改完成。
    修改“passwd”文件

    未修改前
    添加内容为:
    p1_a1=p1_a1
    p1_d1=p1_d1
    p1_d2=p1_d2
    p1_t1=p1_t1

    修改后对文件另存,存储过程中格式选择“UTF-无BOM”然后点击“保存”。
    点击“保存后”在原文件夹内生产如图文件

    修改完成。
    二、如何查看创建的“project”项目版本库
    通过“版本库浏览器”进行查看
    任意空白处点击“鼠标右键”

    点击:“版本库浏览器”

    输入:“svn://localhost/project”

    备注:“localhost”本机的IP地址;
    Project为项目工程名;
    点击“确定”

    问题:为何会出现“无法连接主机“localhost”:由于目前机器积极拒绝,无法连接”
    分析:“Subversion”服务器端“svnserve”服务未启动;

    如何启动“svnserve”服务,操作步骤:
    A.桌面“开始”----“运行”----输入“cmd”;

    B.用“DOS”命令打开此服务
    首先找到该文件:cd c:\Program Files\subversion 进入该目录

    查看该目录里面有哪些文件,用到命令“dir”

    3启动该项服务
    输入:start svnserve.exe –d –r D:/SVNROOT
    备注:D:/SVNROOT为客户端路径。

    运行后弹出一个窗口

    表示此服务已经运行。

    备注:在SVN运行过程中,此窗口 必须一直打开运行。

    服务运行后再次查看创建的版本库

    如图所示,创建版本库成功。
    三、在版本浏览器里面,创建文件,并进行检出
    如何在版本库里面创建文件
    A.在根目录下点击“右键”点击“创建文件夹”

    C.点击“创建文件夹”

    输入文件夹名“trunck”
    D.填写“创建日志”

    E.认证:输入用户名称和密码:
    用户名:p1_a1
    密码:p1_a1

    F.创建成功
    按照此方法在“trunck”根目录下创建“需求分析、RTM文档、测试报告、代码文档、概设文档、详设文档、用例文档”7个文件夹。

    在代码文档里面添加5个代码程序

    A.将5个代码程序 选中后直接拖入“代码文档”中;
    B.点击“复制项到此”

    添加成功。
    C.同样的方法,添加另外4个文件:min.c;mul.c;div.c;main.c;

    添加完成。
    检出添加的文件夹及文件
    A.A.在D盘根目录下建立文件夹“worksp”

    备注:此文件夹为空文件夹
    B.空白处点击“点击右键”

    C.点击“SVN检出(K)….”

    D.检出完成

    备注:检出成功
    E.打开“wroksp”文件夹,出现“trunck”文件夹

    F.打开“trunckt”文件

    G.打开“代码文档”

    备注:版本库浏览器里面的文件全部被检出,检出成功(如图所示)。

    四、如何对该项目入基线
    第一步:打开D盘文件夹“worksp”找到“trunck”文件夹。
    第二步:选中“trunck”文件夹点击“右键”
    选择“分支/标记”

    点击“确定”

    第三步:在D盘根目录中选择“worksp”文件夹,点击“右键”选择“提交更新”

    第四步:查看入基线是否成功,打开文件“worksp”

    第五步:通过“版本浏览器”进行查看;

    入基线成功。

    五、分支文件进行合并
    第一步:在D盘根目录下创建两个文件夹“group1”、“group2”

    第二步:用入基线的操作的方式,创建两个分支“branch1”、“branch2”

    “group1”文件夹

    “group2”文件夹

    第二步:
    “group1”–“branch1”中“代码文档”内添加文件:chengfang.c
    “group2”—“branch2”中“代码文档”内添加文件:kaifang.c
    以添加“chengfang.c”为例:
    A.添加文件“chengfang.c”

    B.选中“chengfang.c”文件点击“增加”

    C.点击“增加”后

    D.选中后“右键”点击“SVN提交”:

    备注:未提交前“代码文档”显示为:

    E.点击确定、添加成功

    添加“kaifang.c”
    同样的方法进行添加
    添加成功后:

    校验是否添加成功
    方法1:点击“worksp”进行更新,检查“branch1”、“branch2”中是否存在添加的两个文件;
    方法2: 版本浏览器查看

    将“chengfang.c”和“kaifang.c”进行合并;
    A.“合并”前“trunck”里面如图

    B.“合并”步骤:
    a.点击 “worksp”中的“trunck”点击“右键”

    将“kaifang.c”合并

    同样的方法对“branch1”中的“changfang.c”进行合并
    C.对“trunck”文件右键进行“提交”

    D.查看合并是否成功
    方法1:“worksp”----“trunck”----“代码文档”查看

    方法二:版本浏览器进行查看

    六、分支冲突的解决
    在D盘根目录下创建2个文件夹分别为“dev1”、“dev2”

    分别点击该文件夹的“右键”—“检出”—“trunck”

    针对“dev1”里面的“代码文档”

    进行修改添加“除数不能为0”;

    修改后“保存”

    “右键”点击“提交”

    针对“dev2”—“代码文档”里面

    进行修改添加“除数不能为0”;

    修改后“保存”

    “右键”进行提交“提交”

    提交失败,起冲突。
    解决冲突
    A.对“dev2”中的“div.c”文件进行更新

    B.更新完成后打开该文件:

    C.对该程序进行修改,完成后对该文件进行“提交”

    D.然后对“代码文档” —“右键”进行“更新”

    E.查看冲突是否解决
    方法1:“worksp”—“trunck”—“代码文档”—“dev.c”
    方法2: 版本浏览器进行查看。

    第十二章 系统测试
    概念:
    1什么事系统测试
    在对软件,硬件,外设,第三方软件,数据,人员,环境,代码,数据库考虑了的情况下,尽可能的模拟用户的使用环境下所做的测试
    另一答案:将已集成的软件系统,作为整个基于计算机系统的一个元素,与计算机硬件、外设、数据、支撑软件、人员等其他元素结合在一起,在实际运行环境下,对计算机系统进行的一系列测试活动
    注:代码称之为数据流,数据称之为信息流
    2系统测试在做什么工作
    验证系统是否满足需求规格说明书SRS
    验证是都满足隐性需求
    验证系统可以支付用户使用
    3系统测试的工作过程
    系统测试个阶段(计划,设计,实现,执行)的输入,输出,入口准则,出口准则
    4系统测试的对象:对整个产品(整个软件系统)
    5系统测试依据什么:SRS
    6系统:整体性、独立性、功能性、环境适应性
    7软件系统:包括支撑软件、数据、硬件、外设、人员及目标软件
    分类:
    功能测试:(Function testing中国 Feature testing国际)
    根据产品的功能是否满足用户需求
    工作内容 1业务,业务流是否正确实现
    2功能点是否正确实现
    3输入是否正确实现
    4输出是否正确实现
    测试工具:QTP(使用 B/S和C/S软件),LR(C/S软件),RIA TEST(针对Flash,Flex软件,用Flash编写),Selenium(firefox的插件), IBM Rational Robot
    工作原理:模拟用户操作
    性能测试:(Sercarity testing)
    定义:验证产品是否满足性能需求
    常用测试点:响应时间和资源性能(CPU,I/O,内存,信道,传输速度)
    例:1.先确认记事本的性能需求(加入50万行内正常)
    2.先输入50万行文本(造场景)
    3.监控所占用的系统资源
    4.报告,提出改进意见
    注:1内存和硬盘的区别
    内存临时存储,电脑关机,信息就消失,内存容量速度快
    硬盘是靠执行存储,断电后也能保存数据,硬盘存储量大,速度慢
    2信道与宽带有关,与访问人数有关
    3吞吐量,每个时间单元的处理数据
    4测试方法:压力测试(stres stesting)短时间
    负载测试(load testing)正常负荷下,长时间运行
    容量测试(volume testing)最大访问量
    5高级录制能找到每个按钮的控件名,低级录制只能找到鼠标移动到的位置
    6没有需求的性能测试无法进行
    测试工具:LR,Webload , Jmeter(开源软件,JAVA编写),Silk Performance
    工作原理:(B或者C端通过传送协议与S进行通信)
    录制协议,模拟多个永和传送协议(可以几句到以点,同时发给S也可以迭代发送给S)通过IP骑在变成不通的IP地址,突显多线程并发操作
    安全性测试:(Security Testing)

    定义:验证产品在系统的内部的安全保护机制和系统外对入侵的防护能力
    考虑内容:1内部包括身份验证,权限,数据的完整一致,数据的保密性(DB中有些数据加密保存)
    2外部,病毒木马,未授权攻击,传输数据安全(传输过程中密码加密,通过HTTP协议传输)
    注:病毒与木马的区别:病毒主要是针对计算机程序的破坏,木马主要用于盗取计算机内的相关机密
    测试工具:MBSA(Microsoft Basehne Sercurity Analyzer),IBM Rational Appscan,X-scan(某黑客组织开发)
    工作原理:监控服务器或客户端那些端口应该被禁止
    安装测试
    定义:产品的安装过程和结果进行测试
    工作内容:根据软件的测试特性列表,软件安装,配置文档,设计安装过程的测试用例
    包括安装前
    安装后
    安装过程中
    安装时异常终止包括:进程终止(操作系统未关闭)断电模式,断网
    测试对象:安装文件、安装系统、安装文档、配置项
    安装后检查:1件是否产生,是否正确
    2否按人工修改的方式安装
    3装日志是否记录正确
    4否有临时目录,垃圾文件,是否清理掉冷补丁(需要关闭系统的补丁) 热补丁(多数网站补丁属于热补丁)
    GUI测试(Graphical user interface)
    又称为用户界面测试,接口测试
    注:GUI测试无法脱离功能测试而独立存在,否则只能测试外观,布局,无法测试行为
    测试内容:对界面元素进行测试,
    验证产品界面是否与SRS设计一致(包括布局,外观,配色,元素行为,元素功能)
    验证界面元素的交互是否正确
    界面元素包括:整体界面,独立元素组合,独立元素
    思路:先找整体界面,再测独立元素组合和独立元素(据对用户的影响严重程度不同)
    测试步骤:对完整的界面进行测试 (包括布局,功能组合,页面展现的外观)
    组合元素(包括拆分,组合行为,独立行为,展现,外观)
    独立元素(包括外观,为)
    工具:所有做功能测试的工具都可以做GUI的测试
    可用性测试(Usability testing)
    又称易用性测试

    定义:验证用户管理和使用软件的容易程度
    工作内容:是否符合用户的实际习惯
    是否符合用户的特殊要求
    操作是否合理,简单,容易理解
    是否有明确的提示可以指导下一步操作
    异常性测试
    系统允许的操作
    健壮性测试,系统不允许的操作
    文档测试
    说明书,与易用性一起做
    备份测试
    主要测试备份策略
    目的:为了解决风险
    备份策略包括:本地备份,异地热备份(实时备份),本地异步备份,异地异步备份
    还原策略:(又称恢复策略)
    配置测试
    又称兼容性测试
    包括软件配置和硬件配置,验证系统都可以使用那些软硬件配置
    网络测试

    主要测试网络中的协议和数据
    工作内容:1网络协议测试(协议是否正确)
    2网络传输是否正确(不同网络是否正常)
    3网络结果测试

    第十三章 用例设计
    等价类
    定义:1、等价:如果多个输入在程序中处理方式(路径)相同,则认为这些输入是等价的,测试一个即可。(前提:测试不能穷举)
    2、输入:分为两类,有效输入(可以保存)、无效输入(不可保存)
    3、结合:有效等价类、无效等价类
    满足规则:只需寻找一个全部满足规则的(有效等价类)
    不满足规则:需分开,每条不满足条件的举出一个,方便定位错误(无效等价类)
    规则的几种情况:
    1 若规则是布尔式的
    有效、无效分别取一个 一个真,一个假
    2. 若规则是区
    有效的取一个 无效的,在小于区间的取一个,大于区间的取一个,空或零取一个
    3. 规则是集合:
    有效取一个 无效的在集合外取一个
    4. 规则是必须满足的一个条件:
    对无效,要细分无效(先试试其他有效等价类,再试试全部等价类全都不满足)
    例:邮箱注册,注册名只能以小写字母开头,设计无效等价类时,先试试其他有效等价类,如大写字母、数字等,再试试其他有效全部都不满足的情况
    有效等价类:程序规格说明有意义,合理的输入数据
    无效等价类:程序规格说明无意义,不合理的输入数据

    ASCII码
    7位 表示27=128个字符 每个字符存储占用1个字节
    分类:不可见字符
    控制字符:LF(换行)、CR(回车)
    通信字符
    NULL(空)
    可见字符
    空格
    数字
    字母(大写、小写)
    符号
    练习
    1.1年龄注册
    注册页面要求输入年龄,限制:1到150期间的正整数
    1、需求分析—找出全部的输入条件
    1)、正整数
    2)、[1,150]
    注意:分解的过程中,条件之间不用组合(完全独立),在设计有效用例输入的时候再考虑组合(一对多)
    2、绘制等价类表格
    条件 有效等价类 有效编号 无效等价类 无效编号
    正整数 >1 A01 数字 小数 B01
    负数 B02
    0 B03
    非数字 空(NULL) B04
    空格 B05
    字母 B06
    符号 B07
    [1,150] [1,150] A02 <1 B08
    >150 B09

    3、设计测试用例输入
    原则:有效输入—尽可能多去覆盖有效等价类 (一对多)
    无效输入—只能覆盖一个无效等价类 (一对一)
    目的:A、出现问题可以更好的去定位
    B、有可能第一个无效处理后,后续无效值被漏测
    输入序号 有效输入值 覆盖有效等价类 输入序号 无效输入值 覆盖无效等价类
    1 100 A01、A02 2 1.8 B01
    3 -9 B02
    4 0 B03、B08
    5 空(NULL) B04
    6 空格 B05
    7 W B06
    8 @ B07
    9 200 B09
    1.2.年龄注册
    某保险公司注册页面要求输入年龄,限制:1到150期间的正整数,其中:
    [1,10] 适用的费率 10%
    [11,50] 适用的费率 30%
    [51,150] 适用的费率 80%

    条件 有效等价类 有效编号 无效等价类 无效编号
    正整数 >1 A01 数字 小数 B01
    负数 B02
    0 B03
    非数字 空(NULL) B04
    空格 B05
    字母 B06
    符号 B07
    [1,150] [1,10] A02 <1 B08
    [11,50] A03 >150
    [51,150] A04

    输入序号 有效输入值 覆盖有效等价类 输入序号 无效输入值 覆盖无效等价类
    1 5 A01、A02 4 1.8 B01
    2 30 A01、A03 5 -9 B02
    3 100 A01、A04 6 0 B03、B08
    7 空(NULL) B04
    8 空格 B05
    9 W B06
    10 @ B07
    11 200 B09
    1.3.扩充
    考虑全角和半角问题(GB231)
    其中,半角占1个字节
    全角占2个字节

    字符集
    1、ASCII
    2、GB2312 -> GB18030 一个汉字占2个字节
    BIG5(繁体)
    3、ISO Unicode (UTF-16) -> UTF-8(变长存储) 一个汉字可能占3个字节
    条件 有效等价类 有效编号 无效等价类 无效编号
    正整数 >1(半角) A01

    			0	B03
    >1(全角)	A02	半角字符	空(NULL)	B04
    			空格	B05
    			字母	B06
    			符号	B07
    		全角字符	空格	B08
    			字母	B09
    			符号	B10
    		汉字
    

    (字符集) GB-2312 B11
    BIG-5 B12
    [1,150] [1,150] A03 <1 B13
    >150 B14

    分解粒度:根据功能的重要性决定(用例图----执行者角色,考虑用户的关注功能)
    根据成本、时间决定
    取消负数、小数的原因:
    1、前提:在编辑框内输入年龄,程序在接收编辑框的内容时,一般以字符(或字符串)的形式接收,再根据需要进行类型转换,如年龄,需要转换为整型
    2、小数中的“小数点”和负数中的“负号”都认为是符号,不允许保存
    边界值
    取值(5个):上点、内点、离点
    1、上点、内点取值与区间的开闭无关
    2、离点和上点互为有效
    作用:(有序、有范围)等价类的补充
    补充:考虑数据类型的边界
    如 int 类型 占4个字节 即 32bit 取值范围[-231, 231-1]
    上点:边界上的点
    内点:区间内的点
    离点:离边界值最近且与上点不属于同一等价类的点(对于小数,没有离点,不用取)
    规则的几种情况:

    1. 规则是区间:
      按上点、内点、离点取
    2. 规则是取值的个数:
      取最小、最大、中间个数
      3.规则是a~z序列:
      取a、z
      4.规则是集合:
      取一个集合内的,取一个集合外的
      (0,20)上点是0,20 离点是1,19 [0,20]上点是0,20 离点是-1,21
      若规则是下拉菜单:建议每个下拉值都选择一个
      2.1.年龄
      条件 有效等价类 有效边界值 有效编号 无效等价类 无效边界值 无效编号
      正整数 >=1 上点:1 A01 数字 小数 B01
      内点:100 A02 负数 B02
      离点:0 0 B03
      非数字 空(NULL) B04
      空格 B05
      大写字母 上点:A B06
      上点:Z
      内点:M
      符号 离点:@ B07
      离点:[
      [1,150] [1,150] 上点:1 A03 离点: 0 B08
      上点:150 A04 离点: 151 B09
      内点:100 A05
      有效用例输入:1 、100、150
      2.2.用户名注册

    1、测试需求分析
    1、内容:字母、数字、
    2、长度:[6,18]
    3、约束:字母开头
    字母或数字结尾
    不允许重名
    不允许使用保留字
    自动去除首尾空格
    2、等价类划分
    条件 有效等价类 有效编号 无效等价类 无效编号
    内容 字母 大写字母 A01 符号(除下划线) B01
    小写字母 A02 NULL(空) B02
    数字 [0,9] A03 空格 B03
    下划线 下划线 A04 GB2312 B04
    BIG5 B05
    长度 [6,18] [6,18] A05 <6 B06
    >18 B07
    约束 字母开头 大写字母开头 A06 数字开头 B08
    小写字母开头 A07 下划线开头 B09
    符号(非下划线)开头 B10
    汉字开头 B11
    字母或数字结尾 大写字母结尾 A08 下划线结尾 B12
    小写字母结尾 A09 符号(非下划线)结尾 B13
    数字结尾 A10 汉字结尾 B13
    不允许重名 不重名 A11 重名 B15
    去除首尾空格 A12
    不允许使用保留字 不使用 A13 使用保留字 B16
    3、用例设计
    序号 有效输入 有效边界值 序号 无效输入 无效边界值
    1 (当前系统无AAb_4)
    AAbb_4 A01、A02、A04、A03
    A05、A06、A10
    A11、A13 5 abc@a B01
    2 (当前系统无AAb_4)
    aAbcvb_A A01、A02、A04、A03
    A05、A07、A08
    A11、A13 …………
    3 (当前系统无AAb_4)
    aA555b_b A01、A02、A04、A03
    A05、A07、A09
    A11、A13 (当前系统存在aaf5bc)
    (空格)aaf5bc(空格) B15
    4 (当前系统无aaf5bc)
    (空格)aaf5bc(空格) A12 Administrator B16
    2.3.变量命名
    在某一版的编程语言中对变量名称规格作如下规定:变量名称是由字母开头的,后跟字母或者数字的任意组合构成,并且区分字母大小写。编译器能够区分的有效字符数为8个,并且变量名称必须先声明,后使用,在同一声明语句中,变量名称至少必须有一个。
    1、测试需求分析
    内容:字母、数字
    长度:[1,8]
    约束:字母开头
    大小写敏感
    先声明后使用
    变量的个数[1,?] (需要和需求人员沟通)
    在同一作用域中不允许重名
    不能使用关键字
    2、等价类划分
    3、确定边界值
    4、设计用例输入
    2.4.进销存价格

    代码实现:
    if (isNumberString(document.form2.tichen.value,“1234567890.”)!=1)
    {
    alert(“价格只能为数字!”);
    return false;
    }

    根据代码的实现,价格编辑框只接受键盘的“数字键”和“小数点”共11个字符

    等价类
    条件 有效等价类 无效等价类
    内容 数字 字母
    字母、符号处理方式(路径)相同,认为都是等价的
    小数点
    约束 小数点最多有1个 >1个小数点

    2.5.Windows文件命名
    目录 目录长度 文件最大长度 目录+文件最大长度
    C:\ 3 255个 = 258
    C:\ABCD 7 251个 =258
    C:\ABCD\AAAA 12 246个 =258

    根目录: 255
    非根目录: 254 (文件夹最小长度为1)

    1、测试需求分析
    内容:非“/😗?"<>|”的字符
    长度: 根目录: 255
    非根目录: 254 (文件夹最小长度为1)
    约束:同目录下不能重名(去除首尾空格)
    不同扩展名使用不同的图标
    不能为空
    总结
    1、适用范围:针对程序的输入
    2、使用步骤
    1)、测试需求分析,找出全部条件(显示、隐士)(条件之间不能交叉或者组合,到设计用例的时候再考虑多条件的组合)
    2)、划分等价类
    划分依据:相同的处理方式(路径)
    分类:有效等价类、无效等价类
    3)、使用边界值补充(有序、有范围的)等价类
    内容:上点、离点、内点
    要求:上点、内点与区间开闭无关,离点和上点互为有效
    分类:将边界值分为有效边界和无效边界,填入等价类表格
    4)、对每个等价类进行唯一的编号
    5)、设计用例的输入
    原则:有效等价类,一对多
    无效等价类,一对一
    3、优点:对输入的考虑充分
    4、缺点:如果不清楚系统的实现方式,造成大量的冗余用例(黑盒方法共有)
    对于多输入的组合不太适宜
    规则的几种情况:
    1 .若规则是布尔式的
    有效、无效分别取一个 一个真,一个假
    2. 若规则是区间
    有效的取一个 无效的,在小于区间的取一个,大于区间的取一个,空或零取一个
    3. 规则是集合:
    有效取一个 无效的在集合外取一个
    4. 规则是必须满足的一个条件:
    对无效,要细分无效(先试试其他有效等价类,再试试全部等价类全都不满足)
    例:邮箱注册,注册名只能以小写字母开头,设计无效等价类时,先试试其他有效等价类,如大写字母、数字等,再试试其他有效全部都不满足的情况
    有效等价类:程序规格说明有意义,合理的输入数据
    无效等价类:程序规格说明无意义,不合理的输入数据
    边界值
    上点:边界上的点
    内点:区间内的点
    离点:离边界值最近且与上点不属于同一等价类的点(对于小数,没有离点,不用取)
    规则的几种情况:

    1. 规则是区间:
      按上点、内点、离点取
    2. 规则是取值的个数:
      取最小、最大、中间个数
      3.规则是a~z序列:
      取a、z
      4.规则是集合:
      取一个集合内的,取一个集合外的
      (0,20)上点是0,20 离点是1,19 [0,20]上点是0,20 离点是-1,21
      若规则是下拉菜单:建议每个下拉值都选择一个

    第十四章 系统测试执行
    测试用例
    编号组成: 项目名—测试阶段—需求—用例
    测试环境搭建文档:
    1.为日后回归测试等搭建环境做指导
    2.刚开始写完后,评审,防止日后因环境搭建而引起测试时的问题
    3.可为日后上线的文档做参考
    4.可以做自动化步骤的参考
    5.项目留存
    用例执行:
    1.严格按照用例步骤,执行用例
    2.发现结果与预期结果不一致:
    a)重复步骤,重现发现的问题
    b)找同类型的数据,重新执行用例
    c)定位发现问题的步骤 (每执行一步都要做记录)
    3.提交BUG (先确认不是重复BUG)
    填BUG报告:
    (一)若开发返回”不可重现的BUG”,可能原因:
    1.开发步骤不对
    2.测试环境和开发环境不同
    3.出现时有概率的,或者其他原因导致,不是每次重现 (环境、概率)
    (二)合并BUG的原则
    1)同样的原因产生的BUG可以合并
    2)与开发沟通后如果修改一处就可以修改一些BUG可以合并
    3)当BUG之间有制约关系,BUG可以合并
    (三)补充 为什么.TXT文件中报春”联通”重新打开后会有乱码
    文件保存格式默认为ASCII码,但恰好”联通”是以EF开头的,保存时误认为UFT-8格式,导致重新打开时歘先乱码
    (四)书写测试用例应该注意一下几点
    1)测试输入数据必须是唯一的,并且明确的
    2)步骤要完整,按照步骤可以测试测试点和预期结果,建议每个步骤后面都有预期结果,步骤要编号
    3)结果要和需求规格说明书完全一致,如果SRS没有明确结果,需要产品人员补充和定义
    4)无效等价类不能合并

    第十五章 QC(Quality Center)
    QC (Quality Control)质量控制
    QC(前身TD Test Director与QTP、LoadRunner同是MI公司开发)
    QC是B/S架构的
    QC 9.0支持JBoss、IIS两款服务器支持SQL Server和Oracle两个数据库
    QC默认端口是8080 数据库连接端口是1433
    Quality Center QC前台
    Site Administration QC后台
    Add-Ins Page QC插件
    Qcsiteadmin_db 是保存后台数据的 而前台中项目的数据,是单独保存在另一个数据库中
    QC预置5个用户组,只能查看,不能修改权限
    Developer Project Manger QATester TDAdmin Viewer
    外部测试人员,只有Defects Module权限,只执行测试,报BUG
    组内测试人员,有QC所有模块权限
    QC后台:
    Site Project 设置项目
    Create Domain 建立区域
    Create Project 建立项目
    Create an empty Project 建立一个空项目
    Create project by copying data from an existing project
    从已有项目拷贝创建一个新项目

    Create a project by importing data from an exported Quality Center project file
    从一个已有项目的导出文件创建一个新项目

    QC前台:
    TOOLS下的Customize…选项
    Customize Project Entities 设置输入项 可添加新选项
    Customize Project Lists 项目列表 可设置下拉列表中的项
    Requirements 需求模块
    菜单栏下Requirements菜单项
    Covert to Tests 将需求转换成测试用例 用于比较系统的测试
    Generate Test…将需求生成测试执行 用于临时的测试
    菜单栏中View菜单项
    Expend 扩展,全部展开
    Collapse 收起
    Numeration 排序
    Full Coverage 完全覆盖(考虑测试覆盖率)
    Requirements Tree 需求树结构 用于项目经理等管理者
    Requirements Grid 需求表结构 用于浏览者(可用Favorites)
    Requirements Coverage 需求覆盖率 用于测试人员
    Coverage Analysis 需求分析视图 用于测试管理者
    菜单栏中Analysis菜单项
    Reports选项下
    Standard Requirements Report 标准需求表 用于评审
    Tabular Requirements 概要表 用于产品经理
    Requirements with Coverage Tests 带覆盖率的表 用于测试经理
    Requirements with Coverage Tests and Steps 带步骤的 用于测试用例评审
    Requirements with Linked Defects 带缺陷的 用于生成测试报告
    Graphs选项下
    Progress 给项目经理
    Requirements Coverage 给测试经理
    Trend 给BOSS
    工具条中Set filter/Sort 过滤器 用*做通配符
    View Order 结果排序 同级才能排序
    双击一条需求 Requirement Details 需求详细信息
    Coverage 覆盖率
    Linked Defects 连接缺陷
    需求的状态:not covered not run not complete passed failed
    Test Plan 测试用例模块
    点击一条测试用例,右侧在Design Steps标签页下
    Call to Test 调用测试用例 只能调用到一个参数 可用于回归测试
    Generate Script 生成脚本
    Insert Parameter 添加参数
    Renumber Steps 重新写序号 (不影响步骤的内容)
    菜单栏中Tests菜单项
    Flag for Follow up 标记一个执行
    Mark as Template Test 标记成为模板用例
    菜单栏下View菜单项
    Trace Changes 变更跟踪警告
    Analysis中所有报表均是测试工程师用
    Test Lab 测试执行模块
    需要添加一个集,用例才能被添加到集
    菜单栏下Test Sets菜单项
    Reset Test Set 重置测试集 用于测试回归,所有状态均为No Run
    Purge Runs 清理执行的状态 清理执行的状态,上个是全部,此为条件式
    状态优先级:
    Failed > Not Complete或N/A或No Run > Passed
    Trace Changes
    在Test Plan中,跟踪的是需求Requirements
    在Test Lab中,跟踪的是缺陷Defects状态时”Fixed”时
    在Defects中,跟踪的是Test Lab成功运行时
    其中红色! 表示未看过 灰色表示已看过
    QC中,Defects只能连接自己,不能连接其他而 Requirements、Test Plan、Test Lab可以连接其他任何一个,不能连接自己,所以,Defects单独划出一个模块
    权限设置:
    需求人员:写需求
    测试人员:用例、BUG、执行用例
    开发人员:改BUG
    项目经理:所有权限
    当产生纠纷时,在设置中增加一个状态,在Defects中的Description选项中的Comments下添加分歧的描述、原因等等,分配给解决纠纷的人,只有被分配的人可以修改状态,其他人不能更改

    变更控制:
    能否变更 用Reviewed(评审)字段,将之设置成必填项,默认值是Not Reviewed
    设置权限中,不给需求人员修改Reviewed的权限

    第十六章 PYTHON
    Python 是面向过程、面向对象,跨平台的高级脚本语言。 是解释性语言,执行源代码,提供丰富的库,支持多线,多进程,网络编程,可以支持C/C++的代码,可以测试C/C++和Python开发的单元产品,Python越来越多的成为开源测试自动化工具的首选语言
    Python的安装
    1.安装之前,先退出杀毒软件
    2.安装 缺省安装
    3.完毕后,配置系统环境变量 path (把解释器pathon.exe所在目录加入path)
    Python的集成环境:
    1.Python文件夹下IDLE
    2.新建文件 New Window
    3.保存(用Save As…后缀.py)
    4.运行方式:
    a)Run Module (若提示”Socket Error”是因为杀毒软件引起的错误)
    b)命令行方式 在DOS下,进入文件目录,输入命令python hello.py
    python可以用#来做注释
    输入中文时在前面加#coding:gbk
    在GUI界面下 若不加保存时会报警告

    补充知识:
    DOS命令:
    切换盘符 盘符名:
    进入文件夹 cd 文件夹名\文件夹名…
    退到根目录 cd
    退出一层目录 cd…
    数据类型:
    Str字符型字符串(可以用单引号、双引号) Int 整形 long长整形 float浮点型 bool(布尔型):True真/False假(首字母大写,不用加引号)
    运算符:
    算术运算符: + — * /
    逻辑运算符: and or not
    比较运算符: 相等 = = 不等于 != < > <= >=
    缩进:
    在行首用空格或者Tab 缩进相当于C语言中的{ } 表示对应一段完整的逻辑
    一般用TAB 因为一段代码中不能同时使用两种缩进方式,默认是TAB
    控制语句:
    IF条件
    1 if (条件1):
    处理语句1
    处理语句2
    处理语句3

    语法解释:满足条件1执行语句1,2,3,不满足条件1执行语句
    2 if (条件1):
    处理语句1
    处理语句2
    elif
    处理语句3

    语法解释:满足条件1执行语句1,2不满足条件1,执行语句3
    3 (1) if (条件1): (2)if (条件1):
    处理语句1 处理语句1
    处理语句2 处理语句2
    elif (条件2): else:
    处理语句3 if(条件2):
    处理语句4 处理语句3
    … 处理语句4
    else: else:
    处理语句5 处理语句5
    处理语句6 处理语句6
    … 。。。。
    处理语句7 处理语句7

    语法解释:满足条件1执行语句1,2,7,不满足条件1,满足条件2,执行语句3,4,7,不满足条件1,2,执行语句5,6,7
    注:条件中可以用逻辑运算符 比较运算符
    条件后面要加“;”
    WHILE循环
    while (条件):
    处理语句1
    处理语句2

    处理语句3

    语法解释:条件为真(满足条件)执行语句1,2,在判断条件,还满足条件的话,还执行1,2直到不满足条件,执行语句3

    while (条件1):
    if (条件2):
    处理语句1
    处理语句2

    elif (条件3):
    处理语句3
    处理语句4

    else:
    处理语句5
    处理语句6

    处理语句7

    语法解释:条件1为真,判断条件2,条件2为真,执行语句1,2,在判断条件1,当条件1 为假,执行7
    条件1为真,判断条件2,条件2为假,,在判断条件3,当条件3为真,执行语句3,4,判断条件1,当条件1为假,执行语句7
    条件1为真,判断条件2,条件2为假,,在判断条件3,当条件3为假,执行语句5,6,判断条件1,当条件1为假,执行语句7
    FOR循环
    for 变量 in 集合函数: 注:range是一个集合函数
    处理语句1
    处理语句2

    处理语句3

    语法解释:变量值在这个集合函数之内执行语句1,2,循环执行1,2,直到变量值不在集合函数范围内,执行语句3
    例题:for I in range(1,7); #循环了6次
    print”sjsddsj”
    BREAK \ CONTINUE
    break \ continue 只能用在循环语句中(for ,while)。
    break:跳出循环,执行循环体外的第一条语句,无论循环体内还有多少语句
    continue:跳过循环体内的语句去执行循环条件的条件判断

    	while (条件1):
    		  处理语句1
            处理语句2
    			if (条件2):
        			处理语句3
       			break
    		 	else:
        			处理语句4
        			continue
    

    处理语句5
    处理语句6

    语法解释:条件1为真,条件2为真,执行1,2,3,6
    条件1为真,条件2为假,执行语句1,2,4,1,2,4执行到条件1为假后,执行语句6
    条件1为假,执行语句6
    这个程序没有执行过语句5

    程序举例:
    a=1
    i=1
    while (a<=10):
    a=a+1
    if (a==10):
    break
    else:
    continue
    i=i+1
    print a
    print i
    运行结果:a=10 i=1
    函数
    1 type () 查看变量的数据类型

    2 Raw-input (“提示信息”) 输入函数(从界面输入的均是字符型)

    3 isdigit() 判断变量a是否是纯数字

    4 int() 强制转换类型

    5 Print输出函数
    6 range 集合函数
    Range(起始值,结束值,步长)求从开始值到小于结束值,并且以步长为某一个值的等差数列

    定义:
    Def 函数名(形参列表):
    函数体语句
    Return 返回值 #若没有返回值,不加这句
    程序举例:Def fun(a,b,c):
    Print a,b,c
    Return a+b+c
    调用:
    有返回值
    变量名=函数名(实参列表)
    例:M=fun(‘3’,’4’,’asd’)
    无返回值
    函数名(实参列表)

    第十七章 单元测试
    单元测试概念:
    1什么是单元测试:对软件的基本组成单元所作的测试(面向过程(一组函数或一个函数)面向对象(类中得方法))
    2语句:真正的处理语句才算是语句(判断框中的语句不算)。
    判定:流程图中的菱形框;
    判定数:流程图中菱形框的个数;
    分支:判定的真或假;
    分支数:判定数2;
    条件:判定中关系(比较)运算符;
    条件数:判定中关系(比较)运算符的个数;
    条件结果数:条件数
    2(每个条件有真、假两个取值)。
    3单元测试的目的:与LLD是否符合;与SRS是否符合;编程是否存在问题。
    4关注重点包括:单元接口(类型,顺序,长度,参数个数);局部数据结构;独立路径;边界值;出错处理。
    5单元测试环境包括:被测单元、驱动单元(把被测单元驱动起来,完成被测单元的调用)、桩单元(被测单元的调用的替代品,替代输入与输出),测试用例(测试数据)。
    6驱动单元的四个职责1)接收侧四数据包含测试用例输入好预期输出
    2)吧测试用例输入传送给要测试的单元
    3)将被测单元的实际输出和预期输出进行比较,得到测试结果
    4)将测试结果输出到指定位置
    7辅助技术:评估,框架应用
    8桩单元:通过一组输入和输出模拟被替代单元的行为
    单元测试静态测试:
    1.从详设的评审开始介入
    2.详设评审、编码完成后,作代码的静态检查
    (可以用专门的检查工具 如:PC_lint)
    3.代码的交叉走读 (要制定标准,标准越清晰,任务被分配者越有目标,工作越细)
    单元测试动态测试:
    1写用例之前,先确定覆盖率标准
    2写用例
    3搭建测试环境
    4执行
    5测试报告
    测试评价准则:
    系统测试评价准则:需求覆盖率(=至少被用例覆盖一次的需求项数/测试需求分析列表中的需求项数)。
    单元测试评价准则:逻辑覆盖率(=item至少被评估一次的次数/item总数)【这是一个总公式】。
    逻辑覆盖率
    逻辑覆盖率包括以下几种:语句覆盖率、分支覆盖率、条件覆盖率、分支条件覆盖率、路径覆盖率。【掌握计算公式、每种覆盖率的问题】
    1语句覆盖率=语句至少被执行一次的次数/语句总数。
    问题:有可能语句覆盖率为100%,有可能发现逻辑运算符的问题
    2判定覆盖率=每个分支取值至少被执行一次的次数/分支总数(判定数2)
    问题:当分支覆盖率为100%时,可能不能发现关系运算符的缺陷
    3条件覆盖率=每个条件取值至少被执行一次的次数/条件结果总数(条件数
    2)
    问题:条件覆盖率为100%时,有可能某个分支没有执行到,若该分支有缺陷,可能会遗漏。
    4分支覆盖率=(每个分支至少被执行一次的次数+每个条件取值至少被执行一次的次数)/(分支总数+条件结果数)
    问题:分支条件覆盖率为100%时,有可能漏路径,若该路径上有缺陷,可能遗漏
    5路径覆盖率=每个路径至少被执行一次的次数/路径总数
    问题:路径覆盖率为100%时,条件覆盖率可能不为100%
    注:独立路径覆盖,若路径覆盖率100%则条件、语句、分支覆盖率均100%
    路径一定要从始点到终点
    可以用软件来计算路径覆盖率 如BullseyeCoverage

    单元测试策略
    孤立、自顶向下、自底向下。
    ⑴ 孤立测试
    缺点:需要大量的桩和驱动
    优点:改方法是最简单最容易操作的 ,可以达到高的结构覆盖率,该方法是纯粹的单元测试
    方法:不考虑每个模块与其他模块之间的关系,为每个模块设计桩模块和驱动模块,每个模块进行独立的单元测试
    【例1】
    对象 驱动 桩
    A Driver-A Stub-B
    B Driver-B Stub-C
    C Driver-C X

    【例2】
    对象 驱动 桩
    A Driver-A Stub-B,C
    B Driver-B Stub-D
    C Driver-C Stub-E
    D Driver-D X
    E Driver-E X

    ⑵自顶向下的单元测试策略
    方法:先对最顶层的单元进行测试,把顶层所调用的单元做成桩模块。其次,对第二层进行测试,使用上面已测试的单元做驱动模块。如此类推直到测试完所有模块。
    优点:可以节省驱动函数的开发工作量,测试效率高。
    缺点:随着被测单元一个一个被加入,测试过程将变得越来越复杂,并且开发和维护的成本将增加。
    【例1】
    对象 驱动 桩
    A Driver-A Stub-B
    B Driver-A Stub-C
    C Driver-A Stub-D
    D Driver-A X

    【例2】
    对象 驱动 桩
    A Driver-A Stub-B,C,D
    B Driver-A Stub-E,C,D
    C Driver-A Stub-E,D
    D Driver-A Stub-F,E
    E Driver-A Stub-F
    F Driver-A X

    【例3】
    对象 驱动 桩
    A Driver-A Stub-B,C
    B Driver-A Stub-C,D
    C Driver-A Stub-E,D
    D Driver-A Stub-F,E
    E Driver-A Stub-F
    F Driver-A Stub-G,H
    G Driver-A Stub-H
    H Driver-A X

    a. 看被测函数下边有没有调用,如果被调用则打一个桩
    b. 假如曾经被测过的函数的时候,这些函数下边是否有调用,而那些调用是否也被测过,如果没有被测过,也需要打桩
    c. 只要驱动单元驱动的时候,所依赖的函数要run的时候,相关的调用都需要加入
    ⑶自底向上的单元测试方法
    方法:先对模块调用层次图上最低层的模块进行单元测试,模拟调用该模块的模块做驱动模块。然后再对上面一层做单元测试,用下面已被测试过的模块做桩模块。以此类推,直到测试完所有的模块
    优点:可以节省桩函数的开发工作量,测试效率较高。
    缺点:不是纯粹的单元测试,底层函数的测试质量对上层函数的测试将产生很大的影响。
    对象 驱动 桩
    E Driver-E X
    C Driver-C X
    F Driver-F X
    B Driver-B X
    D Driver-D X
    A Driver-A X

    如果桩难写,采用自底向上;如果驱动难写,采用自顶向下。
    打桩原则:1被测函数有没有调动,若有调动则打桩
    2加入曾经被测过的函数,这些函数是否有调动,这些调动是否被测过,都没有则打桩
    【例子】
    自底向下
    对象 驱动 桩
    F Driver-F X
    I Driver-I X
    J Driver-J X
    G Driver-G X
    H Driver-H X
    E Driver-E X
    C Driver-C X
    D Driver-D X
    B Driver-B X
    A Driver-A X
    自顶向下
    对象 驱动 桩
    A Driver-A Stub-B
    B Driver-A Stub-C,D
    C Driver-A Stub-E,D
    D Driver-A Stub-E
    E Driver-A Stub-F,G,H
    F Driver-A Stub-G,H
    G Driver-A Stub-I,H
    H Driver-A Stub-I,J
    I Driver-A Stub-J
    J Driver-A

    单元测试用例设计(基本路径覆盖法)★ (面试)
    步骤:(所有的循环仅执行一次)
    1)画流程图
    2)根据流程图画出流图
    3)根据流图找出独立路径
    4)根据独立路径设计用例
    结点:表示一个或者多个无分支的语句
    边:表示处理流向
    分支:判定真假的语句
    区域:由结点和边构成的,域的个数等于独立路径的条数
    注:路径一定要从始点到终点
    复杂性计算 :V(G)=E-N+2
    V ( G ) =P+1
    E:边,
    N:结点数量,
    P:条件结点的数量,
    V最好不要大于10
    程序控制流图
    1顺序结构
    2if结构
    3while循环结构
    4until重复结构
    5Case分支结构(swith结构)

    单元测试执行
    单元测试执行的过程:
    单元计划—单元设计—单元测试用例脚本----单元测试执行
    1.搭建环境 引入googletest
    a)将googletest的include和lib两个文件夹复制到VS的项目文件夹下
    b)在Visual Studio中选择项目,属性,将include和lib导入
    c)引入googletest头文件
    #include “gtest/gtest.h”
    d)在main函数中初始化GTEST
    testing::InitGoogleTest(&argc,argv); 初始化google test
    RUN_ALL_TESTS(); 运行当前工程所有test
    2.加入被测代码
    将被测代码头文件引入
    3.新建测试代码
    新建一个文件,编写测试代码
    调用googletest宏
    TEST(测试套名字,测试用例名字)
    {
    ………
    }
    用断言函数(ASSERT、EXPECT)来判断预期结果与实际结果
    ASSERT:致命断言,若前一个执行失败,则不继续执行
    EXPECT:一般断言,前一个执行失败,继续执行
    系统测试中发现不了在单元测试中比较容易发现的问题,如内存泄露、算法缺陷
    补充:
    C语言中,用malloc申请内存,free释放内存
    C++中,用new申请内存,delete释放内存
    TDD(测试驱动开发)
    内存中的堆、栈
    编译器自动分配的内存是在栈中,栈会自动维护、清理
    手工分配的内存是在堆中,堆不会自动清理,需要手工释放,容易忽略造成内存泄露

    单元测试框架

    1. 框架是一组为重用而设计的方法,单元测试框架包括junit\cppunit\phpunit\c#unit\pyunit
    2. pyunit是 d:\py26\unittest.py
    3. 单元测试框架概念
      测试固件(test fixture)
      一组测试函数执行前或执行后要执行的语句。
      例如,void insertDB(chr sql[30])
      {
      //执行insert语句
      }
      若现在测试该函数,实际上每次测试之前都要建立数据库连接,测试完成之后都要断开断开数据库连接。
      建立连接和断开连接称之为测试固件。测试固件在框架如何体现:
      setUP(…) —表示测试函数执行之前要执行的语句,比如建立连接
      teardown(…) —表示测试函数执行之后要执行的语句,比如断开连接
      有没有测试固件,取决于被测函数在测试执行需要否

    测试用例
    主要以测试函数为主,什么是测试函数?就是对被测单元完成测试的函数,类似于原始的驱动单元。
    在框架中一般来说要继承单元测试框架的TestCase。

    测试套件
    装配测试用例的车间。
    在框架中一般来说使用TestSuite的addTest方法来进行装配,装配的就是测试用例,实际上装配的用例中的测试函数。

    测试运行器
    加载测试套件,对套件中装配的测试函数的运行。
    在框架中一般来说使用TestRunner中的run方法来进行运行TestSuite。
    4. pyunit框架中有:
    unittest.py
    测试固件 -----》TestCase类中有setUp和tearDown有这两个方法
    测试用例 -----》TestCase类,在该类中写测试函数的方法 ????
    测试套件 -----》通过TestSuite类中的addTest方法将测试用例中的测试函数加载
    测试运行器-----》通过TextTestRunner中的run方法将测试套件运行起来。
    5. 实例:
    见例子
    6. 使用pyunit单元测试步骤:
    1)import unittest
    2)继承unittest.TestCase构建自己的测试类
    3)在自己构建的测试类中,若对于测试函数有测试固件,则在setUp和tearDown中进行代码编写。否则跳到1)

    第十八章 集成测试
    1什么是集成测试
    依据概要设计说明书,对软件组成的所有函数模块和子模块进行的测试
    2目的:检查软件是否符合概要设计说明书,是否符合需求
    3关注重点
    全局变量 组合功能(集成后的问题)单元接口(1穿越模块的数据是否会丢失,即做输入输出的的形参定义是否正确2全局数据结构是否被异常修改)
    4集成测试环境
    集成后的功能:单个功能都没有问题,组合在一起时否有问题,单个功能之间是否相互影响
    进程:是一个程序在计算机上的一次执行活动,当运行了一个程序就启动了一个进程,进程是操作系统进行资源分配的单位
    线程:是进行中的一个片段
    集成测试没有联系,不存在集成,联系时全局变量,全局变量可能是内存的一片区域,也可能是同一份文件,数组,堆栈,字段,记录,变量,队列,集成也需要考虑到性能匹配问题,网络集成
    5集成测试的依据:LLD
    6集成测试的对象:接口
    7集成测试策略:大爆炸集成测试,自顶向下,自底向上,三明治集成,基于集成,分层集成,基于功能集成,基于消息集成,基于进度的集成,基于风险的集成
    1)大爆炸集成测试
    方法:对每个模块分别进行单元测试,在吧所有单元组装在一起测试
    优点:从未投入模块间,子系统间角度节省时间,减少桩和驱动的开发,节省成本,方法简单

    使用范围:小型项目,大型项目已经稳定,只有少量修改
    2)自顶向下集成测试

    深度优先:在系树的根优先,根—左—右
    广度优先:从上到下,从左到右,一层一层的
    注:调用没有加入过的都需要打桩
    优点:减少驱动的开发,测试充分(一个模块一个模块的加入)定位问题容易,效率有所下降,最容易让高层人物建立信心
    缺点:打桩开发成本较高,效率有所下降,下层容易测试不充分
    适用范围:上层稳定,下层相对不稳定的系统
    3)自底向上集成测试
    优点:桩少,定位问题容易,小测试成分,下层测试充分,问题容易暴漏
    缺点:驱动开发成本高,顶层测试不充分
    适用范围:下层稳定,上层变化较多
    4)三明治集成测试方法

    优点:桩和驱动都减少,测试灵活,问题好隔离,好定位
    缺点:测试中间层可能不充分
    适用范围:大项目
    注:采用哪种方法根据系统本身确定
    8集成测试用例可借用单元测试和系统测试用例设计技术进行用例设计
    9接口覆盖率:每个接口至少被覆盖一次的次数/系统中所有的接口
    10集成测试的层次:子系统间集成测试,模块内集成测试,子系统内集成测试,还要考虑模块间集成测试

    11集成测试过程:计划(输入HLD测试计划,输出IT计划)——设计(输出IT方案)——实施(输出IT用例,脚本——执行(输出集成测试报告,缺陷报告)

    第一阶段总结
    基础课程:测试基础、测试过程、测试方法、软件质量
    开发基础:SQL、C语言
    需求分析、需求管理(变更控制、需求跟踪)
    环境搭建
    配置管理、QC
    缺陷管理
    系统测试、集成测试、单元测试、python
    要做好测试还应该学习DB 网络知识 数据结构 算法 语言(C JAVA PYTHON)OS
    所有课程全部基于测试目的:尽量多、尽量的发现BUG,并预防BUG
    在做测试之前,先考虑可以套用哪些软件质量特性,再根据特性考虑测试点

    Test platform
    管理工具:QC ,RQM
    执行工具:QTP,LR ,WinRunner,BRAT
    数据库:SQL,Oracle,MySQL
    配置管理工具:SVN,CVS,VSS,CC,DB2
    服务器:Tomcat,IIS,JBOSS
    2. 系统测试流程:
    测试流程:需求阶段(产出测试计划):1)来自客户(用户需求)特点:用户自身根据自己的需要提出来,不考虑技术的实现能力,功能和具体细节的实现不2)来自研发(开发文档):特点,进一步明确客户的需求3)来自标准协议(手机网络协议:CSM,CDMA,WCDMA,TD_WCDMA,EDEE)
    需求分析(产出测试方案):分析测试项。罗列功能模块和功能点,产出测试项1)从质量特性的角度对测试项进行分析,包括6大特性,27个子特性2)从功能交互的角度对测试项进行分析,功能是否存在交合,交互过程中有什么影响3)按用户场景进行测试分析
    设计阶段(产出测试用例)设计方法:等价类、边界值、状态迁移图、因果图等
    执行阶段(产出测试报告)提交缺陷报告: 测试结束,研发人员根据提交的bug进行修改,之后release 1.1(新版本),进行回归测试(REG)。
    补充:
    敏捷开发模式 scrum 短小、快。 没有需求、不便于管理。
    User story 功能点。
    3. 与bug相关
    Bug的其他说法
    Defect、CR(change Request)(Moto使用这种说法、submit a CR)、issue。
    缺陷流程:
    缺陷管理工具:QC、Mantis(开源工具)、bugfree(开源)、bugzilla、DDTS(Moto)、DMS(索爱)。
    缺陷状态:New、open、fixed、reopen、assigned、reject、later(postpone)、closed、abandon、duplicate(很常见的一个状态,重复bug,原有的有效,引用时引用原来的bug)。摩托罗拉的缺陷管理系统DDTS
    Postpone原因:有争议的、项目进度紧,优先级低的bug、以后版本就没有这个功能了。
    Retest时可能是不同的测试人员做回归测试:testerA(原)、TesterB(新),测试完后需要测试经理verify
    验证完成后bug没问题tester是否可以直接closed?如果tester没有对bug进行测试,直接closed后可能导致bug遗漏。

    第二阶段项目笔记
    JXC项目
    扩展名为ASP的文件是微软开发的
    扩展名为JSP的文件是JAVA编写的
    一.建立项目JXC
    1把JXC项目方在d:
    2点JXC右键—共享和安全----WEB共享----编辑别名----执行包括脚本等权限都选上
    3删掉JXC布置:控制面板—管理工具—Internet—网站----默认网站----JXC—右键删除
    4给权限:d:\点上边的文件夹—JXC—data—右键属性----安全与共享----添加—everyone----检查名称----确定----点中everyone—权限都选上—确定
    5登录:控制面板—管理工具—Internet信息服务-----JXC—右边栏里找到index.asp右键—浏览
    6找WEB共享或者安全:工具—文件夹—查看—使用简单文件共享前面的对号去掉
    7客户端需要建TSVN,服务器需要建SVN(必须)/TSVN(可建,可不建)
    二.布置JXC
    1建立仓库:新建文件夹SVN—新建文件夹jxc52-niuxiaoqing和文件夹JXC—在文件夹jxc52-niuxiaoqing里分别新建文件夹trunk branches tags—在文件夹trunk里分别新建文件夹doc code tools—在文件夹doc里分别建立文件夹croup person—在文件person里分别新建文件夹niu wang guo
    2点SVN文件夹—jxc52-niuxiaoqing右键----create repository here
    3创建的文件结构放到仓库:SVN文件夹------tortoisvn----import
    4binary文件时二进制文件(可执行的文件)
    5.Dll动态链接库文件有什么作用
    6 1)开始—运行—CMD—目录—cd空格c:\program files\svbversion\bin(svnserve的路径)—回车
    2)svnserve.exe空格–(2个下划线)help-------回车
    3)svnserve.exe空格-d空格-r空格d:\svn\jxc52-niuxiaoqing----回车
    4)停掉服务ctrl+c
    5)上光标,又出现3)回车,启动服务

    三.配置SVN
    1、svn搭建的服务环境:svnserve方式

    2、修改文件:

    svnserve.conf //修改服务器配置文件
    anon-access = none //去除注释 匿名访问 不允许
    auth-access = write //去除注释 指定用户访问 允许

     password-db = passwd //去除注释 启用密码文件passwd
     authz-db = authz     //去除注释 启用用户文件authz
    

    passwd //增加用户并设置密码
    zhangsan = 1111 // 用户名 = 密码

    authz //控制仓库的访问权限
    [groups]
    组名 = 用户名(逗号分隔)

     [/] 代表仓库的根目录
     如果使用组:@组名 = rw
    
     [/trunk/doc/person/lisi] 控制仓库里具体目录的权限
    
     如果没有特别规定某个目录的权限,则使用根目录[\]权限
    

    3、启动服务
    1、进入svn安装目录:cd C:\Program Files\Subversion\bin

    四.访问SVN
    1 svn checkout
    输入要访问的电脑IP
    File:///仓库建立在之间的计算机上
    SVN://仓库建立在其他机器上,要通过网络方式访问
    Apache为HTTP协议请求提供服务
    Tomcat为JAVA的容器,为JAVA编译的文件解释
    IIS为ASP提供服务
    QC的前身叫testdirector
    9.0之前是mercury公司
    10.0之后是惠普公司
    支持的数据库SQL ,Oracle
    支持的应用服务器JBoss
    支持的WEB服务器JBoss,IIS

    进销存项目
    上传图片:数据库中保存的是图片名称,实体保存在jxc/upload下。access中没有存储图片的类型。

    【SqlServer数据库中binary(二进制)类型可以用来保存图片。】

    为什么改名? ------有可能重名,同一个windows目录下不能重名

    为什么是日期时间?------重名的可能性小

    在哪见过类似的实现方式?----QC生成日志,日志保存名称,年月日秒毫秒(1秒=1000毫秒)
    系统生成的文件,为了解决文件重名的问题,使用日期时间命名,做好精确到毫秒级。

    日志功能怎么测?
    保存4个内容:日志级别、日志的最大保存行数(边界值)、保存天数(边界值)、日志保存路径。

    QC非活动状态(选择域后项目下拉列表中没有这个项目了)才能导出(export)----没有人操作

    离点(10001):预期结果(有三种实现方式):保存一个新的日志、覆盖第一条、整个全删了从第一条开始写。

    保存天数(-1):不限制。
    保存路径:目录空间是否够,如果不限制保存时间,硬盘保存空间不够时怎么办?tester是否对硬盘空间

    可操作?

    安装文件后会有一个bin目录,存有大量二进制文件(编译后),扩展名为.exe,为可运行程序。

    路径写到上一级,不包括本级
    文字相同

    001 用户名 用户名和编号
    002 密码

    新密码 确认新密码合为一个需求 新密码处验证特殊符号等,确认新密码只验证是否与新密码相同。

    排序 翻页 打印 详细信息

    所在仓库 有读取数据库的代码

    测试需求:

    测试需求的目的:保证测试的完整性。
    将需求导入QC中,能得出什么东西??-------not covered 应该被用例覆盖但是还没被覆盖。
    细化需求
    每个需求项下边的description:描述该项需求具体的 测试内容,提供测试思路。
    例如:
    产品图片:
    1.浏览功能
    支持的图片格式 jpg、gif(打开文件窗口对该文件类型进行过滤,默认显示过滤类型为jpg、gif)(不区分扩展名的大小写)。
    浏览本地路径、网络路径。
    2.上传功能
    被选文件路径是否合法(选择U盘中的文件,上传前拔掉U盘;存在本地,上传前删除);
    文件的大小限制(0,1M】
    被上传文件是否被独占打开(需要编写程序来独占打开该图片)。
    图片被上传到服务器的upload目录中,而且被修改图片名称(防止该目录下出现重名文件),名称格式:YYYYMMDDHHMMSS。
    上传成功后相关图片信息(图片的新名称)被保存到buy(photo字段)和produit(photo字段)。
    3.预览功能
    预览模式:平铺、拉伸、居中【一般预览功能就这几种模式】
    使用“居中”的预览模式。

    Buy表中 shulian(数量)保存时采用四舍五入。(有bug)。如何提bug???
    输多个小数点出错 编辑框输入的内容按字符(字符串)处理,放到数据库前需要进行字符转换,多个小数点时不能转换成float型,导致语法错误。
    Bug:
    页面允许输入浮点类型,数据库中为整形。
    页面控制的类型和数据库的类型不一致。
    进销存项目总结
    测试需求分析
    工作思路参考QC需求工作流:

    1、定义测试范围

        依据:ISO9126质量模型
        确定测试范围:被测质量特性
    

    2、建立需求项
    参考需求(SRS),明确具体要测试的需求项(测试点、需求点、功能点。。。)
    树形结构,考虑分析的“粒度”(参考QC)

        (1)、粗:文件夹级(只分解到模块或者页面层次)
        (2)、中:用例级  (只分解到页面中的具体控件,如“产品名称”、“入库数量”,意味这最底层需求可以直接转换为测试用例)
        (3)、细:步骤级  (分解到用例的操作步骤 Step)
        (4)、默认使用级别:用例级
    
         考虑后期用例执行,分解出一些特殊的需求(以后会专门对应某些测试类型)
    
        (1)、页面同名:(作为模板),只考虑该页面中所有控件都输入最正确的值
                       (控件同名的需求,目的从有效、无效两方面反复验证该控件输入的合法性)
        (2)、页面同名 Page: 以后作为“界面测试”使用
        (3)、模块同名:(调用该模块内所有页面同名的用例)作为预测试使用,证明版本的基本功能是否正确
    

    3、细化需求项
    描述每个需求项的详细内容

    详细内容:
    1、页面:(星号)是否允许为空
    2、数据库: 输入类型、长度(边界值)
    3、设计:跑到(所有)的路径----精简测试用例的个数

    如:入库管理模块–新产品入库页面–入库数量控件:
    1、页面:入库输入量不能为空

    2、数据库:
    数字类型(整型)
    边界:-231 ~ 231-1
    31:整型类型占用4个字节(4B),每个字节有8位(8bit),每位有两个取值(0、1),考虑符号(正、负)占用1位
    2:代表每位(bit)有两个取值(0、1)
    31:除去符号位,还剩31位

    3、代码实现:
    1)、当键盘抬起的时候,重置输入
    测试思路:(1)、抬起按键,非法字符的输入被重置
    (2)、绕过该事件,按下字母键不抬起进行鼠标焦点切换
    2)、当粘贴之前,重置输入
    考虑粘贴功能的实现(鼠标右键进行粘贴,快捷键粘贴 Crtl+V)
    3)、小数点个数最多1个(考虑边界值0,1,2个)

     具体分析思路:
    

    4.需要进行评审
    借助Rose的活动图统一思路

    4、需求覆盖率分析
    借助工具----QC
    将写好的需求导入到QC中的需求模块
    切换到需求的第四种视图方式:Coverage Analysis (需求覆盖率分析),看需求的状态(Not Covered 未覆盖状态),得知应该被覆盖到的需求的数量(只统计最底层需求的个数)(被测对象的测试规模),进而预测试出用例的数量

    当粘贴之前
    课前复习:
    需求名称为必填项,有空行不能导到QC中。
    把相同需求分类
    从需求详细描述从哪几方面去找?(1)页面角度 获取需求(星号—是否允许为空);(2)数据库 (长度----边界值、类型);(3)代码 实现方式----【目的:精简用例的个数,路径全部覆盖】
    代码实现讲解:
    onKeyUp="value=value.replace(/[^\d.]/g,’’)
    Onkeyup:当键盘键抬起的时候
    Replace:重新设置
    (/[^\d.]/g,’’):正则表达式 d----代表数字(0~9) .------代表小数点
    整个句子解释:
    当键盘抬起的时候,判断按键是否属于数组或者小数点,如果不属于这些键,则重置该输入(取消该输入)。

    这样只要设计2个用例就可(1)输入字母;(2)输入1.5。
    如果没有的代码,只按照等价类边界值的思想设计用例,需要考虑很多情况(此处略)。
    此处总结如下:

    onbeforepaste=“clipboardData.setData(‘text’,clipboardData.getData(‘text’).replace(/[^\d.]/g,’’))” value=“1”>
    onbeforepaste:当粘贴之前
    入库数量测试(设计):
    1.输出-------入库输入量不能为空;
    2.代码实现:
    (1)当键盘抬起的时候,重置输入;
    测试思路:绕过该事件,按下字母键不抬起鼠标焦点切换
    (2)当粘贴之前,重置输入
    考虑粘贴功能的实现(鼠标右键进行粘贴,快捷键粘贴Ctrl+V)
    3.小数点个数最多1个
    测试需求分析:
    如何做??1.工作思路参考QC需求工作流
    2.定义测试范围; 依据:ISO9126质量特性确定测试范围:被测质量特性
    3.建立需求项------参考需求(SRS)明确具体要测试的需求项(又叫测试点、需求点、功能点….)树型结构,考虑分析的“粒度”
    【粗—文件夹级(只分解到模块或者页面层次);
    中------用例级(只分解到页面中的具体控件,如“产品名称”、“入库数量”)
    细-----步骤级】
    考虑后期用例执行,分解出一些特殊的需求(以后)
    1.页面同名
    2.页面同名page
    3.模块同名
    4.细化需求项
    5.需求覆盖率分析

    判定表
    解决:多个输入的组合问题

    方法1:利用数据库的查询命令—笛卡尔积(全排列组合)
    计算最终的组合数量:每个表记录数的乘积
    如:A(a1,a2)
    B(b1,b2,b3,b4)
    C(c1,c2,c3)
    Select * from A、B、C

    方法2:判定表多个条件,每个条件2个取值

    3.1.读书选择
    1、如果觉得不疲倦,并且对书中的内容不感兴趣,同时书中的内容让你不糊涂,跳到下一章去读
    2、如果觉得不疲倦,并且对书中的内容不感兴趣,同时书中的内容让你糊涂, 跳到下一章去读
    3、如果觉得不疲倦,并且对书中的内容感兴趣, 同时书中的内容让你不糊涂,继续读下去
    4、如果觉得不疲倦,并且对中书的内容感兴趣, 同时书中的内容让你糊涂, 回到本章重读
    5、如果觉得疲倦, 并且对书中的内容不感兴趣,同时书中的内容让你不糊涂,停止阅读,请休息
    6、如果觉得疲倦, 并且对书中的内容不感兴趣,同时书中的内容让你糊涂, 请停止阅读,休息
    7、如果觉得疲倦, 并且对书中的内容感兴趣, 同时书中的内容让你不糊涂,继续读下去
    8、如果觉得疲倦, 并且对书中的内容感兴趣, 同时书中的内容让你糊涂, 回到本章重读

    提炼需求:多个条件的组合生成不同的结果
    1、需求分析:
    条件:是否疲倦(是、否)、是否感兴趣(是、否)、是否糊涂(是、否)
    结果:跳到下一章、继续读、本章重读
    2、绘制判定表
    1、分别填入条件和结果,确定表格的“行数”
    2、计算条件组合的数量,规划表格的“列数”
    3、将条件进行排列组合(全排列–笛卡尔积),利用二进制原理(0代表否,1代表是)。
    4、根据每种组合方式(每列),推出其对应的结果

    	1	2	3	4	5	6	7	8
    

    条件 是否疲倦 0 0 0 0 1 1 1 1
    是否感兴趣 0 0 1 1 0 0 1 1
    是否糊涂 0 1 0 1 0 1 0 1
    结果 跳到下一章 X X
    继续读 X X
    本章重读 X X
    休息 X X
    注:虽然1、2的结果是一样的,但是不能使用等价类的思想将其合并,原因:处理路径不同。(时间紧张可以合并)。

    3、编写测试用例
    原则1:判定表中的每一列生成一个测试用例,多个测试用例
    原则2:判定表中的每一列生成一个测试用例的步骤,一个测试用例

    我们项目使用:原则2
    两个原则测试的充分程度(覆盖率)相同;
    第二种进行需求跟踪更简单易操作;

    原则1:----8个测试用例
    用例编号 用例标题 用例输入 操作步骤 预期结果
    ST-001 不疲倦+没兴趣+不糊涂 是否疲倦: 否
    是否感兴趣:否
    是否糊涂: 否 1、启动系统
    2、输入以上内容
    3、点击“提交”按钮 跳到下一章
    ST-002 不疲倦+没兴趣+糊涂 是否疲倦: 否
    是否感兴趣:否
    是否糊涂: 是 1、启动系统
    2、输入以上内容
    3、点击“提交”按钮 跳到下一章
    ST-008 。。。。。。。。 。。。。。。。。 。。。。。。。。 。。。。。。。。

    原则2:-----1个测试用例,8个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果
    ST-001 读书选择 启动系统 。。。。。。。。 。。。。。。。。 。。。。。。。。
    准备测试 。。。。。。。。 。。。。。。。。 。。。。。。。。
    Step 1不疲倦+没兴趣+不糊涂 是否疲倦: 否
    是否感兴趣:否
    是否糊涂: 否 1、启动系统
    2、输入以上内容
    3、点击“提交”按钮 跳到下一章
    Step 2不疲倦+没兴趣+糊涂 是否疲倦: 否
    是否感兴趣:否
    是否糊涂: 是 1、启动系统
    2、输入以上内容
    3、点击“提交”按钮 跳到下一章
    Step 8 。。。。。。。。 。。。。。。。。
    3.2.Counter

    1、测试需求分析
    条件:是否统计代码行、是否统计空行、是否统计注释行、是否统计总行
    结果:统计代码行、统计空行、统计注释行、统计总行

    2、绘制判定表
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    条件 是否统计代码行 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
    是否统计空行 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
    是否统计注释行 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
    是否统计总行 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
    结果 统计代码行 X X X X X X X X
    统计空行 X X X X X X X X
    统计注释行 X X X X X X X X
    统计总行 X X X X X X X X

    3、生成测试用例
    每一列生成一个测试用例,一共16种组合方式
    原则1:----16个用例
    用例编号 用例编号 用例输入 操作步骤 预期结果 编写人 编写时间
    ST-001 1.
    ST-002 1.
    ST-003
    ST-004
    ST-005

    原则2:----1个测试用例,16个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果 编写人 编写时间
    ST-001 Counter统计

    3.3:word中的判定表举例
    Word中的判定表举例:
    (1)字体和字号

    输入太多,选择有代表的处理(等价类—减少输入个数)。
    字体:中文、英文(英文又可以分为2类:带横线(serif—如times new roman)、不带横线(sens serif----arial Unicode ))
    (2)

    组合:4个条件,每个条件2个值。
    (3)

    这个不能用判定表,因为不能同时选中“左对齐”和“右对齐”,不满足判定表使用条件。

    3.4.合并判定表
    前提:时间紧张,成本过高;
    原则:结果相同,条件有一个不同(貌似该条件没有作用);
    风险:被合并的条件可能走不同的“路径”,合并可能会造成漏测。
    对练习1(读书选择)中的判定表进行合并:

    	1	2	3	4	5	6	7	8
    

    条件 是否疲倦 0 0 0 0 1 1 1 1
    是否感兴趣 0 0 1 1 0 0 1 1
    是否糊涂 0 1 0 1 0 1 0 1
    结果 跳到下一章 X X
    继续读 X X
    本章重读 X X
    休息 X X

    判定表合并:

    	1	2	3	4
    	1、2	3、7	4、8	5、6
    

    条件 是否疲倦 0 ---- ---- 1
    是否感兴趣 0 1 1 0
    是否糊涂 — 0 1 —
    结果 跳到下一章 X
    继续读 X
    本章重读 X
    休息 X

    前提:
    合并:时间紧张,成本过高
    原则:结果相同,条件有一个不同(“貌似”该条件没有作用)
    风险:被合并的条件可能走不同的“路径”,合并会造出漏测

    3.4.密码修改
    若需修改密码,系统验证旧密码正确,两个新密码相同,则更新密码,旧密码即失效,其他修改项也生效,并提示“用户信息修改成功”; 若旧密码不正确,则提示“用户密码错”,系统将不修改个人信息;若两个新密码不同,则提示“新密码与验证新密码不同”,系统将不修改个人信息。
    若只修改密码外其他信息,则不需输入两个新密码,系统只验证旧密码正确,就成功更改个人信息,并提示“用户信息修改成功”;如果系统验证旧密码输入不正确,则提示“用户密码错”。
    流程图如下

    答案:
    (1)需求分析
    条件:只修改密码外其他信息(是/否)、旧密码(正确/错误)、新密码和验证新密码(相同/不相同)
    结果:提示“用户信息修改成功”、提示“用户密码错”、提示“新密码与验证新密码不同”
    (2)绘制判定表
    1 2 3 4 5 6 7 8
    条件 只修改密码外其他信息 0 0 0 0 1 1 1 1
    旧密码 0 0 1 1 0 0 1 1
    新密码和验证新密码 0 1 0 1 0 1 0 1
    结果 提示“用户信息修改成功” x x x
    提示“用户密码错” x x x
    提示“新密码与验证新密码不同” x
    说明 无效 无效
    (3)编写测试用例
    原则2:----1个测试用例,8个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果
    ST-001 修改信息 启动系统 无 双击 系统正常启动
    Step1:修改密码+旧密码错误+新密码与验证密码一致 是否只修改密码外其他信息:否;
    旧密码是否正确:否;
    新密码和验证新密码是否一致:否 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    无任何提示信息
    Step2:修改密码+旧密码错误+新密码与验证密码一致 是否只修改密码外其他信息:否;
    旧密码是否正确:否;
    新密码和验证新密码是否一致:是 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“用户密码错”,个人信息未修改
    Step3:修改密码+旧密码正确+新密码与验证密码不一致 是否只修改密码外其他信息:否;
    旧密码是否正确:是;
    新密码和验证新密码是否一致:否 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“新密码与验证新密码不同”,个人信息未修改
    Step4:修改密码+旧密码正确+新密码与验证密码一致 是否只修改密码外其他信息:否;
    旧密码是否正确:是;
    新密码和验证新密码是否一致:是 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“用户信息修改成功”,旧密码生效,其他个人信息修改
    Step5:只修改密码外信息+旧密码错误 是否只修改密码外其他信息:是;
    旧密码是否正确:否;
    新密码和验证新密码是否一致:否 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“用户密码错”,个人信息未修改
    Step7:只修改密码外信息+旧密码正确 是否只修改密码外其他信息:是;
    旧密码是否正确:是;
    新密码和验证新密码是否一致:否 1.启动系统;
    2.输入以上修改信息;
    3.点击“提交”按钮
    提示“用户信息修改成功”,其他个人信息修改

    3.5.进销存
    进销存中的下列选框可以使用判定表的方法设计用例:

    (1)需求分析
    条件: 仓库(所有仓库/具体仓库)(0表示所有仓库,1表示具体仓库)【“具体”只测试一个即可,处理方式相同,等价类思想】
    类别(包括大类和小类,有三种组合,分别为所有/所有、具体/所有、具体/具体)(分别使用0、1、2表示)
    关键字(填/不填)(0表示不填,1表示填)
    结果:所有仓库所有库存、所有仓库具体库存、具体仓库所有库存、具体仓库具体库存
    (2)绘制判定表
    1 2 3 4 5 6 7 8 9 10 11 12
    条件 仓库 0 0 0 0 0 0 1 1 1 1 1 1
    类别 0 0 1 1 2 2 0 0 1 1 2 2
    关键字 0 1 0 1 0 1 0 1 0 1 0 1
    结果 所有仓库所有库存 X X
    所有仓库具体库存 X X X X
    具体仓库所有库存 X X
    具体仓库具体库存 X X X X
    (3)编写测试用例
    使用原则2:----1个测试用例,12个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果
    ST-001 库存查询信息组合查询 进入库存查询页面。 无 1.打开进销存网页;2.点击库存管理模块中的库存查询页面。 界面显示库存查询页面。
    Step1:所有仓库+所有大类+所有小类+不填关键字 仓库:所有仓库;
    类别:所有大类和所有小类;
    关键字:不填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择所有大类和所有小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的所有库存信息
    Step2:所有仓库+所有大类+所有小类+填关键字 仓库:所有仓库;
    类别:所有大类和所有小类;
    关键字:填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择所有大类和所有小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的所有库存信息
    Step3:所有仓库+具体大类+所有小类+不填关键字 仓库:所有仓库;
    类别:具体大类和所有小类;
    关键字:不填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择具体某一个大类和所有小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的具体库存信息
    Step4:所有仓库+具体大类+所有小类+填关键字 仓库:所有仓库;
    类别:具体大类和所有小类;
    关键字:填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择具体某一个大类和所有小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的具体库存信息
    Step5:所有仓库+具体大类+具体小类+不填关键字 仓库:所有仓库;
    类别:具体大类和具体小类;
    关键字:不填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择具体某一个大类和具体某一个小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的具体库存信息
    Step6:所有仓库+具体大类+具体小类+填关键字 仓库:所有仓库;
    类别:具体大类和具体小类;
    关键字:填 1.在页面右上角搜索处选择所有仓库;
    2.在页面右上角搜索处选择具体某一个大类和具体某一个小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示所有仓库的具体库存信息
    Step7:具体仓库+所有大类+所有小类+不填关键字 仓库:具体仓库;
    类别:所有大类和所有小类;
    关键字:不填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择所有大类和所有小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的所有库存信息
    Step8:具体仓库+所有大类+所有小类+填关键字 仓库:具体仓库;
    类别:所有大类和所有小类;
    关键字:填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择所有大类和所有小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的所有库存信息
    Step9:具体仓库+具体大类+所有小类+不填关键字 仓库:具体仓库;
    类别:具体大类和所有小类;
    关键字:不填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择具体某一个大类和所有小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的具体库存信息
    Step10:具体仓库+具体大类+所有小类+填关键字 仓库:具体仓库;
    类别:具体大类和所有小类;
    关键字:填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择具体某一个大类和所有小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的具体库存信息
    Step11:具体仓库+具体大类+具体小类+不填关键字 仓库:具体仓库;
    类别:具体大类和具体小类;
    关键字:不填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择具体某一个大类和具体某一个小类;
    3.不填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的具体库存信息
    Step12:具体仓库+具体大类+具体小类+填关键字 仓库:具体仓库;
    类别:具体大类和具体小类;
    关键字:填 1.在页面右上角搜索处选择具体某一个仓库;
    2.在页面右上角搜索处选择具体某一个大类和具体某一个小类;
    3.填写关键字;
    4.点击“查询”按钮。 页面中显示具体仓库的具体库存信息

    3.6.总结
    1、什么时候用:
    (1)有多个输入需要组合的时候,采用判定表进行用例设计;
    2、怎么用:
    (2)使用判定表进行用例设计,有以下3个步骤:
    a)需求分析,通过对多个条件的组合生成不同的结果进行分析,得出条件和结果。
    b)绘制判定表。
    i.分别填入条件和结果,确定表格的“行数”;
    ii.计算条件的组合数量,规划表格的“列数”;
    iii.将条件进行排列组合,利用二进制原理;
    iv.根据每种组合方式,推出其对应的结果。
    c)编写测试用例
    有2种方式:判定表中的每一列生成一个测试用例,最终生成多个测试用例;
    判定表中的每一列生成一个测试用例的步骤,最终生成一个测试用例。
    3判定表的优点和缺点
    (3)优点:组合充分,没有遗漏;
    缺点:当条件数多的时候(超过5个)用例数量多,成本高。

    因果图

    条件和结果之间的关系:

    恒等:条件成立,结果成立
    非: 条件成立,结果不成立
    或: 只要有一个条件成立,结果成立
    与: 必须所有条件都成立,结果成立

    条件之间的关系:

    E:不能同时为“真”
    I:不能同时为“假”
    O:有且仅有一个为“真”

    第一个举例:呼吸,用鼻子和嘴(异)
    因果图作用:1、条件和结果之间的关系:分析业务逻辑(类似流程图的作用)
    2、条件之间的关系:去除判定表中的无效的列
    使用viso绘制因果图:
    操作步骤:
    (1):在viso中选择基本流程图,
    (2)按照等价(处理方式相同)条件,添加中间节点(临时结果)。如:A、B处理相同(等价类),做中间节点(如A or B)。
    (3)分析条件和结果之间的关系(恒等、与、或、非)。
    如果不是一次退出结果,或者连线较多,可以增加中间节点。
    (4)分析条件之间的关系(E、I、O)。

    4.1.字母判定
    第一列字符必须是A或B,第二列字符必须是一个数字(每列只能输入一个字符),在此情况下(只有这个时候才)进行文件的修改,但如果第一列字符不正确,则给出信息L;如果第二列字符不是数字,则给出信息M。
    不能使用等价类【不是单一条件】

    1、测试需求分析
    条件:第一列是A、第一列是B、第二列是数字
    结果:修改文件、 提示L、 提示M

    2、绘制因果图

    3、绘制判定表
    假设:当判断完是否是AorB条件后,如果取“否”,则退出系统运行。【实际工作中不允许假设】。

    利用因果图条件之间的关系,去除无效列

    	1	2	3	4	5	6	7	8
    

    条件 第一列是A 0 0 0 0 1 1 1 1
    第一列是B 0 0 1 1 0 0 1 1
    第二列是数字 0 1 0 1 0 1 0 1
    结果 修改文件 X X
    提示L X X
    提示M X X
    实际输入 CC T7 B@ B5 AF A6 无效 无效

    第一种条件是提示L还是提示L、M,在公司中要找开发人员询问,不能主观猜测。

    编写测试用例
    原则2:----1个测试用例,8个步骤
    用例编号 用例标题 步骤名称 步骤输入 操作描述 预期结果 编写人 编写时间
    ST-001 修改文件

    4.2.自动售货机
    有一个处理单价为5角钱的饮料的自动售货机软件测试用例的设计。其规格说明如下:若投入5角钱或1元钱的硬币,押下〖橙汁〗或〖啤酒〗的按钮,则相应的饮料就送出来。若售货机没有零钱找,则一个显示〖零钱找完〗的红灯亮,这时在投入1元硬币并押下按钮后,饮料不送出来而且1元硬币也退出来;若有零钱找,则显示〖零钱找完〗的红灯灭,在送出饮料的同时退还5角硬币。

    1、测试需求分析:
    条件:有零钱、投1元、投5角、选啤酒、选橙汁
    结果:红灯亮、退1元、找5角、出啤酒、出橙汁

    2、绘制因果图

    3、绘制判定表

    红色列为无效组合

    4、生成测试用例
    经办人不能为空,程序中通过管理员ID判断当前登陆用户是否为管理员,如果在数据库login表中删除再添加一个管理员后id变了,导致查询错误。
    Admin登陆,下拉列表中显示所有系统的用户,非admin登陆,下拉列表中只显示当前登陆用户。
    关于下拉列表的问题:

    关于单元测试逻辑覆盖率:
    在只有一个条件时,路径覆盖率为100%时,条件覆盖率也是100%。
    如果条件不是布尔类型,可以使用数据库中的多表关联,计算笛卡尔积。
    如:
    A表(a1、a2)
    B表(b1、b2、b3、b4)
    C表(c1、c2、c3)
    使用select * from A、B、C;查询。

    状态迁移
    5.1.飞机售票系统
    1、客户向航空公司打电话预定机票—>此时机票信息处于“完成”状态
    2、顾客支付了机票费用后—>机票信息就变为“已支付”状态
    3、旅行当天到达机场后,拿到机票后—>机票信息就变为“已出票”状态
    4、登机检票后—>机票信息就变为“已使用”状态
    5、在登上飞机之前任何时间可以取消自己的订票信息,如果已经支付了机票的费用,则还可以得到退款,取消后—>订票信息处于“已被取消”状态

    1、测试需求分析
    状态:完成、已支付、已出票、已使用、已被取消

    2、绘制状态迁移图
    使用rose画状态图:
    方法:
    (1)右键,新建状态图New -----stagechar Diagram。

    特点:每个状态只出现一次

    3、生成用例 — 路径覆盖
    绘制状态迁移树-----每个树枝生成一个测试用例

    特点:
    1、每个状态可以出现多次
    2、箭头方向统一向“右”延伸
    3、状态转换如果出现循环,该路径只遍历一次
    4、编写测试用例
    用例编号 标题 步骤名称 步骤描述 预期结果
    STC-001 售票流程 启动系统
    准备测试数据和环境
    Step 1 完成-取消 1、客户向航空公司打电话预定机票
    2、取消该机票 1、此时机票信息处于“完成”状态
    2、订票信息处于“已被取消”状态
    Step 2 完成-支付-取消 。。。。。 。。。。。
    Step 3 。。。。。 。。。。。
    Step 4 。。。。。 。。。。。
    那些软件适合状态迁移
    淘宝买东西、QC需求、配置管理状态(normal、modified.;……)、缺陷状态(new、open……)
    QC需求状态(只考虑一次运行)

    5.2.缺陷跟踪

    流程分析
    使用ROSE中的活动图进行分析

    状态:吃饭中(有一个延续,使用ing)。
    活动:吃饭
    方法:
    (1)右键,新建状态图New -----activity Diagram。

    6.1.处理流程
    在某嵌入式系统中,将待发送的数据打包成符合CAN协议的帧格式后,便可写入发送缓站区,并自动发送。该发送子程序的流程为:
    1、首先进入发送子程序
    2、系统判断是否有空闲发送缓冲区,如果没有则返回启动发送失败消息。
    3、如果有空闲缓冲区,将数据包写入空闲发送缓冲区
    4、系统判断是否写入成功,如果不成功则返回启动发送失败消息
    5、如果写入成功,则启动发送命令
    6、返回启动发送成功消息
    1、绘制流程(活动)图

    2、生成测试用例------ 路径覆盖
    1、(A) (B) (C) (D) (E) (F) 基本流
    2、(A) (B) (G) 备选流
    3、(A) (B) (C) (D) (G) 备选流

    6.2.系统登录

    C/S程序。
    先判断是否为空,再去判断是否合法,涉及到效率问题。
    把判断时间短的放前边。

    用例:6个(路径)
    单个测试用户名考虑:空、最大值、特殊符号、匹配、不匹配
    6.3.字母判断

    用例:
    基本流: (1) (2) (3)
    (1A) (2) (3) 6列
    (1B) (2) (3) 4列
    备选流1:(1) (4) 1、2列
    备选流2:(1) (2) (5)
    (1A) (2) (5) 5列
    (1B) (2) (5) 3列

    	1	2	3	4	5	6	7	8
    

    条件 第一列是A 0 0 0 0 1 1 1 1
    第一列是B 0 0 1 1 0 0 1 1
    第二列是数字 0 1 0 1 0 1 0 1
    结果 修改文件 X X
    提示L X X
    提示M X X
    实际输入 CC T7 B@ B5 AF A6 无效 无效

    合并判定表
    1 2 3 4 5
    1、2 3 4 5 6
    条件 第一列是A 0 0 0 1 1
    第一列是B 0 1 1 0 0
    第二列是数字 ---- 0 1 0 1
    结果 修改文件 X X
    提示L X
    提示M X X
    实际输入 CC B@ B5 AF A6
    使用流程图和判定表
    都可以,使用流程图前提要保证流程图正确
    6.4.组合查询
    库存管理–库存查询代码实现:
    //
    nowkeyword–输入在查询关键字
    nowku-------输入在仓库名对应在id
    nowbigclass—输入的大类对应的id
    nowsmallclass—输入的小类对应的id
    &—+加在之前sql语句后边

    语句覆盖100%:1个用例;
    分支覆盖100%:2个用例;
    路径覆盖100%:16个用例。
    可以使用独立路径(每个路径只走一次):A-----、AB------、AC------、AD------、AE------(非法用例,不选大类不能选小类,使用ADE)。可以再找一个全选ABCDE。共6个用例。

    用例:
    1、 (A) (F) 独立路径
    2、 (A) (B) (F) 独立路径
    3、 (A) (C) (F) 独立路径
    4、 (A) (D) (F) 独立路径
    5、 (A) (D) (E) (F) 独立路径
    6、 (A) (B) (C) (D) (E) (F) 补充
    补充1关于where中1=1的问题:

    两个查询是一样的。为什么要加上1=1呢??因为每个select查询语句调教中只能加一个where,可以加多个and;如果在这句中不加1=1,之后的sql语句需要判断之前是否加过where,再做处理,影响查询效率。
    查询效率高。
    如果查询的是两个表,两个表查询就不用写了,两个表关联时就把where语句占用了。
    补充2取仓库ID,而不是仓库名称:
    代码实现:

    下拉列表由2部分组成,分别为value和库名。

    课前复习
    单个输入------- 等价类边界值
    多条件组合------判定表(全排列组合,组合个数2n(n代表条件个数)条件是布尔类型,如果不是布尔类型,参考数据库笛卡尔积,条件取值个数相乘)。
    ------因果图(帮助描述中间处理过程,去除判定表中的无效组合,3种约束:E、I、O)
    ------正交试验(两两组合)
    每个条件取值不规范,使用allpairs工具。
    处理流程问题------状态迁移(强调状态属性,用状态来描述流程)
    -----流程分析(活动图活流程图)
    生成用例原则:单元测试的路径覆盖。路径太多可以取独立路径或分支覆盖。

    正交试验
    7.1.环境搭建
    假设一个WEB站点,该站点有大量的服务器和操作系统,并且有许多具有各种插件的浏览器浏览:

    环境配置 Web浏览器 浏览器插件 操作系统 服务器
    配置选项 Netscape RealPlayer WinXP IIS
    IE Flash Win2000 Tomcat
    Firefox PDF Reader Win2003 Weblogic

    1、测试需求分析,找出条件和取值
    条件:4个
    取值:每个条件有3个取值

    2、选择正交表: L9_3_4
    9:代表最终生成用例的个数
    4:因素(因数),代表条件的个数
    3:水平,代表每个条件取值的个数

    3、生成用例
    因素 Web浏览器 浏览器插件 操作系统 服务器
    实验1 Netscape RealPlayer WinXP IIS
    实验2 Netscape Flash Win2000 Tomcat
    实验3 Netscape PDF Reader Win2003 Weblogic
    实验4 IE RealPlayer Win2000 Weblogic
    实验5 IE Flash Win2003 IIS
    实验6 IE PDF Reader WinXP Tomcat
    实验7 Firefox RealPlayer Win2003 Tomcat
    实验8 Firefox Flash WinXP Weblogic
    实验9 Firefox PDF Reader Win2000 IIS

    正交表的特点:
        1、任意一列,每个取值出现的次数一致(均匀)(本题出现3次)
        2、任意两列,任何两个值得组合出现的次数一致(均匀)(本题出现1次)
        3、任意一列,该列的每个值都和其他列的所有值成对组合过(均匀)(本题组合1次)
           正交表:充分的两两组合(9个用例)
           判定表:全排列组合(两两组合、三三、四四。。。。组合)(81个用例)
           
    使用原则:根据经验,如果充分的两两组合不出问题,那么多次组合出问题的可能性很小,基于成本、时间等因素,可以考虑正交试验方法生成测试用例
    

    7.2.Counter

    因素 代码行 注释行 空行 总行
    实验1 选 选 选 选
    实验2 选 选 选 不选
    实验3 选 不选 不选 选
    实验4 选 不选 不选 不选
    实验5 不选 选 不选 选
    实验6 不选 选 不选 不选
    实验7 不选 不选 选 选
    实验8 不选 不选 选 不选
    补充 不选 不选 不选 选
    补充 不选 不选 不选 不选

    7.3.组合
    题目:
    土壤的酸碱度:酸性、碱性、中性
    土壤的潮湿度:潮湿、干燥
    土壤的温度: 高温、低温
    提供标准正交表:
    因素 A B C
    实验1 0 0 0
    实验2 0 1 1
    实验3 1 0 1
    实验4 1 1 0

    答案:
    方法1)、正交试验方法:
    正交表:L4_2_3
    土壤的酸碱度:0(酸性)、1(碱性+中性)
    土壤的潮湿度:0(潮湿)、1(干燥)
    土壤的温度: 0(高温)、1(低温)

    因素 酸碱度 潮湿度 温度
    实验1 酸性 潮湿 高温
    实验2 酸性 干燥 低温
    实验3 碱性+中性 潮湿 低温
    实验4 碱性+中性 干燥 高温

    拆分正交表
    

    因素 酸碱度 潮湿度 温度
    实验1 酸性 潮湿 高温
    实验2 酸性 干燥 低温
    实验3 碱性 潮湿 低温
    实验4 中性 潮湿 低温
    实验5 碱性 干燥 高温
    实验6 中性 干燥 高温

    方法2)、判定表方法:
    绘制判定表
    1 2 3 4 5 6 7 8
    条件 酸碱度 0 0 0 0 1 1 1 1
    潮湿度 0 0 1 1 0 0 1 1
    温度 0 1 0 1 0 1 0 1

    带入判定表
    1 2 3 4 5 6 7 8
    条件 酸碱度 酸性 酸性 酸性 酸性 碱性+中性 碱性+中性 碱性+中性 碱性+中性
    潮湿度 潮湿 潮湿 干燥 干燥 潮湿 潮湿 干燥 干燥
    温度 高温 低温 高温 低温 高温 低温 高温 低温

    拆分判定表
    1 2 3 4 5 6 7 8 9 10 11 12
    条件 酸碱度 酸性 酸性 酸性 酸性 碱性 中性 碱性 中性 碱性 中性 碱性 中性
    潮湿度 潮湿 潮湿 干燥 干燥 潮湿 潮湿 潮湿 潮湿 干燥 干燥 干燥 干燥
    温度 高温 低温 高温 低温 高温 高温 低温 低温 高温 高温 低温 低温

    时间不充裕选择正交实验法,否则选择判定法(充分)

    方法3)、Allpairs工具实现
    使用allpairs工具:
    Cmd-----进入allpairs目录下 cd C:\Documents and Settings\51testing\桌面\allpairs----
    新建空白excl文件,将条件复制到excl中,

    TEST CASES
    case 土壤酸碱度 土壤潮湿度 土壤温度
    1 酸性 潮湿 高温
    2 酸性 干燥 低温
    3 碱性 潮湿 低温
    4 碱性 干燥 高温
    5 中性 潮湿 高温
    6 中性 干燥 低温

    7.4.环境搭建
    假设一个WEB站点,该站点有大量的服务器和操作系统,并且有许多具有各种插件的浏览器浏览:

    环境配置 Web浏览器 浏览器插件 操作系统 服务器
    配置选项 IE RealPlayer WinXP IIS
    Firefox Flash Win2000 Tomcat
    PDF Reader Win2003 Weblogic
    Baidu Win 7
    XunLei

    练习:进销存

    条件:
    仓库:所有、具体
    大类和小类:所有-所有、具体-所有、具体-具体
    关键词:不填、货号(模糊)、产品名称(模糊)

    其他
    输入域
    寻找输入中的特殊值
    如:注册页面输入用户名:hujintao、admin、administrator…….
    如:结构化输入(相互制约)要考虑组合:日期(年月日)、国家—城市,进销存权限复选控制(库存查询、删除)
    【上下级关系的选项】
    员工权限
    Bug:不选库存查询可以选择修改和删除。
    输出域
    找输出或者设计的等价类和边界值----根据结果的边界反推出输入
    如:数据库涉及时间的查询(首尾时间参考数据的第一条和最后一条记录的时间)
    如:QC需求,同级最大的需求数量:263
    最多的子集层数:255/3 = 85
    如:QC中的日志最大行数、
    如:QC中添加字段最多可以添加24个字段(可以通过查看数据库中的表得到)。

    异常分析
    可靠特性:容错、恢复
    如:SQL Server的导入功能(在源数据上构造错误数据,看异常处理功能的代码是否正确)
    如:进销存的数据还原

    错误猜测
    凭经验
    如:数字输入框(非数字字符的输入控制,小数点的个数)
    数据库的设计(约束关系)

    作业:
    三角形问题:

    成立条件:两边之和大于第三边。
    1、等价类
    2、判定表
    3、流程分析。
    密码修改问题:
    1、判定表
    流程分析

    展开全文
  • 软件测试面试题汇总

    万次阅读 多人点赞 2018-09-27 12:31:09
    请试着分别比较这些不同的测试类型的区别联系(如功能测试、性能测试……)? ................................................................................................................................
    转载自: https://blog.csdn.net/koudaidai/article/details/7394126

    软件测试面试题汇总

    测试技术面试题

    ........................................................................................................................................................................ 5

    1、什么是兼容性测试?兼容性测试侧重哪些方面?.................................................................................... 5

    2、我现在有个程序,发现在Windows上运行得很慢,怎么判别是程序存在问题还是软硬件系统存在问题?       5

    3、测试的策略有哪些?................................................................................................................................. 5

    4、正交表测试用例设计方法的特点是什么?............................................................................................... 5

    5、描述使用bugzilla缺陷管理工具对软件缺陷(BUG)跟踪的管理的流程?.......................................... 5

    6、你觉得bugzilla在使用的过程中,有什么问题?................................................................................. 5

    7、描述测试用例设计的完整过程?.............................................................................................................. 6

    8、单元测试的策略有哪些?......................................................................................................................... 6

    9、LoadRunner分哪三部分?....................................................................................................................... 6

    10、LoadRunner进行测试的流程?................................................................................................................ 6

    什么是并发?在lordrunner中,如何进行并发的测试?集合点失败了会怎么样?.................................. 6

    12、使用QTP做功能测试,录制脚本的时候,要验证多个用户的登录情况/查询情况,如何操作?......... 6

    13、QTP中的Action有什么作用?有几种?................................................................................................. 6

    14、TestDirector有些什么功能,如何对软件测试过程进行管理?............................................................... 7

    15、你所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)?........................................................................................................................................... 7

    16、条软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?...... 8

    17、Beta测试与Alpha测试有什么区别?...................................................................................................... 8

    18、软件的评审一般由哪些人参加?其目的是什么?.................................................................................. 8

    19、测试活动中,如果发现需求文档不完善或者不准确,怎么处理?........................................................ 8

    20、阶段评审与项目评审有什么区别?......................................................................................................... 8

    21、阐述工作版本的定义?............................................................................................................................ 8

    22、什么是桩模块?什么是驱动模块?......................................................................................................... 8

    23、什么是扇入?什么是扇出?.................................................................................................................... 8

    24、你认为做好测试计划工作的关键是什么?............................................................................................. 8

    25、你认为做好测试用例工作的关键是什么?............................................................................................. 9

    26、简述一下缺陷的生命周期?.................................................................................................................... 9

    27、软件的安全性应从哪几个方面去测试?................................................................................................. 9

    28、软件配置管理工作开展的情况和认识?................................................................................................. 9

    29、你觉得软件测试通过的标准应该是什么样的?.................................................................................... 10

    30、引入测试管理的含义?......................................................................................................................... 10

    31、一套完整的测试应该由哪些阶段组成?............................................................................................... 10

    32、单元测试的主要内容?......................................................................................................................... 10

    33、集成测试也叫组装测试或者联合测试,请简述集成测试的主要内容?.............................................. 10

    34、简述集成测试与系统测试关系?.......................................................................................................... 10

    35、软件测试的文档测试应当贯穿于软件生命周期的全过程,其中用户文档是文档测试的重点。那么软件系统的用户文档包括哪些?.............................................................................................................................. 10

    36、软件系统中除用户文档之外,文档测试还应该关注哪些文档?.......................................................... 10

    37、简述软件系统中用户文档的测试要点?............................................................................................... 11

    38、单元测试主要内容是什么?.................................................................................................................. 11

    39、如何理解强度测试?............................................................................................................................. 13

    40、如何理解压力、负载、性能测试测试?............................................................................................... 13

    41、什么是系统瓶颈?................................................................................................................................. 13

    42、文档测试主要包含什么内容?.............................................................................................................. 13

    43、功能测试用例需要详细到什么程度才是合格的?................................................................................ 14

    44、配置和兼容性测试的区别是什么?....................................................................................................... 14

    45、软件文档测试主要包含什么?.............................................................................................................. 15

    46、没有产品说明书和需求文档地情况下能够进行黑盒测试吗?............................................................. 15

    47、测试中的“杀虫剂怪事”是指什么?................................................................................................... 15

    48、在配置测试中,如何判断发现的缺陷是普通问题还是特定的配置问题?........................................... 15

    49、为什么尽量不要让时间有富裕的员工去做一些测试?......................................................................... 16

    50、完全测试程序是可能的吗?.................................................................................................................. 16

    51、软件测试的风险主要体现在哪里?....................................................................................................... 16

    52、发现的缺陷越多,说明软件缺陷越多吗?........................................................................................... 16

    53、所有的软件缺陷都能修复吗?所有的软件缺陷都要修复吗?............................................................. 17

    54、软件测试人员就是QA吗?.................................................................................................................... 17

    55、如何减少测试人员跳槽带来的损失?................................................................................................... 17

    56、测试产品与测试项目的区别是什么?................................................................................................... 17

    57、和用户共同测试(UAT测试)的注意点有哪些?................................................................................. 18

    58、如何编写提交给用户的测试报告?....................................................................................................... 18

    59、测试工具在测试工作中是什么地位?................................................................................................... 18

    60、什么是软件测试,软件测试的目的?................................................................................................... 18

    61、简述负载测试与压力测试的区别。....................................................................................................... 19

    62、写出bug报告流转的步骤,每步的责任人及主要完成的工作。.......................................................... 19

    63、写出bug报告当中一些必备的内容。................................................................................................... 19

    64、开发人员老是犯一些低级错误怎么解决?........................................................................................... 20

    65、画出软件测试的V模型图。.................................................................................................................. 20

    66、为什么要在一个团队中开展软件测试工作?........................................................................................ 20

    67、您在以往的测试工作中都曾经具体从事过哪些工作?其中最擅长哪部分工作?............................... 20

    68、您所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)............................................................................................................................................. 20

    69、您认为做好测试用例设计工作的关键是什么?.................................................................................... 21

    70、请试着比较一下黑盒测试、白盒测试、单元测试、集成测试、系统测试、验收测试的区别与联系。21

    71、测试计划工作的目的是什么?测试计划工作的内容都包括什么?其中哪些是最重要的?................. 22

    72、您所熟悉的测试用例设计方法都有哪些?请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。................................................................................................................................................................ 22

    73、请以您以往的实际工作为例,详细的描述一次测试用例设计的完整的过程。.................................... 23

    74、您以往是否曾经从事过性能测试工作?如果有,请尽可能的详细描述您以往的性能测试工作的完整过程。................................................................................................................................................................ 23

    75、你对测试最大的兴趣在哪里?为什么?................................................................................................ 23

    76、你以前工作时的测试流程是什么?....................................................................................................... 24

    77、当开发人员说不是BUG时,你如何应付?.......................................................................................... 24

    78、软件的构造号与版本号之间的区别?BVT(BuildVerificationTest)............................................... 24

    79、您以往的工作中,一条软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?.................................................................................................................................................... 25

    80、您以往所从事的软件测试工作中,是否使用了一些工具来进行软件缺陷(Bug)的管理?如果有,请结合该工具描述软件缺陷(Bug)跟踪管理的流程。.......................................................................................... 25

    81、您认为性能测试工作的目的是什么?做好性能测试工作的关键是什么?........................................... 25

    82、单元测试、集成测试、系统测试的侧重点是什么?............................................................................. 25

    83、集成测试通常都有那些策略?............................................................................................................... 25

    84、一个缺陷测试报告的组成...................................................................................................................... 25

    85、基于WEB信息管理系统测试时应考虑的因素有哪些?......................................................................... 25

    86、软件测试项目从什么时候开始,?为什么?........................................................................................ 26

    87、需求测试注意事项有哪些?.................................................................................................................. 26

    88、简述一下缺陷的生命周期...................................................................................................................... 26

    89、你在你所在的公司是怎么开展测试工作的?是如何组织的?............................................................. 26

    90、你认为理想的测试流程是什么样子?................................................................................................... 26

    91、您在从事性能测试工作时,是否使用过一些测试工具?如果有,请试述该工具的工作原理,并以一个具体的工作中的例子描述该工具是如何在实际工作中应用的。...................................................................... 26

    92、软件测试活动的生命周期是什么?....................................................................................................... 26

    93、请画出软件测试活动的流程图?.......................................................................................................... 26

    94、针对缺陷采取怎样管理措施?.............................................................................................................. 26

    95、什么是测试评估?测试评估的范围是什么?........................................................................................ 26

    96、如果能够执行完美的黑盒测试,还需要进行白盒测试吗?为什么?.................................................. 26

    97、测试结束的标准是什么?...................................................................................................................... 26

    98、软件验收测试除了alpha ,beta测试以外,还有哪一种?.................................................................... 26

    99、做测试多久了?以前做过哪些项目?你们以前测试的流程是怎样的?用过哪些测试工具?............. 27

    100、请就如何在开发中进行软件质量控制说说你的看法.......................................................................... 27

    101、一套完整的测试应该由哪些阶段组成?分别阐述一下各个阶段。.................................................... 27

    102、软件测试的类型有那些?分别比较这些不同的测试类型的区别与联系。......................................... 27

    103、测试用例通常包括那些内容?着重阐述编制测试用例的具体做法.................................................... 27

    104、在分别测试winform的C/S结构与测试WEB结构的软件是,应该采取什么样的方法分别测试?他们存在什么样的区别与联系?.................................................................................................................................. 27

    105、在测试winform的C/S结构软件时,发现这个软件的运行速度很慢,您会认为是什么原因?您会采取哪些方法去检查这个原因?.............................................................................................................................. 27

    106、描述使用bugzilla缺陷管理工具对软件缺陷(BUG)跟踪的管理的流程........................................ 27

    107、你都用什么测试方法 针对不同的产品或者系统或者模块,有不同的测试方法。总体而言有白盒测试和黑盒测试。.................................................................................................................................................... 27

    108、怎么编写案例 案例的编写与测试阶段的定义有很大的关系。系统测试和unit测试的案例可能不同。总体而言测试案例根据系统的需求而定。....................................................................................................... 27

    109、怎么才能够全面的测试到每一个点 测试的全面性主要需要在设计测试计划的时候考虑,从测试策略,产品需求等等多个角度考虑从而定义全部的测试点。................................................................................. 27

    110、谈谈软件测试技术,以及如何提高..................................................................................................... 27

    111、谈谈软件测试职业发展,以及个人的打算......................................................................................... 27

    112、谈谈软件测试在企业的地位,也可以结合软件生命周期来谈........................................................... 27

    113、一般公司里实际的软件测试流程是什么样的?你们公司又是怎样的?............................................ 27

    114、软件工程师要具有那些素质?............................................................................................................ 27

    115、你会哪些测试工具?怎么操作?........................................................................................................ 27

    116、你能不能说下你的3到5年的职业计划(规划)............................................................................... 27

    117、你觉得你来应聘有那些优势?............................................................................................................ 27

    其他问题:(有可能清晰的思路比确切的答案更重要)............................................................................. 27

     

    开发及环境搭建类面试题

    ....................................................................................................................................................................... 28

    1、描述软件产生内存泄露的原因以及检查方式。(可以结合一种开发语言进行描述)............................ 28

    2、简述什么是值传递,什么是地址传递,两者区别是什么?................................................................... 28

    3、结构化程序设计和面向对象程序设计各自的特点及优缺点是什么?.................................................... 28

    4、简述什么是存储过程和触发器?............................................................................................................. 28

    5、使用C语言编写一个函数,用于交换两个变量的值(地址传递)。...................................................... 29

    6、请简述DNS、活动目录、域的概念。..................................................................................................... 29

    7、描述TCP/IP协议的层次结构,以及每一层中重要协议。...................................................................... 29

    8、简述子网掩码的用途。............................................................................................................................ 29

    9、说出4种以上常用的操作系统及其主要的应用范围(微软的操作系统除外)。.................................... 29

    10、在Linux系统中,一个文件的访问权限是755,其含义是什么?......................................................... 29

    11、Windows操作系统中PATH环境变量的作用是什么?.......................................................................... 30

    12、Ghost的主要用途和常用方法?........................................................................................................... 30

    13、在RedHat中,从root用户切到userl用户,一般用什么命令?..................................................... 30

    14、Linux中,一般怎么隐藏文件?........................................................................................................... 30

    15、如何将自己的本地磁盘(D)做成FTP供远端主机使用?................................................................... 30

    16、对RUP.CMM,CMMI,XP,PSP.TSP的认识?............................................................................................. 30

    17、DNS是什么,它是如何工作的?............................................................................................................... 31

    18、防火墙如何保证安全的?主要有哪些?............................................................................................... 31

    19、目前流行的操作的系统有哪些?请举例说明安装操作系统的注意事项?........................................... 33

    20、简述一下c/s模式或者b/s模式?....................................................................................................... 33

    21、TCP/UDP有哪些区别?.......................................................................................................................... 34

    22、ISO模型?HUB、tch、Router是ISO的第几层设备?....................................................................... 34

    23、内存有哪几种存储组织结构.请分别加以说明?.................................................................................. 34

     

    人力资源面试题

    ....................................................................................................................................................................... 34

    1、你的测试职业发展是什么?你自认为做测试的优势在哪里?................................................................ 34

    2、你为什么想离开目前的职务?................................................................................................................ 34

    3、你对我们公司了解有多少?.................................................................................................................... 34

    4、你找工作时,最重要的考虑因素为何?................................................................................................. 34

    5、为什么我们应该录取你?........................................................................................................................ 34

    6、请谈谈你个人的最大特色。.................................................................................................................... 34

    7、一个测试工程师应具备那些素质和技能?.............................................................................................. 35

    8、您认为在测试人员同开发人员的沟通过程中,如何提高沟通的效率和改善沟通的效果?维持测试人员同开发团队中其他成员良好的人际关系的关键是什么?..................................................................................... 35

    9、在您以往的测试工作中,最让您感到不满意或者不堪回首的事情是什么?您是如何来对待这些事情的?35

    10、在即将完成这次笔试前,您是否愿意谈一些自己在以往的学习和工作中获得的工作经验和心得体会?(可以包括软件测试、过程改进、软件开发或者与此无关的其他方面)....................................................... 35

    11、为什么选择测试这行?......................................................................................................................... 35

    12、你的工作通常能在时限内完成吗.(我想问一下就是她问这个问题的动机是什么).......................... 35

    13、通常你对于别人批评你会有什么样的反应........................................................................................... 35

    14、如果明知这样做不对,你还会依主管的指过去做吗?......................................................................... 35

    15、如果你接到一个客户抱怨的电话,你确知无法解决他的问题,你会怎么处理?............................... 35

    16、请就软件测试人员应该具备什么样的基本素质说说你的看法。.......................................................... 36

    17、你在五年内的个人目标和职业目标分别是什么?................................................................................ 36

    18、你怎样做出自己的职业选择?.............................................................................................................. 36

     

     

     

     

     

     

     

     

    测试技术面试题

    1、什么是兼容性测试?兼容性测试侧重哪些方面?

    参考答案:

    兼容测试主要是检查软件在不同的硬件平台、软件平台上是否可以正常的运行,即是通常说的软件的可移植性。

    兼容的类型,如果细分的话,有平台的兼容,网络兼容,数据库兼容,以及数据格式的兼容。

    兼容测试的重点是,对兼容环境的分析。通常,是在运行软件的环境不是很确定的情况下,才需要做兼容。根据软件运行的需要,或者根据需求文档,一般都能够得出用户会在什么环境下使用该软件,把这些环境整理成表单,就得出做兼容测试的兼容环境了。

    兼容和配置测试的区别在于,做配置测试通常不是Clean OS下做测试,而兼容测试多是在Clean OS的环境下做的。

    2、我现在有个程序,发现在Windows上运行得很慢,怎么判别是程序存在问题还是软硬件系统存在问题?

    参考答案:

    1、检查系统是否有中毒的特征;

    2、检查软件/硬件的配置是否符合软件的推荐标准;

    3、确认当前的系统是否是独立,即没有对外提供什么消耗CPU资源的服务;

    4、如果是C/S或者B/S结构的软件,需要检查是不是因为与服务器的连接有问题,或者访问有问题造成的;

    5、在系统没有任何负载的情况下,查看性能监视器,确认应用程序对CPU/内存的访问情况。

    3、测试的策略有哪些?

    参考答案:

    黑盒/白盒,静态/动态,手工/自动,冒烟测试,回归测试,公测(Beta测试的策略)

    4、正交表测试用例设计方法的特点是什么?

    参考答案:

    用最少的实验覆盖最多的操作,测试用例设计很少,效率高,但是很复杂;

    对于基本的验证功能,以及二次集成引起的缺陷,一般都能找出来;但是更深的缺陷,更复杂的缺陷,还是无能为力的;

    具体的环境下,正交表一般都很难做的。大多数,只在系统测试的时候使用此方法。

    5、描述使用bugzilla缺陷管理工具对软件缺陷(BUG)跟踪的管理的流程?

    参考答案:

    就是Bugzilla的状态转换图。

    6、你觉得bugzilla在使用的过程中,有什么问题?

    参考答案:

    界面不稳定;

    根据需要配置它的不同的部分,过程很烦琐。

    流程控制上,安全性不好界定,很容易对他人的Bug进行误操作;

    没有综合的评分指标,不好确认修复的优先级别。

    7、描述测试用例设计的完整过程?

    参考答案:

    需求分析 + 需求变更的维护工作;

    根据需求 得出测试需求;

    设计测试方案,评审测试方案;

    方案评审通过后,设计测试用例,再对测试用例进行评审;

    8、单元测试的策略有哪些?

    参考答案:

    逻辑覆盖、循环覆盖、同行评审、桌前检查、代码走查、代码评审、景泰数据流分析

    9、LoadRunner分哪三部分?

    参考答案:

    用户动作设计;

    场景设计;

    测试数据分析;

    10、LoadRunner进行测试的流程?

    参考答案:

    1、 测试测试

    2、 创建虚拟用户脚本

    3、 创建运行场景

    4、 运行测试脚本

    5、 监视场景

    6、 分析测试的结果

    以上,最好是结合一个案例,根据以上流程来介绍。

    什么是并发?在lordrunner中,如何进行并发的测试?集合点失败了会怎么样?

    参考答案:

    在同一时间点,支持多个不同的操作。

    LoadRunner中提供IP伪装,集合点,配合虚拟用户的设计,以及在多台电脑上设置,可以比较好的模拟真实的并发。

    集合点,即是多个用户在某个时刻,某个特定的环境下同时进行虚拟用户的操作的。集合点失败,则集合点的才操作就会取消,测试就不能进行。

    12、使用QTP做功能测试,录制脚本的时候,要验证多个用户的登录情况/查询情况,如何操作?

    参考答案:

    分析用户登录的基本情况,得出一组数据,通过性测试/失败性测试的都有(根据TC来设计这些数据),然后录制登录的脚本,将关键的数据参数化,修改脚本,对代码进行加强,调试脚本。

    13、QTP中的Action有什么作用?有几种?

    参考答案:

    Action的作用

    用Action可以对步骤集进行分组

    步骤重组,然后被整体调用

    拥有自己的sheet

    组合有相同需求的步骤,整体操作

    具有独立的对象仓库

    Action的种类

    可复用Action

    不可复用Action

    外部Action

    14、TestDirector有些什么功能,如何对软件测试过程进行管理?

    参考答案:

     需求管理

    定义测试范围

    定义需求树

    描述需求树的功能点

    测试计划

    定义测试目标和测试策略。

    分解应用程序,建立测试计划树。

    确定每个功能点的测试方法。

    将每个功能点连接到需求上,使测试计划覆盖全部的测试需求。

    描述手工测试的测试步骤

    指明需要进行自动测试的功能点

    测试执行

    定义测试集合。

    为每个测试人员制定测试任务和测试日程安排。

    运行自动测试。

    缺陷跟踪

    记录缺陷

    查看新增缺陷,并确定哪些是需要修正的

    相关技术人员修改缺陷

    回归测试

    分析缺陷统计图表,分析应用程序的开发质量。

    15、你所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)?

    参考答案:Compatibility Testing(兼容性测试),也称“Configuration testing(配置测试)”,测试软件是否和系统的其它与之交互的元素之间兼容,如:浏览器、操作系统、硬件等。验证测试对象在不同的软件和硬件配置中的运行情况。

     

    Functional testing (功能测试),也称为behavioral testing(行为测试),根据产品特征、操作描述和用户方案,测试一个产品的特性和可操作行为以确定它们满足设计需求。本地化软件的功能测试,用于验证应用程序或网站对目标用户能正确工作。使用适当的平台、浏览器和测试脚本,以保证目标用户的体验将足够好,就像应用程序是专门为该市场开发的一样。

    Performance testing(性能测试),评价一个产品或组件与性能需求是否符合的测试。包括负载测试、强度测试、数据库容量测试、基准测试等类型。

    16、软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?

    参考答案:5C标准

    17、Beta测试与Alpha测试有什么区别?

    参考答案:Beta testing(β测试),测试是软件的多个用户在一个或多个用户的实际使用环境下进行的测试。开发者通常不在测试现场

    Alpha testing (α测试),是由一个用户在开发环境下进行的测试,也可以是公司内部的用户在模拟实际操作环境下进行的受控测试

    18、软件的评审一般由哪些人参加?其目的是什么?

    参考答案:

    在正式的会议上将软件项目的成果(包括各阶段的文档、产生的代码等)提交给用户、客户或有关部门人员对软件产品进行评审和批准。其目的是找出可能影响软件产品质量、开发过程、维护工作的适用性和环境方面的设计缺陷,并采取补救措施,以及找出在性能、安全性和经济方面的可能的改进。

    人员:用户、客户或有关部门开发人员,测试人员,需求分析师都可以,就看处于评审那个阶段

    19、测试活动中,如果发现需求文档不完善或者不准确,怎么处理?

    参考答案:

    测试需求分析发现需求文档不完善或者不准确,应该立即和相关人员进行协调交流。

    20、阶段评审与项目评审有什么区别?

    参考答案:

    阶段评审对项目各阶段评审:对阶段成果和工作

    项目评审对项目总体评审:对工作和产品

    21、阐述工作版本的定义?

    参考答案:

    构造号: BUILD

    22、什么是桩模块?什么是驱动模块?

    参考答案:

    桩模块:被测模块调用模块

    驱动模块调用被测模块

    23、什么是扇入?什么是扇出?

    参考答案:

    扇入:被调次数,扇出:调其它模块数目

    24、你认为做好测试计划工作的关键是什么?

    参考答案:

    软件测试计划就是在软件测试工作正式实施之前明确测试的对象,并且通过对资源、时间、风险、测试范围和预算等方面的综合分析和规划,保证有效的实施软件测试;

    做好测试计划工作的关键:目的,管理,规范

    1. 明确测试的目标,增强测试计划的实用性

    编写软件测试计划得重要目的就是使测试过程能够发现更多的软件缺陷,因此软件测试计划的价值取决于它对帮助管理测试项目,并且找出软件潜在的缺陷。因此,软件测试计划中的测试范围必须高度覆盖功能需求,测试方法必须切实可行,测试工具并且具有较高的实用性,便于使用,生成的测试结果直观、准确

    2.坚持“5W”规则,明确内容与过程

    “5W”规则指的是“What(做什么)”、“Why(为什么做)”、“When(何时做)”、“Where(在哪里)”、“How(如何做)”。利用“5W”规则创建软件测试计划,可以帮助测试团队理解测试的目的(Why),明确测试的范围和内容(What),确定测试的开始和结束日期(When),指出测试的方法和工具(How),给出测试文档和软件的存放位置(Where)。

    3.采用评审和更新机制,保证测试计划满足实际需求

    测试计划写作完成后,如果没有经过评审,直接发送给测试团队,测试计划内容的可能不准确或遗漏测试内容,或者软件需求变更引起测试范围的增减,而测试计划的内容没有及时更新,误导测试执行人员。

    4. 分别创建测试计划与测试详细规格、测试用例

    应把详细的测试技术指标包含到独立创建的测试详细规格文档,把用于指导测试小组执行测试过程的测试用例放到独立创建的测试用例文档或测试用例管理数据库中。测试计划和测试详细规格、测试用例之间是战略和战术的关系,测试计划主要从宏观上规划测试活动的范围、方法和资源配置,而测试详细规格、测试用例是完成测试任务的具体战术。

    25、你认为做好测试用例工作的关键是什么?

    参考答案:

     需求和设计文档的理解程度,对系统的熟悉程度

    26、简述一下缺陷的生命周期?

    参考答案:提交->确认->分配->修复->验证->关闭

    27、软件的安全性应从哪几个方面去测试?

    参考答案:

    (1)用户认证机制:如数据证书、智能卡、双重认证、安全电子交易协议

    (2)加密机制

    (3)安全防护策略:如安全日志、入侵检测、隔离防护、漏洞扫描

    (4)数据备份与恢复手段:存储设备、存储优化、存储保护、存储管理

    (5)防病毒系统

    28、软件配置管理工作开展的情况和认识?

    参考答案:

    软件配置管理贯穿于软件开发、测试活动的始终,覆盖了开发、测试活动的各个环节,它的重要作用之一就是要全面的管理保存各个配置项,监控各配置项的状态,并向项目经理及相关的人员报告,从而实现对软件过程的控制。

    软件测试配置管理包括4个最基本的活动:

    配置项标识

    配置项控制

    配置项状态报告

    配置审计

           软件配置管理通常借助工具来辅助,主要有MS SourceSafe、Rational ClearCase等

    29、你觉得软件测试通过的标准应该是什么样的?

    参考答案:

        缺陷密度值达到客户的要求

    30、引入测试管理的含义?

    参考答案:风险分析,进度控制、角色分配、质量控制

    31、一套完整的测试应该由哪些阶段组成?

    参考答案:测试计划、测试设计与开发、测试实施、测试评审与测试结论

    32、单元测试的主要内容?

    参考答案:

     模块接口测试、局部数据结构测试、路径测试、错误处理测试、边界测试

    33、集成测试也叫组装测试或者联合测试,请简述集成测试的主要内容?

    参考答案:

    (1)在把各个模块连接起来的时候,穿越模块接口的数据是否会丢失;

     (2)一个模块的功能是否会对另一个模块的功能产生不利的影响;

     (3)各个子功能组合起来,能否达到预期要求的父功能;

     (4)全局数据结构是否有问题;

     (5)单个模块的误差累积起来,是否会放大,从而达到不能接受的程度。

    34、简述集成测试与系统测试关系?

    参考答案:

     (1)集成测试的主要依据概要设计说明书,系统测试的主要依据是需求设计说明书;

     (2)集成测试是系统模块的测试,系统测试是对整个系统的测试,包括相关的软硬件平台、网络以及相关外设的测试。

    35、软件测试的文档测试应当贯穿于软件生命周期的全过程,其中用户文档是文档测试的重点。那么软件系统的用户文档包括哪些?

    参考答案:

      用户手册

      安装和设置指导

      联机帮助

      指南、向导

      样例、示例和模板

      授权/注册登记表

    最终用户许可协议

    36、软件系统中除用户文档之外,文档测试还应该关注哪些文档?

    参考答案:

    开发文档

    软件需求说明书

        数据库设计说明书

        概要设计说明书

        详细设计说明书

        可行性研究报告

    管理文档

        项目开发计划

        测试计划

        测试报告

        开发进度月报

        开发总结报告

    37、简述软件系统中用户文档的测试要点?

    参考答案:

     (1)读者群。文档面向的读者定位要明确。对于初级用户、中级用户以及高级用户应该有不同的定位

     (2)术语。文档中用到的术语要适用与定位的读者群,用法一致,标准定义与业界规范相吻合。

     (3)正确性。测试中需检查所有信息是否真实正确,查找由于过期产品说明书和销售人员夸大事实而导致的错误。检查所有的目录、索引和章节引用是否已更新,尝试链接是否准确,产品支持电话、地址和邮政编码是否正确。

     (4)完整性。对照软件界面检查是否有重要的分支没有描述到,甚至是否有整个大模块没有描述到。

     (5)一致性。按照文档描述的操作执行后,检查软件返回的结果是否与文档描述的相同。

     (6)易用性。对关键步骤以粗体或背景色给用户以提示,合理的页面布局、适量的图表都可以给用户更高的易用性。需要注意的是文档要有助于用户排除错误。不但描述正确操作,也要描述错误处理办法。文档对于用户看到的错误信息应当有更详细的文档解释。

     (7)图表与界面截图。检查所有图表与界面截图是否与发行版本相同。

     (8)样例与示例。像用户一样载入和使用样例。如果是一段程序,就输入数据并执行它。以每一个模块制作文件,确认它们的正确性。

     (9)语言。不出现错别字,不要出现有二义性的说法。特别要注意的是屏幕截图或绘制图形中的文字。

     (10)印刷与包装。检查印刷质量;手册厚度与开本是否合适;包装盒的大小是否合适;有没有零碎易丢失的小部件等等。

    38、单元测试主要内容是什么?

    参考答案:

    单元测试大多数由开发人员来完成,测试人员技术背景较好或者开发系统软件时可能会安排测试人员进行单元测试,大多数进行的单元测试都是开发人员调试程序或者开发组系统联合调试的过程。讨论这个问题主要是扩充一下读者的视野。

    单元测试一般包括五个方面的测试:

    (1)模块接口测试:模块接口测试是单元测试的基础。只有在数据能正确流入、流出模块的前提下,其他测试才有意义。模块接口测试也是集成测试的重点,这里进行的测试主要是为后面打好基础。测试接口正确与否应该考虑下列因素:

    -输入的实际参数与形式参数的个数是否相同;

    -输入的实际参数与形式参数的属性是否匹配;

    -输入的实际参数与形式参数的量纲是否一致;

    -调用其他模块时所给实际参数的个数是否与被调模块的形参个数相同;

    -调用其他模块时所给实际参数的属性是否与被调模块的形参属性匹配;

    -调用其他模块时所给实际参数的量纲是否与被调模块的形参量纲一致;

    -调用预定义函数时所用参数的个数、属性和次序是否正确;

    -是否存在与当前入口点无关的参数引用;

    -是否修改了只读型参数;

    -对全程变量的定义各模块是否一致;

    -是否把某些约束作为参数传递。

    如果模块功能包括外部输入输出,还应该考虑下列因素:

    -文件属性是否正确;

    -OPEN/CLOSE语句是否正确;

    -格式说明与输入输出语句是否匹配;

    -缓冲区大小与记录长度是否匹配;

    -文件使用前是否已经打开;

    -是否处理了文件尾;

    -是否处理了输入/输出错误;

    -输出信息中是否有文字性错误。

    -局部数据结构测试;

    -边界条件测试;

    -模块中所有独立执行通路测试;

    (2)局部数据结构测试:检查局部数据结构是为了保证临时存储在模块内的数据在程序执行过程中完整、正确,局部功能是整个功能运行的基础。重点是一些函数是否正确执行,内部是否运行正确。局部数据结构往往是错误的根源,应仔细设计测试用例,力求发现下面几类错误:

    -不合适或不相容的类型说明;

    -变量无初值;

    -变量初始化或省缺值有错;

    -不正确的变量名(拼错或不正确地截断);

    -出现上溢、下溢和地址异常。

    (3)边界条件测试:边界条件测试是单元测试中最重要的一项任务。众所周知,软件经常在边界上失效,采用边界值分析技术,针对边界值及其左、右设计测试用例,很有可能发现新的错误。边界条件测试是一项基础测试,也是后面系统测试中的功能测试的重点,边界测试执行的较好,可以大大提高程序健壮性。

    (4)模块中所有独立路径测试:在模块中应对每一条独立执行路径进行测试,单元测试的基本任务是保证模块中每条语句至少执行一次。测试目的主要是为了发现因错误计算、不正确的比较和不适当的控制流造成的错误。具体做法就是程序员逐条调试语句。常见的错误包括:

    -误解或用错了算符优先级;

    -混合类型运算;

    -变量初值错;

    -精度不够;

    -表达式符号错。

    比较判断与控制流常常紧密相关,测试时注意下列错误:

    -不同数据类型的对象之间进行比较;

    -错误地使用逻辑运算符或优先级;

    -因计算机表示的局限性,期望理论上相等而实际上不相等的两个量相等;

    -比较运算或变量出错;

    -循环终止条件或不可能出现;

    -迭代发散时不能退出;

    -错误地修改了循环变量。

    模块的各条错误处理通路测试:程序在遇到异常情况时不应该退出,好的程序应能预见各种出错条件,并预设各种出错处理通路。如果用户不按照正常操作,程序就退出或者停止工作,实际上也是一种缺陷,因此单元测试要测试各种错误处理路径。一般这种测试着重检查下列问题:

    -输出的出错信息难以理解;

    -记录的错误与实际遇到的错误不相符;

    -在程序自定义的出错处理段运行之前,系统已介入;

    -异常处理不当;

    -错误陈述中未能提供足够的定位出错信息。

    39、如何理解强度测试?

    参考答案:

    强度测试是为了确定系统在最差工作环境的工作能力,也可能是用于验证在标准工作压力下的各种资源的最下限指标。

    它和压力测试的目标是不同的,压力测试是在标准工作环境下,不断增加系统负荷,最终测试出该系统能力达到的最大负荷(稳定和峰值),而强度测试则是在非标准工作环境下,甚至不断人为降低系统工作环境所需要的资源,如网络带宽,系统内存,数据锁等等,以测试系统在资源不足的情况下的工作状态,通过强度测试,可以确定本系统正常工作的最差环境.

    强度测试和压力测试的测试指标相近,大多都是与时间相关的指标,如并发量(吞吐量),延迟(最大\最小\平均)以及顺序指标等

    强度测试需要对系统的结构熟悉,针对系统的特征设计强度测试的方法

    40、如何理解压力、负载、性能测试测试?

    参考答案:

    性能测试是一个较大的范围,实际上性能测试本身包含了性能、强度、压力、负载等多方面的测试内容。

    压力测试是对服务器的稳定性以及负载能力等方面的测试,是一种很平常的测试。增大访问系统的用户数量、或者几个用户进行大数据量操作都是压力测试。而负载测试是压力相对较大的测试,主要是测试系统在一种或者集中极限条件下的相应能力,是性能测试的重要部分。100个用户对系统进行连续半个小时的访问可以看作压力测试,那么连续访问8个小时就可以认为负载测试,1000个用户连续访问系统1个小时也可以看作是负载测试。

    实际上压力测试和负载测试没有明显的区分。测试人员应该站在关注整体性能的高度上来对系统进行测试。

    41、什么是系统瓶颈?

    参考答案:

    瓶颈主要是指整个软硬件构成的软件系统某一方面或者几个方面能力不能满足用户的特定业务要求,“特定”是指瓶颈会在某些条件下会出现,因为毕竟大多数系统在投入前。

    严格的从技术角度讲,所有的系统都会有瓶颈,因为大多数系统的资源配置不是协调的,例如CPU使用率刚好达到100%时,内存也正好耗尽的系统不是很多见。因此我们讨论系统瓶颈要从应用的角度讨论:关键是看系统能否满足用户需求。在用户极限使用系统的情况下,系统的响应仍然正常,我们可以认为改系统没有瓶颈或者瓶颈不会影响用户工作。

    因此我们测试系统瓶颈主要是实现下面两个目的:

    -发现“表面”的瓶颈。主要是模拟用户的操作,找出用户极限使用系统时的瓶颈,然后解决瓶颈,这是性能测试的基本目标。

    -发现潜在的瓶颈并解决,保证系统的长期稳定性。主要是考虑用户在将来扩展系统或者业务发生变化时,系统能够适应变化。满足用户目前需求的系统不是最好的,我们设计系统的目标是在保证系统整个软件生命周期能够不断适应用户的变化,或者通过简单扩展系统就可以适应新的变化。

    42、文档测试主要包含什么内容?

    参考答案:

    在国内软件开发管理中,文档管理几乎是最弱的一项,因而在测试工作中特别容易忽略文档测试也就不足为奇了。要想给用户提供完整的产品,文档测试是必不可少的。文档测试一般注重下面几个方面:

    文档的完整性:主要是测试文档内容的全面性与完整性,从总体上把握文档的质量。例如用户手册应该包括软件的所有功能模块。

    描述与软件实际情况的一致性:主要测试软件文档与软件实际的一致程度。例如用户手册基本完整后,我们还要注意用户手册与实际功能描述是否一致。因为文档往往跟不上软件版本的更新速度。

    易理解性:主要是检查文档对关键、重要的操作有无图文说明,文字、图表是否易于理解。对于关键、重要的操作仅仅只有文字说明肯定是不够的,应该附有图表使说明更为直观和明了。

    文档中提供操作的实例:这项检查内容主要针对用户手册。对主要功能和关键操作提供的应用实例是否丰富,提供的实例描述是否详细。只有简单的图文说明,而无实例的用户手册看起来就像是软件界面的简单拷贝,对于用户来说,实际上没有什么帮助。

    印刷与包装质量:主要是检查软件文档的商品化程度。有些用户手册是简单打印、装订而成,过于粗糙,不易于用户保存。优秀的文档例如用户手册和技术白皮书,应提供商品化包装,并且印刷精美。

    43、功能测试用例需要详细到什么程度才是合格的?

    参考答案:

    这个问题也是测试工程师经常问的问题。有人主张测试用例详细到每个步骤执行什么都要写出来,目的是即使一个不了解系统的新手都可以按照测试用例来执行工作。主张这类写法的人还可以举出例子:欧美、日本等软件外包文档都是这样做的。

    另外一种观点就是主张写的粗些,类似于编写测试大纲。主张这种观点的人是因为软件开发需求管理不规范,变动十分频繁,因而不能按照欧美的高标准来编写测试用例。这样的测试用例容易维护,可以让测试执行人员有更大的发挥空间。

    实际上,软件测试用例的详细程度首先要以覆盖到测试点为基本要求。举个例子:“用户登陆系统”的测试用例可以不写出具体的执行数据,但是至少要写出五种以上情况(),如果只用一句话覆盖了这个功能是不合格的测试用例。覆盖功能点不是指列出功能点,而是要写出功能点的各个方面(如果组合情况较多时可以采用等价划分)。

    另一个影响测试用例的就是组织的开发能力和测试对象特点。如果开发力量比较落后,编写较详细的测试用例是不现实的,因为根本没有那么大的资源投入,当然这种情况很随着团队的发展而逐渐有所改善。测试对象特点重点是指测试对象在进度、成本等方面的要求,如果进度较紧张的情况下,是根本没有时间写出高质量的测试用例的,甚至有些时候测试工作只是一种辅助工作,因而不编写测试用例。

    因此,测试用例的编写要根据测试对象特点、团队的执行能力等各个方面综合起来决定编写策略。最后要注意的是测试人员一定不能抱怨,力争在不断提高测试用例编写水平的同时,不断地提高自身能力。

    44、配置和兼容性测试的区别是什么?

    参考答案:

    配置测试的目的是保证软件在其相关的硬件上能够正常运行,而兼容性测试主要是测试软件能否与不同的软件正确协作。

    配置测试的核心内容就是使用各种硬件来测试软件的运行情况,一般包括:

    (1)软件在不同的主机上的运行情况,例如Dell和Apple;

    (2)软件在不同的组件上的运行情况,例如开发的拨号程序要测试在不同厂商生产的Modem上的运行情况;

    (3)不同的外设;

    (4)不同的接口;

    (5)不同的可选项,例如不同的内存大小;

    兼容性测试的核心内容:

    (1)测试软件是否能在不同的操作系统平台上兼容;

    (2)测试软件是否能在同一操作系统平台的不同版本上兼容;

    (3)软件本身能否向前或者向后兼容;

    (4)测试软件能否与其它相关的软件兼容;

    (5)数据兼容性测试,主要是指数据能否共享;

    配置和兼容性测试通称对开发系统类软件比较重要,例如驱动程序、操作系统、数据库管理系统等。具体进行时仍然按照测试用例来执行。

    45、软件文档测试主要包含什么?

    参考答案:

    随着软件文档系统日益庞大,文档测试已经成为软件测试的重要内容。文档测试对象主要如下:

    -包装文字和图形;

    -市场宣传材料、广告以及其它插页;

    -授权、注册登记表;

    -最终用户许可协议;

    -安装和设置向导;

    -用户手册;

    -联机帮助;

    -样例、示范例子和模板;

    -……

    文档测试的目的是提高易用性和可靠性,降低支持费用,因为用户通过文档就可以自己解决问题。因文档测试的检查内容主要如下:

    -读者对象——主要是文档的内容是否能让该级别的读者理解;

    -术语——主要是检查术语是否适合读者;

    -内容和主题——检查主题是否合适、是否丢失、格式是否规范等;

    -图标和屏幕抓图——检查图表的准确度和精确度;

    -样例和示例——是否与软件功能一致;

    -拼写和语法;

    -文档的关联性——是否与其它相关文档的内容一致,例如与广告信息是否一致;

    文档测试是相当重要的一项测试工作,不但要给予充分的重视,更要要认真的完成,象做功能测试一样来对待文档测试。

    46、没有产品说明书和需求文档地情况下能够进行黑盒测试吗?

    参考答案:

    这个问题是国内测试工程师经常遇到的问题,根源就是国内软件开发文档管理不规范,对变更的管理方法就更不合理了。实际上没有任何文档的时候,测试人员是能够进行黑盒测试的,这种测试方式我们可以称之为探索测试,具体做法就是测试工程师根据自己的专业技能、领域知识等不断的深入了解测试对象、理解软件功能,进而发现缺陷。

    在这种做法基本上把软件当成了产品说明书,测试过程中要和开发人员不断的进行交流。尤其在作项目的时候,进度压力比较大,可以作为加急测试方案。最大的风险是不知道有些特性是否被遗漏。

    47、测试中的“杀虫剂怪事”是指什么?

    参考答案:

    “杀虫剂怪事”一词由BorisBeizer在其编著的《软件测试技术》第二版中提出。用于描述测试人员对同一测试对象进行的测试次数越多,发现的缺陷就会越来越少的现象。就像老用一种农药,害虫就会有免疫力,农药发挥不了效力。这种现象的根本原因就是测试人员对测试软件过于熟悉,形成思维定势。

    为了克服这种现象,测试人员需要不断编写新的测试程序或者测试用例,对程序的不同部分进行测试,以发现更多的缺陷。也可以引用新人来测试软件,刚刚进来的新手往往能发现一些意想不到的问题。

    48、在配置测试中,如何判断发现的缺陷是普通问题还是特定的配置问题?

    参考答案:

    在进行配置测试时,测试工程师仍然会发现一些普通的缺陷,也就是与配置环境无关的缺陷。因此判断新发现的问题,需要在不同的配置中重新执行发现软件缺陷的步骤,如果软件缺陷不出现了,就可能是配置缺陷;如果在所有的配置中都出现,就可能是普通缺陷。

    需要注意的是,配置问题可以在一大类配置中出现。例如,拨号程序可能在所有的外置Modem中都存在问题,而内置的Modem不会有任何问题。

    49、为什么尽量不要让时间有富裕的员工去做一些测试?

    参考答案:

    表面上看这体现了管理的效率和灵活性,但实际上也体现了管理者对测试的轻视。测试和测试的人有很大关系。测试工作人员应该是勤奋并富有耐心,善于学习、思考和发现问题,细心有条理,总结问题,如果具备这样的优点,做其它工作同样也会很出色,因此这里还有一个要求,就是要喜欢测试这项工作。如果他是专职的,那么肯定更有经验和信心。国内的小伙子好象都喜欢做程序员,两者工作性质不同,待遇不同,地位不同,对自我实现的价值的认识也不同,这是行业的一个需要改善的问题。如果只是为了完成任务而完成任务,或者发现了几个问题就觉得满意了,这在任何其它工作中都是不行的。

    50、完全测试程序是可能的吗?

    参考答案:

    软件测试初学者可能认为拿到软件后需要进行完全测试,找到全部的软件缺陷,使软件“零缺陷”发布。实际上完全测试是不可能的。主要有以下一个原因:

    -完全测试比较耗时,时间上不允许;

    -完全测试通常意味着较多资源投入,这在现实中往往是行不通的;

    -输入量太大,不能一一进行测试;

    -输出结果太多,只能分类进行验证;

    -软件实现途径太多;

    -软件产品说明书没有客观标准,从不同的角度看,软件缺陷的标准不同;

    因此测试的程度要根据实际情况确定。

    51、软件测试的风险主要体现在哪里?

    参考答案:

    我们没有对软件进行完全测试,实际就是选择了风险,因为缺陷极有可能存在没有进行测试的部分。举个例子,程序员为了方便,在调试程序时会弹出一些提示信息框,而这些提示只在某种条件下会弹出,碰巧程序发布前这些代码中的一些没有被注释掉。在测试时测试工程师又没有对其进行测试。如果客户碰到它,这将是代价昂贵的缺陷,因为交付后才被客户发现。

    因此,我们要尽可能的选择最合适的测试量,把风险降低到最小。

    52、发现的缺陷越多,说明软件缺陷越多吗?

    参考答案:

    这是一个比较常见的现象。测试工程师在没有找到缺陷前会绞尽脑汁的思考,但是找到一个后,会接二连三的发现很多缺陷,颇有个人成就感。其中的原因主要如下:

    -代码复用、拷贝代码导致程序员容易犯相同的错误。类的继承导致所有的子类会包含基类的错误,反复拷贝同一代码意味可能也复制了缺陷。

    -程序员比较劳累是可以导致某些连续编写的功能缺陷较多。程序员加班是一种司空见惯的现象,因此体力不只时容易编写一些缺陷较多的程序。而这些连续潜伏缺陷恰恰时测试工程师大显身手的地方。

    “缺陷一个连着一个”不是一个客观规律,只是一个常见的现象。如果软件编写的比较好,这种现象就不常见了。测试人员只要严肃认真的测试程序就可以了。

    53、所有的软件缺陷都能修复吗?所有的软件缺陷都要修复吗?

    参考答案:

    从技术上讲,所有的软件缺陷都是能够修复的,但是没有必要修复所有的软件缺陷。测试人员要做的是能够正确判断什么时候不能追求软件的完美。对于整个项目团队,要做的是对每一个软件缺陷进行取舍,根据风险决定那些缺陷要修复。发生这种现象的主要原因如下:

    -没有足够的时间资源。在任何一个项目中,通常情况下开发人员和测试人员都是不够用的,而且在项目中没有预算足够的回归测试时间,再加上修改缺陷可能引入新的缺陷,因此在交付期限的强大压力下,必须放弃某些缺陷的修改。

    -有些缺陷只是特殊情况下出现,这种缺陷处于商业利益考虑,可以在以后升级中进行修复。

    -不是缺陷的缺陷。我们经常会碰到某些功能方面的问题被当成缺陷来处理,这类问题可以以后有时间时考虑再处理。

    最后要说的是,缺陷是否修改要由软件测试人员、项目经理、程序员共同讨论来决定是否修复,不同角色的人员从不同的角度来思考,以做出正确的决定。

    54、软件测试人员就是QA吗?

    参考答案:

    软件测试人员的职责是尽可能早的找出软件缺陷,确保得以修复。而质量保证人员(QA)主要职责是创建或者制定标准和方法,提高促进软件开发能力和减少软件缺陷。测试人员的主要工作是测试,质量保证人员日常工作重要内容是检查与评审,测试工作也是测试保证人员的工作对象。

    软件测试和质量是相辅相成的关系,都是为了提高软件质量而工作。

    55、如何减少测试人员跳槽带来的损失?

    参考答案:

    在IT行业里跳槽已经是一种司空见惯的现象,而且跳槽无论给公司还是给个人都会带来一定的损失。测试队伍也无疑会面临跳槽的威胁,作为测试经理管理者,只有从日常工作中开始做起,最能最大限度的减少损失。建议我们从以下两个方面做起:

    -加强部门内员工之间的互相学习,互相学习是建立学习型组织的基本要求,是知识互相转移的过程。在此基础上,可以把个人拥有的技术以知识的形式沉积下来,也就完成了隐性知识到显性知识的转化。

    -通常情况下,企业能为员工提供足够大的发展空间时,如果不是待遇特别低,员工都不会主动离开企业。因此我们要想留住员工,管理者就应该把员工的个人成长和企业的发展联系起来,为员工设定合理发展规划并付诸实现。不过这项要求做起来比较,要有比较好的企业文化为依托。

    56、测试产品与测试项目的区别是什么?

    参考答案:

    习惯上把开发完成后进行商业化、几乎不进行代码修改就可以售给用户使用的软件成为软件产品,也就是可以买“卖拷贝”的软件,例如Windows2000。而通常把针对一个或者几个特定的用户而开发的软件成为软件项目,软件项目是一种个性化的产品,可以是按照用户要求全部重新开发,也可以修改已有的软件产品来满足特定的用户需求。项目和产品的不同特点,决定我们测试产品和测试项目仍然会有很多不同的地方:

    -质量要求不同。通常产品的质量要高一些,修复发布后产品的缺陷成本较高,甚至会带来很多负面的影响。而做项目通常面向某一用户,虽然质量越高越好,但是一般只要满足用户要求就可以了。

    -测试资源投入多少不同。做软件产品通常是研发中心来开发,进度压力要小些。同时由于质量要求高,因此会投入较多的人力、物力资源。

    -项目最后要和用户共同验收测试,这是产品测试不具有的特点。

    此外,测试产品与测试项目在缺陷管理方面、测试策略制定都会有很大不同,测试管理者应该结合具体的环境,恰如其分的完成工作。

    57、和用户共同测试(UAT测试)的注意点有哪些?

    参考答案:

    软件产品在投产前,通常都会进行用户验收测试。如果用户验收测试没有通过,直接结果就是那不到“Money”,间接影响是损害了公司的形象,而后者的影响往往更严重。根据作者的经验,用户验收测试一定要让用户满意。

    实际上用户现场测试更趋于是一种演示。在不欺骗用户的前提下,我们向用户展示我们软件的优点,最后让“上帝”满意并欣然掏出“银子”才是我们的目标。因此用户测试要注意下面的事项:

    (1)用户现场测试不可能测试全部功能,因此要测试核心功能。这需要提前做好准备,这些核心功能一定要预先经过测试,证明没有问题才可以和用户共同进行测试。测试核心模块的目的是建立用户对软件的信心。当然如果这些模块如果问题较多,不应该进行演示。

    (2)如果某些模块确实有问题,我们可以演示其它重要的业务功能模块,必要时要向用户做成合理的解释。争得时间后,及时修改缺陷来弥补。

    (3)永远不能欺骗用户,蒙混过关。道理很简单,因为软件是要给用户用的,问题早晚会暴露出来,除非你可以马上修改。

    和用户进行测试还要注意各种交流技巧,争取不但短期利益得到了满足,还要为后面得合作打好基础。

    58、如何编写提交给用户的测试报告?

    参考答案:

    随着测试工作越来越受重视,开发团队向客户提供测试文档是不可避免的事情。很多人会问:“我们可以把工作中的测试报告提供给客户吗?”答案是否定的。因为提供内部测试报告,可能会让客户失去信心,甚至否定项目。

    测试报告一般分为内部测试报告和外部测试报告。内部报告是我们在测试工作中的项目文档,反映了测试工作的实施情况,这里不过多讨论,读者可以参考相关教材。这里主要讨论一下外部测试报告的写法,一般外部测试报告要满足下面几个要求:

    -根据内部测试报告进行编写,一般可以摘录;

    -不可以向客户报告严重缺陷,即使是已经修改的缺陷,开发中的缺陷也没有必要让客户知道;

    -报告上可以列出一些缺陷,但必须是中级的缺陷,而且这些缺陷必须是修复的;

    -报告上面的内容尽量要真实可靠;

    -整个测试报告要仔细审阅,力争不给项目带来负面作用,尤其是性能测试报告。

    总之,外部测试报告要小心谨慎的编写。

    59、测试工具在测试工作中是什么地位?

    参考答案:

    国内的很多测试工程师对测试工具相当迷恋,尤其是一些新手,甚至期望测试工具可以取代手工测试。测试工具在测试工作中起的是辅助作用,一般用来提高测试效率。自动化测试弥补了手工测试的不足,减轻一定的工作量。实际上测试工具是无法替代大多数手工测试的,而一些诸如性能测试等自动化测试也是手工所不能完成的。

    对于自动测试技术,应当依据软件的不同情况来分别对待,一般自动技术会应用在引起大量重复性工作的地方、系统的压力点、以及任何适合使用程序解决大批量输入数据的地方。然后再寻找合适的自动测试工具,或者自己开发测试程序。一定不要为了使用测试工具而使用。

    60、什么是软件测试,软件测试的目的?

    参考答案:

    61、简述负载测试与压力测试的区别。

    参考答案:

        压力测试(Stress Testing)

    压力测试的主要任务就是获取系统正确运行的极限,检查系统在瞬间峰值负荷下正确执行的能力。例如,对服务器做压力测试时就可以增加并发操作的用户数量;或者不停地向服务器发送请求;或一次性向服务器发送特别大的数据等。看看服务器保持正常运行所能达到的最大状态。人们通常使用测试工具来完成压力测试,如模拟上万个用户从终端同时登录,这是压力测试中常常使用的方法。

    负载测试(Volume Testing)

    用于检查系统在使用大量数据的时候正确工作的能力,即检验系统的能力最高能达到什么程度。例如,对于信息检索系统,让它使用频率达到最大;对于多个终端的分时系统,让它所有的终端都开动。在使整个系统的全部资源达到“满负荷”的情形下,测试系统的承受能力。

    62、写出bug报告流转的步骤,每步的责任人及主要完成的工作。

    参考答案:(要结合自己实际的工作经验进行回答,不同公司略有区别)

        测试人员提交新的Bug入库,错误状态为New。

    高级测试员/测试经理验证错误,如果确认是错误,分配给开发组。设置状态为Open。如果不是错误,则拒绝,设置为Declined状态。

    开发经理分配bug至对应的模块开发人员。

    开发人员查询状态为Open的Bug,如果不是错误,则置状态为Declined;如果是Bug则修复并置状态为Fixed。不能解决的Bug,要留下文字说明及保持Bug为Open状态。

    对于不能解决和延期解决的Bug,不能由开发人员自己决定,一般要通过某种会议(评审会)通过才能认可。

    测试人员查询状态为Fixed的Bug,然后验证Bug是否已解决,如解决,置Bug的状态为Closed,如没有解决,置bug状态为Reopen。

    63、写出bug报告当中一些必备的内容。

    参考答案:

           硬件平台和操作系统

           测试应用的硬件平台(Platform),通常选择“PC”。

           测试应用的操作系统平台(OS)。

    a)        版本

           提交缺陷报告时通过该字段标识此缺陷存在于被测试软件的哪个版本。

    b)        Bug报告优先级

    c)         Bug状态

    d)        Bug的编号

    e)         发现人

    f)         提交人

    g)        指定处理人

    h)        概述

    i)          从属关系

    j)         详细描述

    k)        严重程度

    l)          所属模块

    m)      附件

    n)        提交日期

    64、开发人员老是犯一些低级错误怎么解决?

    参考答案:

    这种现象在开发流程不规范的团队里特别常见,尤其是一些“作坊式”的团队里。解决这种问题一般从两个方面入手:

    一方面从开发管理入手,也就是从根源来解决问题。可以制定规范的开发流程,甚至可以制定惩罚制度,还有就是软件开发前做好规划设计。

    另一方面就是加强测试,具体做法就是加强开发人员的自己测试,把这些问题“消灭”在开发阶段,这是比较好的做法,读者可以参考第13章试案例分析的“13.1.2缺陷反复出现,谁的责任”小节,13.1.2专门讨论了这类问题的方法。

    此外,还可以通过规范的缺陷管理来对开发人员进行控制,比如测试部门整理出常见的缺陷,让开发人员自己对照进行检查,以减少这类低级错误的发生。

    开发人员犯错误是正常的现象,作为测试人员一定不能抱怨,要认认真真的解决问题才是上策。

    65、画出软件测试的V模型图。

      参考答案:

            

    66、为什么要在一个团队中开展软件测试工作?

    参考答案:

    因为没有经过测试的软件很难在发布之前知道该软件的质量,就好比ISO质量认证一样,测试同样也需要质量的保证,这个时候就需要在团队中开展软件测试的工作。在测试的过程发现软件中存在的问题,及时让开发人员得知并修改问题,在即将发布时,从测试报告中得出软件的质量情况。

    67、您在以往的测试工作中都曾经具体从事过哪些工作?其中最擅长哪部分工作?

    参考答案:(根据项目经验不同,灵活回答即可)

    我曾经做过web测试,后台测试,客户端软件,其中包括功能测试性能测试,用户体验测试。最擅长的是功能测试

    68、您所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)

    参考答案:

    测试类型有:功能测试,性能测试,界面测试。

      功能测试在测试工作中占的比例最大,功能测试也叫黑盒测试。是把测试对象看作一个黑盒子。利用黑盒测试法进行动态测试时,需要测试软件产品的功能,不需测试软件产品的内部结构和处理过程。采用黑盒技术设计测试用例的方法有:等价类划分、边界值分析、错误推测、因果图和综合策略。

      性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。负载测试和压力测试都属于性能测试,两者可以结合进行。通过负载测试,确定在各种工作负载下系统的性能,目标是测试当负载逐渐增加时,系统各项性能指标的变化情况。压力测试是通过确定一个系统的瓶颈或者不能接收的性能点,来获得系统能提供的最大服务级别的测试。

      界面测试,界面是软件与用户交互的最直接的层,界面的好坏决定用户对软件的第一印象。而且设计良好的界面能够引导用户自己完成相应的操作,起到向导的作用。同时界面如同人的面孔,具有吸引用户的直接优势。设计合理的界面能给用户带来轻松愉悦的感受和成功的感觉,相反由于界面设计的失败,让用户有挫败感,再实用强大的功能都可能在用户的畏惧与放弃中付诸东流。

      区别在于,功能测试关注产品的所有功能上,要考虑到每个细节功能,每个可能存在的功能问题。性能测试主要关注于产品整体的多用户并发下的稳定性和健壮性。界面测试更关注于用户体验上,用户使用该产品的时候是否易用,是否易懂,是否规范(快捷键之类的),是否美观(能否吸引用户的注意力),是否安全(尽量在前台避免用户无意输入无效的数据,当然考虑到体验性,不能太粗鲁的弹出警告)?做某个性能测试的时候,首先它可能是个功能点,首先要保证它的功能是没问题的,然后再考虑该功能点的性能测试

    69、您认为做好测试用例设计工作的关键是什么?

    参考答案:

           白盒测试用例设计的关键是以较少的用例覆盖尽可能多的内部程序逻辑结果

    黑盒法用例设计的关键同样也是以较少的用例覆盖模块输出和输入接口。不可能做到完全测试,以最少的用例在合理的时间内发现最多的问题

    70、请试着比较一下黑盒测试、白盒测试、单元测试、集成测试、系统测试、验收测试的区别与联系。

    参考答案:

           黑盒测试:已知产品的功能设计规格,可以进行测试证明每个实现了的功能是否符合要求。

      白盒测试:已知产品的内部工作过程,可以通过测试证明每种内部操作是否符合设计规格要求,所有内部成分是否以经过检查。

      软件的黑盒测试意味着测试要在软件的接口处进行。这种方法是把测试对象看做一个黑盒子,测试人员完全不考虑程序内部的逻辑结构和内部特性,只依据程序的需求规格说明书,检查程序的功能是否符合它的功能说明。因此黑盒测试又叫功能测试或数据驱动测试。黑盒测试主要是为了发现以下几类错误:

      1、是否有不正确或遗漏的功能?

      2、在接口上,输入是否能正确的接受?能否输出正确的结果?

      3、是否有数据结构错误或外部信息(例如数据文件)访问错误?

      4、性能上是否能够满足要求?

      5、是否有初始化或终止性错误?

      软件的白盒测试是对软件的过程性细节做细致的检查。这种方法是把测试对象看做一个打开的盒子,它允许测试人员利用程序内部的逻辑结构及有关信息,设计或选择测试用例,对程序所有逻辑路径进行测试。通过在不同点检查程序状态,确定实际状态是否与预期的状态一致。因此白盒测试又称为结构测试或逻辑驱动测试。白盒测试主要是想对程序模块进行如下检查:

      1、对程序模块的所有独立的执行路径至少测试一遍。

      2、对所有的逻辑判定,取“真”与取“假”的两种情况都能至少测一遍。

      3、在循环的边界和运行的界限内执行循环体。

      4、测试内部数据结构的有效性,等等。

      单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。

      单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。

      集成测试(也叫组装测试,联合测试)是单元测试的逻辑扩展。它的最简单的形式是:两个已经测试过的单元组合成一个组件,并且测试它们之间的接口。从这一层意义上讲,组件是指多个单元的集成聚合。在现实方案中,许多单元组合成组件,而这些组件又聚合成程序的更大部分。方法是测试片段的组合,并最终扩展进程,将您的模块与其他组的模块一起测试。最后,将构成进程的所有模块一起测试。

      系统测试是将经过测试的子系统装配成一个完整系统来测试。它是检验系统是否确实能提供系统方案说明书中指定功能的有效方法。(常见的联调测试)

      系统测试的目的是对最终软件系统进行全面的测试,确保最终软件系统满足产品需求并且遵循系统设计。

      验收测试是部署软件之前的最后一个测试操作。验收测试的目的是确保软件准备就绪,并且可以让最终用户将其用于执行软件的既定功能和任务。

    验收测试是向未来的用户表明系统能够像预定要求那样工作。经集成测试后,已经按照设计把所有的模块组装成一个完整的软件系统,接口错误也已经基本排除了,接着就应该进一步验证软件的有效性,这就是验收测试的任务,即软件的功能和性能如同用户所合理期待的那样。

    71、测试计划工作的目的是什么?测试计划工作的内容都包括什么?其中哪些是最重要的?

    参考答案:

           软件测试计划是指导测试过程的纲领性文件,包含了产品概述、测试策略、测试方法、测试区域、测试配置、测试周期、测试资源、测试交流、风险分析等内容。借助软件测试计划,参与测试的项目成员,尤其是测试管理人员,可以明确测试任务和测试方法,保持测试实施过程的顺畅沟通,跟踪和控制测试进度,应对测试过程中的各种变更。

    测试计划和测试详细规格、测试用例之间是战略和战术的关系,测试计划主要从宏观上规划测试活动的范围、方法和资源配置,而测试详细规格、测试用例是完成测试任务的具体战术。所以其中最重要的是测试测试策略和测试方法(最好是能先评审)

    72、您所熟悉的测试用例设计方法都有哪些?请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。

    参考答案:

           1.等价类划分

      划分等价类: 等价类是指某个输入域的子集合.在该子集合中,各个输入数据对于揭露程序中的错误都是等效的.并合理地假定:测试某等价类的代表值就等于对这一类其它值的测试.因此,可以把全部输入数据合理划分为若干等价类,在每一个等价类中取一个数据作为测试的输入条件,就可以用少量代表性的测试数据.取得较好的测试结果.等价类划分可有两种不同的情况:有效等价类和无效等价类.

      2.边界值分析法

      边界值分析方法是对等价类划分方法的补充。测试工作经验告诉我,大量的错误是发生在输入或输出范围的边界上,而不是发生在输入输出范围的内部.因此针对各种边界情况设计测试用例,可以查出更多的错误.

      使用边界值分析方法设计测试用例,首先应确定边界情况.通常输入和输出等价类的边界,就是应着重测试的边界情况.应当选取正好等于,刚刚大于或刚刚小于边界的值作为测试数据,而不是选取等价类中的典型值或任意值作为测试数据.

        3.错误推测法

      基于经验和直觉推测程序中所有可能存在的各种错误, 从而有针对性的设计测试用例的方法.

      错误推测方法的基本思想: 列举出程序中所有可能有的错误和容易发生错误的特殊情况,根据他们选择测试用例. 例如, 在单元测试时曾列出的许多在模块中常见的错误. 以前产品测试中曾经发现的错误等, 这些就是经验的总结. 还有, 输入数据和输出数据为0的情况. 输入表格为空格或输入表格只有一行. 这些都是容易发生错误的情况. 可选择这些情况下的例子作为测试用例.

        4.因果图方法

      前面介绍的等价类划分方法和边界值分析方法,都是着重考虑输入条件,但未考虑输入条件之间的联系, 相互组合等. 考虑输入条件之间的相互组合,可能会产生一些新的情况. 但要检查输入条件的组合不是一件容易的事情, 即使把所有输入条件划分成等价类,他们之间的组合情况也相当多. 因此必须考虑采用一种适合于描述对于多种条件的组合,相应产生多个动作的形式来考虑设计测试用例. 这就需要利用因果图(逻辑模型). 因果图方法最终生成的就是判定表. 它适合于检查程序输入条件的各种组合情况.

    73、请以您以往的实际工作为例,详细的描述一次测试用例设计的完整的过程。

    参考答案:

           就说最近的这次网站功能的测试吧

      首先:得到相关文档(需求文档和设计文档),理解需求和设计设计思想后,想好测试策略(测试计划简单点就OK了),考虑到测试环境,测试用例,测试时间等问题。

      第二步:设计测试用例,测试策略是:把网站部分的功能点测试完,然后在进行系统测试(另外个模块呢有另一个测试人员负责,可以进行联调测试),网站模块的测试基本是功能测试和界面测试(用户并发的可能性很小,所以不考虑):这次的网站的输入数据呢是使用数据库中的某张表记录,如果表中某一数据记录中新加进来的(还没有被处理的,有个标志位),网站启动后会立刻去刷那张表,得到多条数据,然后在进行处理。处理过程中,会经历3个步骤,网站才算完成了它的任务。有3个步骤呢,就可以分别对  这3个步骤进行测试用例的设计,尽量覆盖到各种输入情况(包括数据库中的数据,用户的输入等),得出了差不多50个用例。界面测试,也就是用户看的到的地方,包括发送的邮件和用户填写资料的页面展示。

      第三步:搭建测试环境(为什么这个时候考虑测试环境呢?因为我对网站环境已经很熟了,只有有机器能空于下来做该功能测试就可以做了),因为网站本身的环境搭建和其他的系统有点不同,它需要的测试环境比较麻烦,需要web服务器(Apache,tomcat),不过这次需求呢,网站部分只用到了tomcat,所以只要有tomcat即可

      第四步:执行测试

    74、您以往是否曾经从事过性能测试工作?如果有,请尽可能的详细描述您以往的性能测试工作的完整过程。

    参考答案:(以自己最熟悉的性能测试项目为例)

           是的,曾经做过网站方面的性能测试,虽然做的时间并不久(2个月吧),当时呢,是有位网站性能测试经验非常丰富的前辈带着我一起做。

    性能测试类型包括负载测试,强度测试,容量测试等

      负载测试:负载测试是一种性能测试指数据在超负荷环境中运行,程序是否能够承担。

      强度测试:强度测试是一种性能测试,他在系统资源特别低的情况下软件系统运行情况

      容量测试:确定系统可处理同时在线的最大用户数   

      在网站流量逐渐加大的情况下,开始考虑做性能测试了,首先要写好性能测试计划,根据运营数据得出流量最大的页面(如果是第一次的话,一般是首页,下载页,个人帐户页流量最大,而且以某种百分比),

      Web服务器指标指标:

      * Avg Rps: 平均每秒钟响应次数=总请求时间 / 秒数;

      * Successful Rounds:成功的请求;

      * Failed Rounds :失败的请求;

      * Successful Hits :成功的点击次数;

      * Failed Hits :失败的点击次数;

      * Hits Per Second :每秒点击次数;

      * Successful Hits Per Second :每秒成功的点击次数;

      * Failed Hits Per Second :每秒失败的点击次数;

      * Attempted Connections :尝试链接数; 

    75、你对测试最大的兴趣在哪里?为什么?

    参考答案:

           最大的兴趣就是测试有难度,有挑战性!做测试越久越能感觉到做好测试有多难。曾经在无忧测试网上看到一篇文章,是关于如何做好一名测试工程师。一共罗列了11,12点,有部分是和人的性格有关,有部分需要后天的努力。但除了性格有关的1,2点我没有把握,其他点我都很有信心做好它。

      刚开始进入测试行业时,对测试的认识是从无忧测试网上了解到的一些资料,当时是冲着做测试需要很多技能才能做的好,虽然入门容易,但做好很难,比开发更难,虽然当时我很想做开发(学校专业课我基本上不缺席,因为我喜欢我的专业),但看到测试比开发更难更有挑战性,想做好测试的意志就更坚定了。

      不到一年半的测试工作中,当时的感动和热情没有减退一点(即使环境问题以及自身经验,技术的不足,做测试的你一定也能理解)。

      我觉得做测试整个过程中有2点让我觉得很有难度(对我来说,有难度的东西我就非常感兴趣),第一是测试用例的设计,因为测试的精华就在测试用例的设计上了,要在版本出来之前,把用例写好,用什么测试方法写?(也就是测试计划或测试策略),如果你刚测试一个新任务时,你得花一定的时间去消化业务需求和技术基础,业务需求很好理解(多和产品经理和开发人员沟通就能达到目的),而技术基础可就没那么简单了,这需要你自觉的学习能力,比如说网站吧,最基本的技术知识你要知道网站内部是怎么运作的的,后台是怎么响应用户请求的?测试环境如何搭建?这些都需要最早的学好。至少在开始测试之前能做好基本的准备,可能会遇到什么难题?需求细节是不是没有确定好?这些问题都能在设计用例的时候发现。

      第二是发现BUG的时候了,这应该是测试人员最基本的任务了,一般按测试用例开始测试就能发现大部分的bug,还有一部分bug需要测试的过程中更了解所测版本的情况获得更多信息,补充测试用例,测试出bug。还有如何发现bug?这就需要在测试用例有效的情况下,通过细心和耐心去发现bug了,每个用例都有可能发现bug,每个地方都有可能出错,所以测试过程中思维要清晰(测试过程数据流及结果都得看仔细了,bug都在里面发现的)。如何描述bug也很有讲究,bug在什么情况下会产生,如果条件变化一点点,就不会有这个bug,以哪些最少的操作步骤就能重现这个bug,这个bug产生的规律是什么?如果你够厉害的话,可以帮开发人员初步定位问题。

    76、你以前工作时的测试流程是什么?

    参考答案:(灵活回答)

    公司对测试流程没有规定如何做,但每个测试人员都有自己的一套测试流程。我说下我1年来不断改正(自己总结,吸取同行的方法)后的流程吧。需求评审(有开发人员,产品经理,测试人员,项目经理)->需求确定(出一份确定的需求文档)->开发设计文档(开发人员在开始写代码前就能输出设计文档)->想好测试策略,写出测试用例->发给开发人员和测试经理看看(非正式的评审用例)->接到测试版本->执行测试用例(中间可能会补充用例)->提交bug(有些bug需要开发人员的确定(严重级别的,或突然发现的在测试用例范围之外的,难以重现的),有些可以直接录制进TD)->开发人员修改(可以在测试过程中快速的修改)->回归测试(可能又会发现新问题,再按流程开始跑)。

    77、当开发人员说不是BUG时,你如何应付?

    参考答案:

      开发人员说不是bug,有2种情况,一是需求没有确定,所以我可以这么做,这个时候可以找来产品经理进行确认,需不需要改动,3方商量确定好后再看要不要改。二是这种情况不可能发生,所以不需要修改,这个时候,我可以先尽可能的说出是BUG的依据是什么?如果被用户发现或出了问题,会有什么不良结果?程序员可能会给你很多理由,你可以对他的解释进行反驳。如果还是不行,那我可以给这个问题提出来,跟开发经理和测试经理进行确认,如果要修改就改,如果不要修改就不改。其实有些真的不是bug,我也只是建议的方式写进TD中,如果开发人员不修改也没有大问题。如果确定是bug的话,一定要坚持自己的立场,让问题得到最后的确认。

    78、软件的构造号与版本号之间的区别?BVT(BuildVerificationTest)

    参考答案:版本控制命名格式: 主版本号.子版本号[.修正版本号[.编译版本号 ]]

    Major.Minor [.Revision[.Build]]

          应根据下面的约定使用这些部分:

    Major :具有相同名称但不同主版本号的程序集不可互换。例如,这适用于对产品的大量重写,这些重写使得无法实现向后兼容性。

    Minor :如果两个程序集的名称和主版本号相同,而次版本号不同,这指示显著增强,但照顾到了向后兼容性。例如,这适用于产品的修正版或完全向后兼容的新版本。

    Build :内部版本号的不同表示对相同源所作的重新编译。这适合于更改处理器、平台或编译器的情况。

    Revision :名称、主版本号和次版本号都相同但修订号不同的程序集应是完全可互换的。这适用于修复以前发布的程序集中的安全漏洞。

    BVT(BuildVerificationTest):

    作为Build的一部分,主要是通过对基本功能、特别是关键功能的测试,保证新增代码没有导致功能失效,保证版本的持续稳定。实现BVT方式是有以下几种:1、测试人员手工验证关键功能实现的正确性。特点:这是传统开发方法中,通常采用的方式。无需维护测试脚本的成本,在测试人力资源充足,测试人员熟悉业务、并对系统操作熟练情况下效率很高,比较灵活快速。缺点:人力成本较高;对测试人员能力有一定要求;测试人员面对重复的工作,容易产生疲倦懈怠,从而影响测试质量。2、借助基于GUI的自动化功能测试工具来完成,将各基本功能操作录制成测试脚本,每次回放测试脚本验证功能实现的正确性。特点:能够模拟用户操作完成自动的测试,从UI入口到业务实现,每一层的代码实现都经过验证;节约人力成本;降低测试人员重复劳动的工作量,机器不会疲倦;缺点:对于UI变动比较频繁的系统来说,这种方式的维护成本很高,实施起来非常困难。另外,在项目周期较短且后续无延续性或继承的情况下,也不推荐使用此方式。3、由开发人员通过自动化测试工具完成业务层的BVT测试。特点:通过对业务层关键功能的持续集成测试,保证系统功能的持续稳定。可以结合DailyBuild,做为Build的一部分,自动实现并输入BVT报告。缺点:仅对业务规则实现的正确性进行了测试,对表现层无法测试到,对于诸如:前台页面控件各种事件响应、页面元素变化等方面的问题无法保证。

    79、您以往的工作中,一条软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?

    参考答案:

    80、您以往所从事的软件测试工作中,是否使用了一些工具来进行软件缺陷(Bug)的管理?如果有,请结合该工具描述软件缺陷(Bug)跟踪管理的流程。

    参考答案:

    81、您认为性能测试工作的目的是什么?做好性能测试工作的关键是什么?

    参考答案:

    82、单元测试、集成测试、系统测试的侧重点是什么?

           参考答案:

    83、集成测试通常都有那些策略?

    参考答案:

    84、一个缺陷测试报告的组成

    参考答案:

    85、基于WEB信息管理系统测试时应考虑的因素有哪些?

    参考答案:

    86、软件测试项目从什么时候开始,?为什么?

    参考答案:

    87、需求测试注意事项有哪些?

    参考答案:

    88、简述一下缺陷的生命周期

    参考答案:

    89、你在你所在的公司是怎么开展测试工作的?是如何组织的?

    参考答案:

    90、你认为理想的测试流程是什么样子?

    参考答案:

    91、您在从事性能测试工作时,是否使用过一些测试工具?如果有,请试述该工具的工作原理,并以一个具体的工作中的例子描述该工具是如何在实际工作中应用的。

    参考答案:        

    92、软件测试活动的生命周期是什么?

    参考答案:

    93、请画出软件测试活动的流程图?

    参考答案:

    94、针对缺陷采取怎样管理措施?

    参考答案:

    95、什么是测试评估?测试评估的范围是什么?

    参考答案:

    96、如果能够执行完美的黑盒测试,还需要进行白盒测试吗?为什么?

    参考答案:

    97、测试结束的标准是什么?

    参考答案:

    98、软件验收测试除了alpha ,beta测试以外,还有哪一种?

    参考答案:

    99、做测试多久了?以前做过哪些项目?你们以前测试的流程是怎样的?用过哪些测试工具?

    参考答案:

    100、请就如何在开发中进行软件质量控制说说你的看法

    参考答案:

    101、一套完整的测试应该由哪些阶段组成?分别阐述一下各个阶段。

    102、软件测试的类型有那些?分别比较这些不同的测试类型的区别与联系。

    103、测试用例通常包括那些内容?着重阐述编制测试用例的具体做法

    104、在分别测试winform的C/S结构与测试WEB结构的软件是,应该采取什么样的方法分别测试?他们存在什么样的区别与联系?

    105、在测试winform的C/S结构软件时,发现这个软件的运行速度很慢,您会认为是什么原因?您会采取哪些方法去检查这个原因?

    106、描述使用bugzilla缺陷管理工具对软件缺陷(BUG)跟踪的管理的流程

    107、你都用什么测试方法

    针对不同的产品或者系统或者模块,有不同的测试方法。总体而言有白盒测试和黑盒测试。

    108、怎么编写案例

    案例的编写与测试阶段的定义有很大的关系。系统测试和unit测试的案例可能不同。总体而言测试案例根据系统的需求而定。

    109、怎么才能够全面的测试到每一个点

    测试的全面性主要需要在设计测试计划的时候考虑,从测试策略,产品需求等等多个角度考虑从而定义全部的测试点。

    110、谈谈软件测试技术,以及如何提高

    111、谈谈软件测试职业发展,以及个人的打算

    112、谈谈软件测试在企业的地位,也可以结合软件生命周期来谈

    113、一般公司里实际的软件测试流程是什么样的?你们公司又是怎样的?

    114、软件工程师要具有那些素质?

    115、你会哪些测试工具?怎么操作?

    116、你能不能说下你的3到5年的职业计划(规划)

    117、你觉得你来应聘有那些优势?

    其他问题:(有可能清晰的思路比确切的答案更重要)

    对测试的理解——考查点:基本的测试知识,对测试是否认可

    谈一谈过去自己的工作——考查点:了解经历、提供进一步提问的素材,表达能力、测试技能

    测试设计的方法并举例说明——考查点:测试技术的使用

    测试工具——考查点:熟悉程度,能否与当前工作匹配?

    如何做计划?如何跟踪计划?——考查点:日常工作能力

    如果开发人员提供的版本不满足测试的条件,如何做?——考查点:与开发人员协作的能力

    熟悉unix系统、oracle数据库吗?——考查点:是否具备系统知识

    做过开发吗?写过哪些代码?——考查点:开发技能

    阅读英语文章,给出理解说明?——考查点:部分英语能力

    文档的意义——考查点:是否善于思考?(最简单的概念,不同层次的理解)

    假如进入我们公司,对我们哪些方面会有帮助?——考查点:讲讲自己的特长

    随便找一件物品,让其测试——考查点:测试的实际操作能力

    有一个新的软件,假如你是测试工程师,该如何做——考查点:实际项目经验、是否有带领测试团队的经验和潜力

    开发及环境搭建类面试题

    1、描述软件产生内存泄露的原因以及检查方式。(可以结合一种开发语言进行描述)

    参考答案:

    内存泄露的原因,主要是由于开发过程当中申请了计算机资源(例如对象、内存等),但是使用资源完成以后没有及时释放资源导致的。例如在C语言当中使用了malloc申请了内存,但是未使用free来释放内存。

    2、简述什么是值传递,什么是地址传递,两者区别是什么?

    参考答案:

    值传递主调函数传递给被调函数的是值的拷贝,不是原值;地址传递主调函数传递给被调函数的是值的地址。区别是值传递被调函数中的操作不改变主调函数的值,而地址传递则不同。

    3、结构化程序设计和面向对象程序设计各自的特点及优缺点是什么?

    参考答案:(不需要回答如此复杂)

    结构化程序设计思想采用了模块分解与功能抽象和自顶向下、分而治之的方法,从而有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子程序,便于开发和维护。它的重点在于把功能进行分解。但是由于在实际开发过程当中需求会经常发生变化,因此,它不能很好的适应需求变化的开发过程。结构化程序设计是面向过程的。

    面向对象程序设计以需求当中的数据作为中心,来进行设计,具有良好的代码重用性。

    封装性:也叫数据隐藏,用户无需知道内部工作流程,只要知道接口和操作就可以的,C++中一般用类来实现封装。

    继承性:一种支持重用的思想,在现有的类型派生出新的子类,例如新型电视机在原有型号的电视机上增加若干中功能而得到,新型电视机是原有电视机的派生,继承了原有电视机的属性,并增加了新的功能。

    多态性:指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。

    动态联编:指一个计算机程序自身彼此关联的过程,按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。

    4、简述什么是存储过程和触发器?

    参考答案:

    存储过程:是数据库中的一个对象,Transact-SQL 语句的预编译集合,这些语句在一个名称下存储并作为一个单元进行处理。(可以理解为C语言中的函数,有参数、返回值等函数特性)

    触发器是一种特殊类型的存储过程,当使用下面的一种或多种数据修改操作在指定表中对数据进行修改时,触发器会生效:UPDATE、INSERT 或 DELETE。

    5、使用C语言编写一个函数,用于交换两个变量的值(地址传递)。

           参考答案:

           void Swap(int *a,int *b)

    {

                  int temp;

                  int temp=*a;

                  int *a=*b;

                  int *b=temp;

    }

    6、请简述DNS、活动目录、域的概念。

    参考答案:

    DNS:域名服务,作用是将网络域名解析成IP地址;

    活动目录:微软提供的目录服务的一种,它存储有关网络上的对象信息,并使管理员和用户更方便的查找和使用这类信息;

    域:网络系统的一个安全边界,在一个域当中,计算机和用户共享一些列的安全信息。

    7、描述TCP/IP协议的层次结构,以及每一层中重要协议。

    参考答案:(可以回答五层结构)

    TCP/IP

    协议

    应用层/Application

    HTTP、SMTP、FTP

    传输层/Transport

    TCP、UDP

    网络层/Network

    IP

    链路层/Link

    ARP、RARP

          

     

     

     

     

     

    8、简述子网掩码的用途。

    参考答案:

        子网掩码主要用来判断两个IP地址是否处在同一个局域网当中;子网掩码是由连续的2进制1组成的。子网掩码和IP地址进行按位与运算后,结果一致,表示处于一个局域网当中,如果不一致,表示不再一个局域网当中,需要寻找路由。

    9、说出4种以上常用的操作系统及其主要的应用范围(微软的操作系统除外)。

    参考答案:

    Linux(Red Hat、SUSE、Debian、Trubo Linux):主要用于搭建各类服务器

    MAC OS:苹果机的操作系统,用于图像处理

    Unix(AIX:IBM服务器的专用操作系统;

    Solaris:Sun操作系统;FreeBSD、NetBSD)

    10、在Linux系统中,一个文件的访问权限是755,其含义是什么?

    参考答案:

           755表示该文件所有者对该文件具有读、写、执行权限,该文件所有者所在组用户及其他用户对该文件具有读和执行权限。

    11、Windows操作系统中PATH环境变量的作用是什么?

    参考答案:

           PATH是Windows操作系统环境变量,PATH作用是用户在命令行窗口执行一个命令,则在PATH变量设置的目录下依次寻找该命令或对应的执行文件,若找到,则执行,若没有找到,则命令行窗口返回无效命令。

    12、Ghost的主要用途和常用方法?

    参考答案:

    Ghost是一个非常著名的硬盘克隆工具。该工具的主要作用是可以将一个硬盘或硬盘中的某个分区原封不动的复制到另一个硬盘或其他的分区中。如果你需要备份启动分区或者是需要在多台机器上安装相应的系统和应用程序,都可以通过Ghost来实现,相信通过这个工具备份,恢复速度和硬盘安装速度会成倍的提高。

    Norton Ghost有一个很大的特点,就是在克隆硬盘时不会改变任何文件信息,程序可以很好的支持FAT16、FAT32以及NTFS格式的文件分配结构(其中包括Windows 2000的文件分配格式),虽然是DOS环境下运行的程序,但工具可支持Win 9x的长文件名特性。

    常用方法包括:硬盘克隆、分区克隆、硬盘或分区克隆成镜像文件等。

    13、在RedHat中,从root用户切到userl用户,一般用什么命令?

    参考答案:su

    su user1  切换到user1,但切换后的当前目录还是root访问的目录

    su – user1 切换到user1,并且当前目录切换到user1的根目录下(/home/user1/)

    14、Linux中,一般怎么隐藏文件?

    参考答案:文件名以一个.开头

    15、如何将自己的本地磁盘(D)做成FTP供远端主机使用?

    参考答案:Windows下安装FTP服务,并将FTP的根目录指向D盘即可。

    16、对RUP.CMM,CMMI,XP,PSP.TSP的认识?

    参考答案:软件过程标准:CMMI、PSP、TSP、RUP、软件工程规范国家标准;(AP、XP、ASD等开发过程思想好像还不能称其为标准)

    RUP(Rational Unified Process)是Rational公司提出的一套开发过程模型,它是一个面向对象软件工程的通用业务流程。它描述了一系列相关的软件工程流程,它们具有相同的结构,即相同的流程构架。RUP 为在开发组织中分配任务和职责提供了一种规范方法,其目标是确保在可预计的时间安排和预算内开发出满足最终用户需求的高品质的软件。RUP具有两个轴,一个轴是时间轴,这是动态的。另一个轴是工作流轴,这是静态的。在时间轴上,RUP划分了四个阶段:初始阶段、细化阶段、构造阶段和发布阶段。每个阶段都使用了迭代的概念。在工作流轴上,RUP设计了六个核心工作流程和三个核心支撑工作流程,核心工作流轴包括:业务建模工作流、需求工作流、分析设计工作流、实现工作流、测试工作流和发布工作流。核心支撑工作流包括:环境工作流、项目管理工作流和配置与变更管理工作流。RUP 汇集现代软件开发中多方面的最佳经验,并为适应各种项目及组织的需要提供了灵活的形式。作为一个商业模型,它具有非常详细的过程指导和模板。但是同样由于该模型比较复杂,因此在模型的掌握上需要花费比较大的成本。尤其对项目管理者提出了比较高的要求。

    CMM(Capability Maturity Model能力成熟度模型) 由美国卡内基-梅隆大学的软件工程研究所(简称SEI)受美国国防部委托,于1991年研究制定,初始的主要目的是为了评价美国国防部的软件合同承包组织的能力,后因为在软件企业应用CMM模型实施过程改进取得较大的成功,所以在全世界范围内被广泛使用,SEI同时建立了主任评估师评估制度,CMM的评估方法为CBA-IPI。CMM的本质是软件管理工程的一个部分。它是对于软件组织在定义,实现,度量,控制和改善其软件过程的进程中各个发展阶段的描述。他通过5个不断进化的层次来评定软件生产的历史与现状:初始层是混沌的过程;可重复层是经过训练的软件过程;定义层是标准一致的软件过程;管理层是可预测的软件过程;优化层是能持续改善的软件过程。

    CMM/PSP/TSP即软件能力成熟度模型/ 个体软件过程/群组软件过程,是1987年美国 Carnegie Mellon 大学软件工程研究所(CMU/SEI)以W.S.Humphrey为首的研究组发表的研究成果"承制方软件工程能力的评估方法"。

    CMMI是SEI于2000年发布的CMM的新版本。CMMI不但包括了软件开发过程改进,还包含系统集成、软硬件采购等方面的过程改进内容。

    CMMI纠正了CMM存在的一些缺点,使其更加适用企业的过程改进实施。CMMI适用SCAMPI评估方法。需要注意的是,SEI没有废除CMM模型,只是停止了CMM评估方法:CBA-IPI。现在如要进行CMM评估,需使用SCAMPI方法。但CMMI模型最终代替CMM模型的趋势不可避免。

    XP (极限编程)规定了一组核心价值和方法,可以让软件开发人员发挥他们的专长:编写代码。XP 消除了大多数重量型过程的不必要产物,通过减慢开发速度、耗费开发人员的精力(例如干特图、状态报告,以及多卷需求文档)从目标偏离。

    XP 的核心价值:交流、简单、反馈、勇气。

    17、DNS是什么,它是如何工作的?

    参考答案:域名解析服务。用于将域名解析为IP,或反和将IP解析为域名。

    客户机可指定DNS服务器来解析,或用本机hosts文件进行解析。

    Windows下配置DNS服务器在《搭建Windows测试环境》中有。

    18、防火墙如何保证安全的?主要有哪些?

    参考答案:防火墙分类1

    从防火墙的软、硬件形式来分的话,防火墙可以分为软件防火墙和硬件防火墙以及芯片级防火墙。

    第一种:软件防火墙

    软件防火墙运行于特定的计算机上,它需要客户预先安装好的计算机操作系统的支持,一般来说这台计算机就是整个网络的网关。俗称“个人防火墙”。软件防火墙就像其它的软件产品一样需要先在计算机上安装并做好配置才可以使用。防火墙厂商中做网络版软件防火墙最出名的莫过于Checkpoint。使用这类防火墙,需要网管对所工作的操作系统平台比较熟悉。

    第二种:硬件防火墙

    这里说的硬件防火墙是指“所谓的硬件防火墙”。之所以加上"所谓"二字是针对芯片级防火墙说的了。它们最大的差别在于是否基于专用的硬件平台。目前市场上大多数防火墙都是这种所谓的硬件防火墙,他们都基于PC架构,就是说,它们和普通的家庭用的PC没有太大区别。在这些PC架构计算机上运行一些经过裁剪和简化的操作系统,最常用的有老版本的Unix、Linux和FreeBSD系统。值得注意的是,由于此类防火墙采用的依然是别人的内核,因此依然会受到OS(操作系统)本身的安全性影响。

    传统硬件防火墙一般至少应具备三个端口,分别接内网,外网和DMZ区(非军事化区),现在一些新的硬件防火墙往往扩展了端口,常见四端口防火墙一般将第四个端口做为配置口、管理端口。很多防火墙还可以进一步扩展端口数目。

    第三种:芯片级防火墙

    芯片级防火墙基于专门的硬件平台,没有操作系统。专有的ASIC芯片促使它们比其他种类的防火墙速度更快,处理能力更强,性能更高。做这类防火墙最出名的厂商有NetScreen、FortiNet、Cisco等。这类防火墙由于是专用OS(操作系统),因此防火墙本身的漏洞比较少,不过价格相对比较高昂。

    防火墙技术虽然出现了许多,但总体来讲可分为“包过滤型”和“应用代理型”两大类。前者以以色列的Checkpoint防火墙和美国Cisco公司的PIX防火墙为代表,后者以美国NAI公司的Gauntlet防火墙为代表。

    (1). 包过滤(Packet filtering)型

    包过滤型防火墙工作在OSI网络参考模型的网络层和传输层,它根据数据包头源地址,目的地址、端口号和协议类型等标志确定是否允许通过。只有满足过滤条件的数据包才被转发到相应的目的地,其余数据包则被从数据流中丢弃。

    包过滤方式是一种通用、廉价和有效的安全手段。之所以通用,是因为它不是针对各个具体的网络服务采取特殊的处理方式,适用于所有网络服务;之所以廉价,是因为大多数路由器都提供数据包过滤功能,所以这类防火墙多数是由路由器集成的;之所以有效,是因为它能很大程度上满足了绝大多数企业安全要求。

    在整个防火墙技术的发展过程中,包过滤技术出现了两种不同版本,称为“第一代静态包过滤”和“第二代动态包过滤”。

    ●第一代静态包过滤类型防火墙

    这类防火墙几乎是与路由器同时产生的,它是根据定义好的过滤规则审查每个数据包,以便确定其是否与某一条包过滤规则匹配。过滤规则基于数据包的报头信息进行制订。报头信息中包括IP源地址、IP目标地址、传输协议(TCP、UDP、ICMP等等)、TCP/UDP目标端口、ICMP消息类型等。

    ●第二代动态包过滤类型防火墙

    这类防火墙采用动态设置包过滤规则的方法,避免了静态包过滤所具有的问题。这种技术后来发展成为包状态监测(Stateful Inspection)技术。采用这种技术的防火墙对通过其建立的每一个连接都进行跟踪,并且根据需要可动态地在过滤规则中增加或更新条目。

    包过滤方式的优点是不用改动客户机和主机上的应用程序,因为它工作在网络层和传输层,与应用层无关。但其弱点也是明显的:过滤判别的依据只是网络层和传输层的有限信息,因而各种安全要求不可能充分满足;在许多过滤器中,过滤规则的数目是有限制的,且随着规则数目的增加,性能会受到很大地影响;由于缺少上下文关联信息,不能有效地过滤如UDP、RPC(远程过程调用)一类的协议;另外,大多数过滤器中缺少审计和报警机制,它只能依据包头信息,而不能对用户身份进行验证,很容易受到“地址欺骗型”攻击。对安全管理人员素质要求高,建立安全规则时,必须对协议本身及其在不同应用程序中的作用有较深入的理解。因此,过滤器通常是和应用网关配合使用,共同组成防火墙系统。

     (2). 应用代理(Application Proxy)型

    应用代理型防火墙是工作在OSI的最高层,即应用层。其特点是完全"阻隔"了网络通信流,通过对每种应用服务编制专门的代理程序,实现监视和控制应用层通信流的作用。其典型网络结构如图所示。

    在代理型防火墙技术的发展过程中,它也经历了两个不同的版本,即:第一代应用网关型代理防火和第二代自适应代理防火墙。

    第一代应用网关(Application Gateway)型防火墙

    这类防火墙是通过一种代理(Proxy)技术参与到一个TCP连接的全过程。从内部发出的数据包经过这样的防火墙处理后,就好像是源于防火墙外部网卡一样,从而可以达到隐藏内部网结构的作用。这种类型的防火墙被网络安全专家和媒体公认为是最安全的防火墙。它的核心技术就是代理服务器技术。

    第二代自适应代理(Adaptive proxy)型防火墙

    它是近几年才得到广泛应用的一种新防火墙类型。它可以结合代理类型防火墙的安全性和包过滤防火墙的高速度等优点,在毫不损失安全性的基础之上将代理型防火墙的性能提高10倍以上。组成这种类型防火墙的基本要素有两个:自适应代理服务器(Adaptive Proxy Server)与动态包过滤器(Dynamic Packet filter)。

    在“自适应代理服务器”与“动态包过滤器”之间存在一个控制通道。在对防火墙进行配置时,用户仅仅将所需要的服务类型、安全级别等信息通过相应Proxy的管理界面进行设置就可以了。然后,自适应代理就可以根据用户的配置信息,决定是使用代理服务从应用层代理请求还是从网络层转发包。如果是后者,它将动态地通知包过滤器增减过滤规则,满足用户对速度和安全性的双重要求。

    代理类型防火墙的最突出的优点就是安全。由于它工作于最高层,所以它可以对网络中任何一层数据通信进行筛选保护,而不是像包过滤那样,只是对网络层的数据进行过滤。

    另外代理型防火墙采取是一种代理机制,它可以为每一种应用服务建立一个专门的代理,所以内外部网络之间的通信不是直接的,而都需先经过代理服务器审核,通过后再由代理服务器代为连接,根本没有给内、外部网络计算机任何直接会话的机会,从而避免了入侵者使用数据驱动类型的攻击方式入侵内部网。

    代理防火墙的最大缺点就是速度相对比较慢,当用户对内外部网络网关的吞吐量要求比较高时,代理防火墙就会成为内外部网络之间的瓶颈。那因为防火墙需要为不同的网络服务建立专门的代理服务,在自己的代理程序为内、外部网络用户建立连接时需要时间,所以给系统性能带来了一些负面影响,但通常不会很明显。

    防火墙分类3

    从防火墙结构上分,防火墙主要有:单一主机防火墙、路由器集成式防火墙和分布式防火墙三种。

    单一主机防火墙是最为传统的防火墙,独立于其它网络设备,它位于网络边界。

    这种防火墙其实与一台计算机结构差不多(如下图),同样包括CPU、内存、硬盘等基本组件,当然主板更是不能少了,且主板上也有南、北桥芯片。它与一般计算机最主要的区别就是一般防火墙都集成了两个以上的以太网卡,因为它需要连接一个以上的内、外部网络。其中的硬盘就是用来存储防火墙所用的基本程序,如包过滤程序和代理服务器程序等,有的防火墙还把日志记录也记录在此硬盘上。虽然如此,但我们不能说它就与我们平常的PC机一样,因为它的工作性质,决定了它要具备非常高的稳定性、实用性,具备非常高的系统吞吐性能。正因如此,看似与PC机差不多的配置,价格甚远。

    随着防火墙技术的发展及应用需求的提高,原来作为单一主机的防火墙现在已发生了许多变化。最明显的变化就是现在许多中、高档的路由器中已集成了防火墙功能,还有的防火墙已不再是一个独立的硬件实体,而是由多个软、硬件组成的系统,这种防火墙,俗称“分布式防火墙”。

    原来单一主机的防火墙由于价格非常昂贵,仅有少数大型企业才能承受得起,为了降低企业网络投资,现在许多中、高档路由器中集成了防火墙功能。如Cisco IOS防火墙系列。但这种防火墙通常是较低级的包过滤型。这样企业就不用再同时购买路由器和防火墙,大大降低了网络设备购买成本。

    分布式防火墙再也不是只是位于网络边界,而是渗透于网络的每一台主机,对整个内部网络的主机实施保护。在网络服务器中,通常会安装一个用于防火墙系统管理软件,在服务器及各主机上安装有集成网卡功能的PCI防火墙卡,这样一块防火墙卡同时兼有网卡和防火墙的双重功能。这样一个防火墙系统就可以彻底保护内部网络。各主机把任何其它主机发送的通信连接都视为“不可信”的,都需要严格过滤。而不是传统边界防火墙那样,仅对外部网络发出的通信请求“不信任”。

    防火墙分类4

    如果按防火墙的应用部署位置分,可以分为边界防火墙、个人防火墙和混合防火墙三大类。

    边界防火墙是最为传统的那种,它们于内、外部网络的边界,所起的作用的对内、外部网络实施隔离,保护边界内部网络。这类防火墙一般都是硬件类型的,价格较贵,性能较好。

    个人防火墙安装于单台主机中,防护的也只是单台主机。这类防火墙应用于广大的个人用户,通常为软件防火墙,价格最便宜,性能也最差。

    混合式防火墙可以说就是“分布式防火墙”或者“嵌入式防火墙”,它是一整套防火墙系统,由若干个软、硬件组件组成,分布于内、外部网络边界和内部各主机之间,既对内、外部网络之间通信进行过滤,又对网络内部各主机间的通信进行过滤。它属于最新的防火墙技术之一,性能最好,价格也最贵。

    防火墙分类5

    如果按防火墙的性能来分可以分为百兆级防火墙和千兆级防火墙两类。

    因为防火墙通常位于网络边界,所以不可能只是十兆级的。这主要是指防火的通道带宽(Bandwidth),或者说是吞吐率。当然通道带宽越宽,性能越高,这样的防火墙因包过滤或应用代理所产生的延时也越小,对整个网络通信性能的影响也就越小。

    19、目前流行的操作的系统有哪些?请举例说明安装操作系统的注意事项?

    参考答案:MS Windows系列:win 98、windows 2000系列、win XP、win 2003 Server、win Vista等等。

    UNIX类:SVRx、FreeBSD、OpenBSD、NetBSD、Solaris、各种Linux等等。Mac OS……

    多重引导时,一般先安装win操作系统,从低版本到高,再安装Linux

    20、简述一下c/s模式或者b/s模式?

    参考答案:C/S模式:客户端/服务器模式。工作原理:Client向Server提交一个请求;Server则使用一些方法处理这个请求,并将效果返回给Client。

    B/S结构,即Browser/Server(浏览器/服务器)结构,是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。在这种结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现,形成所谓3-tier结构。B/S结构,主要是利用了不断成熟的WWW浏览器技术,结合浏览器的多种Script语言(VBScript、JavaScript…)和ActiveX技术,用通用浏览器就实现了原来需要复杂专用软件才能实现的强大功能,并节约了开发成本,是一种全新的软件系统构造技术。

    21、TCP/UDP有哪些区别?

    参考答案:TCP-有连接,所以握手过程会消耗资源,过程为可靠连接,不会丢失数据,适合大数据量交换
    UDP-非可靠连接,会丢包,没有校验,速度快,无须握手过程

     
    TCP
    UDP
    是否连接
    面向连接
    面向非连接
    传输可靠性
    可靠的
    不可靠的
    应用场合
    传输大量数据
    少量数据
    速度

    22、ISO模型?HUB、tch、Router是ISO的第几层设备?

    参考答案:从底向上:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层

    HUB:1层(物理层);Switch:2层(数据链路层);Router:3层(网络层)

    23、内存有哪几种存储组织结构.请分别加以说明?

    参考答案:

    人力资源面试题

    1、你的测试职业发展是什么?你自认为做测试的优势在哪里?

    参考答案:

           测试经验越多,测试能力越高。所以我的职业发展是需要时间累积的,一步步向着高级测试工程师奔去。而且我也有初步的职业规划,前3年累积测试经验,按如何做好测试工程师的要求自己,不断的更新自己改正自己,做好测试任务。

      优势在于我对测试坚定不移的信心和热情,虽然经验还不够,但测试需要的基本技能我有信心在工作中得以发挥。

    2、你为什么想离开目前的职务?

    参考答案:

    3、你对我们公司了解有多少?

    参考答案:

    4、你找工作时,最重要的考虑因素为何?

    参考答案:工作的性质和内容是否能让我发挥所长,并不断成长。

    5、为什么我们应该录取你?

    参考答案:您可以由我过去的工作表现所呈现的客观数据,明显地看出我全力以赴的工作态度。

    6、请谈谈你个人的最大特色。

    参考答案:我的坚持度很高,事情没有做到一个令人满意的结果,绝不罢手。

    7、一个测试工程师应具备那些素质和技能?

    参考答案:

    8、您认为在测试人员同开发人员的沟通过程中,如何提高沟通的效率和改善沟通的效果?维持测试人员同开发团队中其他成员良好的人际关系的关键是什么?

    参考答案:

    9、在您以往的测试工作中,最让您感到不满意或者不堪回首的事情是什么?您是如何来对待这些事情的?

    参考答案:

    10、在即将完成这次笔试前,您是否愿意谈一些自己在以往的学习和工作中获得的工作经验和心得体会?(可以包括软件测试、过程改进、软件开发或者与此无关的其他方面)

    参考答案:

    11、为什么选择测试这行?

    参考答案:

      它是一个新兴的行业,有发展潜力,而且很锻炼人,需要掌握更多的技能,比做开发要更难

      为什么值得他们公司雇用?如果我雇用你,你能给部门带来什么贡献?

        如果明知这样做不对,你还会依主管的指过去做吗

      如果你接到一个客户抱怨的电话,你确知无法解决他的问题,你会怎么处理

      你觉得什么样的人最难相处

      为什么值得他们公司雇用?

        帮助公司提高软件质量和测试部门的技术水平

      如果我雇用你,你能给部门带来什么贡献?

        分享我的测试经验和测试技能,提高测试部门技术水平

      如何从工作中看出你是个自动自觉的人

            自动自觉范围太广

         1. 工作成果

         2. 工作质量  

    12、你的工作通常能在时限内完成吗.(我想问一下就是她问这个问题的动机是什么)

    参考答案:

        在有足够的资源和合理的工作量的情况下,完全可以按时完成,并能比一般人做的更好

    13、通常你对于别人批评你会有什么样的反应

    参考答案:有错即改,无错勉之

    14、如果明知这样做不对,你还会依主管的指过去做吗?

           参考答案:

    15、如果你接到一个客户抱怨的电话,你确知无法解决他的问题,你会怎么处理?

    参考答案:

        弄清楚客户为什么抱怨?是怎么样的问题?

      如果是客服问题,提交客服部门解决

      如果是质量问题,分析原因,下一版本改进

    16、请就软件测试人员应该具备什么样的基本素质说说你的看法。

    参考答案:

    17、你在五年内的个人目标和职业目标分别是什么?

    参考答案:

    分析这个问题是用来了解你的计划能力的,通过这个问题,面试人同时还可以知道你的目标是否符合企业对你的安排。

      错误回答我想在将来的某个时候考虑这个问题。如今企业的领导者更换频繁,我认为做太多的个人计划是荒谬可笑的,不是吗?

      评论这种回答属于令人反感的一类。首先,当有人想了解你的目标时,"将来的某个时候"这种通俗说法并不奏效。其次,认为企业很脆弱,领导者更换频繁,这种说法毫无疑问会令人反感,而且也是不合理的。最后,认为做计划可笑,看不起这个问题,而且反问面试人,这些都注定了这样的求职者最终会失败。

      正确回答从现在起的五年之内,我希望能够在一个很好的职位上待几年,而且最好有一次晋升,然后就期待着下一步。不管是向上提升,还是在企业内横向调动,对我个人来说,我希望找到一家企业——一家愿意做相互投入的企业——待上一段时间。

      评论这个问题没有回答得过分具体(那样可能会产生漏洞),而且它表明你有雄心,并且思考过在企业中的成长方式。通过表达横向调动和向上提升的愿望,表明你是一个有灵活性的人。

    18、你怎样做出自己的职业选择?

    参考答案:

           分析 面试人提出这个问题是为了了解求职者的动机,看看他(她)应聘这份工作是否有什么历史渊源,是否有职业规划,是不是仅仅在漫无目的地申请很多工作。

      错误回答 我一直都想在企业界工作。自孩提时代起,我就梦想自己至少也要成为大企业的副总裁。

      评论 除了难以令人相信之外,这种回答还存在一个问题:它表明求职者会对副总裁以下的职位不感兴趣。

      正确回答 在上大学四年级前的那个夏天,我决定集中精力在某一领域谋求发展。尽管我是学商业的,但是我不知道自己最终会从事哪一行业的工作。我花了一定的时间考虑自己的目标,想清楚了自己擅长做的事情以及想从工作中得到的东西,最后我得出了一个坚定的结论,那就是这个行业是最适合我的。

      评论 这种回答表明,求职者认真地做过一些计划,缩小了自己的关注点,而且也认准了前进的方向。这种回答还表明,求职者理解个人职业规划的重要性,并且有能力做出认真的个人决策。

     

     

    展开全文
  • 闲聊如何成为产品经理

    千次阅读 2019-07-16 09:11:30
    6、整理好用户反馈,让每个人有成就感 7、每次开会都有明确的目的,有腹稿 8、描绘问题清楚,不偏题 9、每个问题都形成结论,不要讨论来讨论去瞎扯 10、自己时刻准备提出更多方案和建议 11、每次讨论的过程和...

    1、什么是产品经理

    一般人想象的产品经理:出点子、做到极致、改变世界、华丽视觉是用户体验、带领大家冲锋陷阵
    实际上的产品经理:落实需求、做到完整、服务用户、满足用户体验、给大家后勤保障

    1)产品经理不是经理。
    2)不管设计再完美,需求总会改
    3)用户很傻,会越来越傻(就需求和产品使用而言)
    4)要把用户体验做好,并不简单
    5)产品设计、尤其是和UI交互,在一个产品中起到的作用只占10%
    6)产品经理要都看书多学习:用户体验中的可用性原则、用户体验的五大要素、图片排列方式、注册登录方法......
    7)程序员都是讨厌产品经理

    2、产品经理需要具备的素质

    充分条件
    1)出色的逻辑分析能力
    2)对用户、市场、行业特别熟悉
    3)出色的资源统筹能力和沟通能力

    必要条件
    4)自我管理能力:安排好自己的任务、规划好自己的工作、利用好自己的时间。
    5)学习能力
    6)责任心
    7)对产品有一定的追求

    3、对产品经理未来的思考

    对领域细分更多,对专业的要求更高
    1)互联网产品的类别越来越多、差别越来越大
    2)用户需求越来越复杂、产品的复杂度越来越高
    3)产品经理将随互联网渗透到各行各业

    4、小公司或大公司

    1)大公司工作流程讲规矩、小公司看效果
    2)大公司看经验、自理,小公司看是不是聪明、执行力强不强
    3)策略大公司老板订、小公司产品经理有机会参与
    4)大公司和小公司的产品设计流程都会有不合理的地方
    5)不管大公司还是小公司,都会接触到杂七杂八的事
    6)产品靠不靠谱在于是不是有很多经验的人
    7)大公司的产品经理是被塑造的,小公司是自塑的

    5、入职后

    1)锻炼自己对产品的理解
    对公司定位的理解、对用户定位的理解、对产品定位的理解、对公司研发能力的理解、对其他状况的理解。总之,做出的每个判断必须基于对产品多方面的理解而不是对竞品的理解、对市场的理解这些零散的因素。例如当你对稿子不满意时【你这个风格可能更适合年轻人,我们的目标客户是商务人士可能不合适】,而不是说【你这个不够大气,没有feel】;当后端工程师有问题的时候应该是【后续运营部门有很多次大活动,到时候访问量暴涨会有XXX风险】而不是【怎么该是我产品说了算】。
    2)提高分析和判断能力
    就美甲APP来说
    场景----需求----方案
    首先明白用户使用产品有哪些场景:
    1、已经有了想做的样式
    2、想做但不确定样式
    3、不确定做不做只是来看看
    针对上述用户,需求肯定不同:
    1、看看有没有那个样式或者类似样式
    2、看现在有什么好看的样式
    3、看看有什么能吸引一个人来做美甲
    对于三种需求我们有什么方案:
    1、根据用户的样式提供定制服务;搜索和筛选
    2、精品、热门推荐
    3、专题;精品热门推荐

    用户在第一次看到我们产品的时候,我们需要功能一目了然:专题、推荐、搜索/筛选、定制,进而我们判断产品刚上线,第三种用户最多,第二种逐渐增多,第一种是高端、深度用户,这样主次功能就分清了。
    初版的产品我们主要把精力放在推荐和专题上,搜索功能是第二版,定制是第三版
    3)确保良好的沟通和表达能力
    1、清楚自己是产品经理不是经理,不要盛气凌人
    2、用有理有据的事实来说服别人
    3、别人遇到困难积极帮助
    4、产品项目组任何同事有矛盾要积极协调
    5、随时让每个产品线的同事了解全局进展
    6、整理好用户反馈,让每个人有成就感
    7、每次开会都有明确的目的,有腹稿
    8、描绘问题清楚,不偏题
    9、每个问题都形成结论,不要讨论来讨论去瞎扯
    10、自己时刻准备提出更多方案和建议
    11、每次讨论的过程和结论都有记录
    4)对产品的责任心

    产品经理的最重要能力是让正确的事情相继发生
    5)关于工具的使用
    需求管理:
    伙伴云表格:快速指定责任人、状态、优先级等可选项
    任务协作:Tita、Teambition、worktile
    个人工具:Markdown
    整理记录:Evernote
    免费图标:Iconfinder、pixabay

    6)对于需求
    把产品的需求P和实现难度D做一个PD矩阵图

    记得看《用户体验要素》哦

    展开全文
  • WPF开发教程

    万次阅读 多人点赞 2019-07-02 23:13:20
    在应用程序的调度程序中,您可以调用 TranslateAccelerator,它会探查 User32 中的输入消息,并确定是否有任何消息已注册的快捷键匹配。在 WPF 中,上述内容不会起作用,因为系统是完全“可组合”的 – 任何元素都...

    ------WPF开发教程

     

    目录

    WPF基础入门.... 3

    1.    WPF基础之体系结构... 3

    2.    WPF基础之XAML. 9

    3.    WPF基础之基元素... 23

    4.    WPF基础之属性系统... 26

    5.    WPF基础之路由事件... 33

    6.    WPF基础之布局系统... 46

    7.    WPF基础之样式设置和模板化... 51

    8.    详谈WPF开发中的数据虚拟化... 64

    XAML语法.... 74

    1.    XAML语法术语... 74

    2.    代码隐藏和XAML. 82

    3.    XAML和自定义类... 83

    4.    标记扩展和XAML. 87

    5.    XAML命名空间和命名空间映射... 90

    6.    WPF名称范围... 92

    WPF控件开发.... 95

    1.    WPF控件开发之控件概述... 95

    2.    使用XAML创建按钮... 103

    3.    WPF控件库之Button. 114

    4.    WPF控件库之Menu. 115

    5.    WPF控件库之Lable. 119

    6.    WPF控件库之Toolbar. 121

    7.    WPF控件开发之自定义控件... 124

    8.    WPF控件开发之装饰器... 140

    WPF数据绑定.... 143

    1.    数据绑定概述... 143

    2.    WPF数据绑定之绑定源... 164

    3.    WPF数据绑定之数据模板... 166

    4.    WPF数据绑定之绑定声明... 181

    5.    实例一:绑定到ADO.NET数据源... 184

    6.    实例二:绑定到LINQ查询的结果... 186

    WPF图形和多媒体开发.... 187

    1.    WPF 图形动画和媒体概述... 187

    2.    WPF的图形呈现... 191

    3.    WPF的图像处理... 205

    4.    WPF的三维图形应用... 219

    5.    WPF的三维变换应用... 229

    6.    WPF的动画开发... 238

    7.    WPF的多媒体开发... 250

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    WPF基础入门

    1. WPF基础之体系结构

    本主题提供 Windows Presentation Foundation (WPF) 类层次结构的指导教程,涵盖了 WPF 的大部分主要子系统,并描述它们是如何交互的。本主题还详细介绍了 WPF 架构师所做的一些选择。

    System.Object

    WPF 主要编程模型是通过托管代码公开的。在 WPF 的早期设计阶段,曾有过大量关于如何界定系统的托管组件和非托管组件的争论。CLR 提供一系列的功能,可以令开发效率更高并且更加可靠(包括内存管理、错误处理和通用类型系统等),但这是需要付出代价的。

    下图说明了 WPF 的主要组件。关系图的红色部分(PresentationFramework、PresentationCore 和 milcore)是 WPF 的主要代码部分。在这些组件中,只有一个是非托管组件 – milcore。milcore 是以非托管代码编写的,目的是实现与 DirectX 的紧密集成。WPF 中的所有显示是通过 DirectX 引擎完成的,可实现高效的硬件和软件呈现。WPF 还要求对内存和执行进行精确控制。milcore 中的组合引擎受性能影响关系大,需要放弃 CLR 的许多优点来提高性能。

    本主题的后面部分将讨论 WPF 的托管和非托管部分之间的通信。下面介绍托管编程模型的其余部分。

    System.Threading.DispatcherObject

    WPF 中的大多数对象是从 DispatcherObject 派生的,这提供了用于处理并发和线程的基本构造。WPF 基于调度程序实现的消息系统。其工作方式与常见的 Win32 消息泵非常类似;事实上,WPF 调度程序使用 User32 消息执行跨线程调用。

    要讨论 WPF 中的并发,首先必须真正理解两个核心概念 – 调度程序和线程关联。

    在 WPF 的设计阶段,目标趋向于单一线程的执行,但这不是一种与线程“关联的”模型。当一个组件使用执行线程的标识来存储某种类型的状态时,将发生线程关联。最常见的形式是使用线程本地存储 (TLS) 来存储状态。线程关联要求执行的每个逻辑线程仅由操作系统中的一个物理线程所拥有,这将占用大量内存。最后,WPF 的线程处理模型保持与具有线程关联的单一线程执行的现有 User32 线程处理模型同步。主要原因是互操作性 – 类似于 OLE 2.0 的系统、剪贴板和 Internet Explorer 均需要单一线程关联 (STA) 执行。

    假设您具有带有 STA 线程的对象,则需要一种方式来在线程之间通信,并验证您是否位于正确的线程上。调度程序的作用就在于此。调度程序是一个基本的消息调度系统,具有多个按优先级排列的队列。消息的示例包括原始输入通知(鼠标移动)、框架函数(布局)或用户命令(执行此方法)。通过从 DispatcherObject 派生,您可以创建一个具有 STA 行为的 CLR 对象,并在创建时获得一个指向调度程序的指针。

    System.Windows.DependencyObject
    生成 WPF 时使用的主要体系结构原理之一是首选属性而不是方法或事件。属性是声明性的,使您更方便地指定意图而不是操作。它还支持模型驱动或数据驱动的系统,以显示用户界面内容。这种理念的预期效果是创建您可以绑定到的更多属性,从而更好地控制应用程序的行为。

    为了从由属性驱动的系统获得更多,需要一个比 CLR 提供的内容更丰富的属性系统。此丰富性的一个简单示例就是更改通知。为了实现双向绑定,您需要绑定的双方支持更改通知。为了使行为与属性值相关联,您需要在属性值更改时得到通知。Microsoft .NET Framework 具有一个 INotifyPropertyChange 接口,使对象可以发布更改通知(不过,这是可选的)。

    WPF 提供一个丰富的属性系统,该属性系统是从 DependencyObject 类型派生的。该属性系统实际是一个“依赖”属性系统,因为它会跟踪属性表达式之间的依赖关系,并在依赖关系更改时自动重新验证属性值。例如,如果您具有一个会继承的属性(如 FontSize),当继承该值的元素的父级发生属性更改时,会自动更新系统。

    WPF 属性系统的基础是属性表达式的概念。在 WPF 的第一版中,属性表达式系统是关闭的,表达式都是作为框架的一部分提供的。表达式致使属性系统不具有硬编码的数据绑定、样式调整或继承,而是由框架内后面的层来提供这些功能。

    属性系统还提供属性值的稀疏存储。因为对象可以有数十个(如果达不到上百个)属性,并且大部分值处于其默认状态(被继承、由样式设置等),所以并非对象的每个实例都需要具有在该对象上定义的每个属性的完全权重。

    属性系统的最后一个新功能是附加属性的概念。WPF 元素是基于组合和组件重用的原则生成的。某些包含元素(如 Grid 布局元素)通常需要子元素上的其他数据才能控制其行为(如行/列信息)。任何对象都可以为任何其他对象提供属性定义,而不是要将所有这些属性与每个元素相关联。这与 JavaScript 中的“expando”功能相似。

    System.Windows.Media.Visual

    定义一个系统后,下一步是将像素绘制到屏幕上。Visual 类用于生成可视化对象的树,每个对象可以选择性地包含绘制指令以及有关如何呈现这些指令(剪辑、变换等)的元数据。Visual 设计为极其轻量且灵活,所以大部分功能未进行 API 公开,并且极为依赖受保护的回调函数。

    Visual 实际上是到 WPF 组合系统的入口点。Visual 是以下两个子系统之间的连接点:托管 API 和非托管 milcore。

    WPF 通过遍历由 milcore 管理的非托管数据结构来显示数据。这些结构(称为组合节点)代表层次结构显示树,其中每个节点都有呈现指令。只能通过消息传递协议来访问此树(下图右侧所示)。

    当对 WPF 编程时,您将创建 Visual 元素及派生的类型,它们通过此消息传递协议在内部与此组合树进行通信。WPF 中的每个 Visual 可以不创建组合节点,也可以创建一个或多个组合节点。

    请注意一个非常重要的体系结构细节 – 可视对象和绘制指令的整个树都要进行缓存。在图形方面,WPF 使用一个保留的呈现系统。这可以使系统以一个高刷新率重绘系统,并且不会发生组合系统阻止对用户代码的回调。这有助于防止出现应用程序无响应的情况。

    关系图中不十分引人注意的另一个重要细节是系统实际上如何执行组合。

    在 User32 和 GDI 中,系统是在一个即时模式剪辑系统上工作。当需要呈现一个组件时,系统会建立一个剪辑边界,不允许组件接触该边界之外的像素,然后会要求此组件在该框中绘制像素。此系统在内存受限的系统上工作良好,因为当某些内容更改时,只需要处理受影响的组件即可 – 不会有两个组件对一个像素的颜色更改起作用。

    WPF 使用“绘画器的算法”绘制模型。这意味着并不是剪辑每个组件,而是要求从显示内容的背面至正面来呈现每个组件。这允许每个组件在先前的组件的显示内容上绘制。此模型的优点是您可以生成部分透明的复杂形状。与现今的现代图形硬件比较,此模型相对要快(创建 User32/ GDI 的情况除外)。

    如上面所述,WPF 的一个核心原理是移动到一个更具声明性且“以属性为核心”的编程模型。在可视化系统中,这会表现为需要关注的两种情况。

    首先,如果您考虑保留的模式图形系统,则实际上是从命令性 DrawLine/DrawLine 类型模型移动到面向数据的模型 new Line()/new Line()。通过这一向数据驱动的呈现移动,可以在使用属性表达的绘制指令上进行复杂的操作。从 Drawing 派生的类型实际上是用于呈现的对象模型。

    第二,如果评估动画系统,您将看到它几乎是完全声明性的。无需要求开发人员计算下一位置或下一颜色,您可以将动画表示为动画对象上的一组属性。于是,这些动画可以表示开发人员或设计人员的意图(在 5 秒内将此按钮从一个位置移动到另一个位置),系统就可以确定完成此任务的最有效方式。

    System.Windows.UIElement

    UIElement 定义核心子系统,包括 Layout、Input 和 Event。

    Layout 是 WPF 中的一个核心概念。在许多系统中,可能有一组固定的布局模型(HTML 支持三种布局模型:流、绝对和表),也可能没有布局模型(User32 实际仅支持绝对定位)。WPF 先假设开发人员和设计人员希望有一个灵活的可扩展布局模型,该模型可能是由属性值而不是命令性逻辑驱动的。在 UIElement 级别,会引入布局的基本协定 - 具有 Measure 和 Arrange 处理过程的两阶段模型。

    Measure 允许组件确定它要采用的大小。此阶段独立于 Arrange,因为在许多情形下,父元素会要求子元素测量若干次以确定其最佳位置和大小。父元素要求子元素测量这一事实体现了 WPF 的另一关键原则 – 内容大小。WPF 中的所有控件支持调整到内容原始大小的功能。这使本地化更加容易,并允许在调整大小时对元素进行动态布局。Arrange 阶段允许父元素定位并确定每个子元素的最终大小。

    通常会花费大量的时间来讨论 WPF 的输出端(Visual 及其相关对象)。然而,在输入端也有许多创新。WPF 输入模型的最基本更改也许是一致模型,输入事件通过系统借助此模型进行路由。

    输入是作为内核模式设备驱动程序上的信号发出的,并通过涉及 Windows 内核和 User32 的复杂进程路由到正确的进程和线程。与输入相对应的 User32 消息一旦路由到 WPF,它就会转换为 WPF 原始输入消息,并发送到调度程序。WPF 允许原始输入事件转换为多个实际事件,允许在保证传递到位的情况下在较低的系统级别实现类似“MouseEnter”的功能。

    每个输入事件至少会转换为两个事件 – “预览”事件和实际事件。WPF 中的所有事件都具有通过元素树路由的概念。如果事件从目标向上遍历树直到根,则被称为“冒泡”,如果从根开始向下遍历到目标,它们被称为“隧道”。输入预览事件隧道,使树中的任何元素都有机会筛选事件或对事件采取操作。然后,常规(非预览)事件将从目标向上冒泡到根。

    分割隧道和冒泡阶段使快捷键等功能在复合世界中表现一致。在 User32 中,您可以通过使用一个全局表来实现快捷键,该表中包含您希望支持的所有快捷键(Ctrl+N 映射为“新建”)。在应用程序的调度程序中,您可以调用 TranslateAccelerator,它会探查 User32 中的输入消息,并确定是否有任何消息与已注册的快捷键匹配。在 WPF 中,上述内容不会起作用,因为系统是完全“可组合”的 – 任何元素都可以处理和使用任何快捷键。将这个两阶段模型用于输入,将允许组件实现其自己的TranslateAccelerator"。

    为了进一步深化此功能,UIElement 还引入了 CommandBindings 的概念。WPF 命令系统允许开发人员以命令终结点(一种用于实现 ICommand 的功能)的方式定义功能。命令绑定使元素可以定义输入笔势 (Ctrl+N) 和命令(“新建”)之间的映射。输入笔势和命令定义都是可扩展的,并且可以在使用时联系到一起。这使得一些操作(例如,允许最终用户自定义其要在应用程序内使用的键绑定)显得无关紧要。

    至此,本主题已重点讨论了 WPF 的“核心”功能 - PresentationCore 程序集中实现的功能。当生成 WPF 时,基础部分(例如带有 Measure 和 Arrange 的布局的协定)和框架部分(例如 Grid 的特定布局的实现)之间的明确划分是希望的结果。目标就是提供在堆栈中处于较低位置的可扩展性点,这将允许外部开发人员可以在需要时创建自己的框架。

    System.Windows.FrameworkElement

    可以按两种不同的方式来看待 FrameworkElement。它对在 WPF 的较低层中的子系统引入一组策略和自定义项。它还引入了一组新的子系统。

    FrameworkElement 引入的主要策略是关于应用程序布局。FrameworkElement 在 UIElement 引入的基本布局协定之上生成,并增加了布局“插槽”的概念,使布局制作者可以方便地拥有一组面向属性的一致的布局语义。HorizontalAlignment、VerticalAlignment、MinWidth 和 Margin 等属性使得从 FrameworkElement 派生的所有组件在布局容器内具有一致的行为。

    利用 FrameworkElement,WPF 的核心层中具有的许多功能可以更方便地进行 API 公开。例如,FrameworkElement 通过 BeginStoryboard 方法提供对动画的直接访问。Storyboard 提供一种针对一组属性为多个动画编写脚本的方式。

    FrameworkElement 引入的两个最关键的内容是数据绑定和样式。

    曾经使用 Windows 窗体或 ASP.NET 创建应用程序用户界面 (UI) 的用户应当对 WPF 中的数据绑定子系统较为熟悉。在上述每个系统中,可通过一种简单的方式来表达您希望将给定元素中的一个或多个属性绑定到一个数据片断。WPF 对属性绑定、变换和列表绑定提供全面支持。

    WPF 中数据绑定的最值得关注的功能之一是引入了数据模板。利用数据模板,您可以声明性地指定某个数据片断的可视化方式。您可以将问题换个方向,让数据来确定将要创建的显示内容,而无需创建可绑定到数据的自定义用户界面。

    样式实际上是轻量级的数据绑定。使用样式,您可以将共享定义的一组属性绑定到元素的一个或多个实例。通过显式引用(通过设置 Style 属性)或通过将样式与元素的 CLR 类型隐式关联,便可以将样式应用到元素。

    System.Windows.Controls.Control

    控件的最重要的功能是模板化。如果您将 WPF 的组合系统视为一个保留模式呈现系统,则控件可通过模板化以一种参数化的声明性方式描述其呈现。ControlTemplate 实际上不过是一个用于创建一组子元素的脚本,同时绑定到由控件提供的属性。

    Control 提供一组常用属性,如 Foreground、Background、Padding 等,模板创作者可以使用这些常用属性来自定义控件的显示。控件的实现提供了数据模型和交互模型。交互模型定义了一组命令(如窗口的“关闭”),以及到输入笔势的绑定(如单击窗口上角的红叉)。数据模型提供了一组属性,用于自定义交互模型或自定义显示(由模板确定)。

    数据模型(属性)、交互模型(命令和事件)及显示模型(模板)之间的划分,使用户可以对控件的外观和行为进行完全自定义。

    最常见的控件数据模型是内容模型。如果查看 Button 之类的控件,您会看到它具有一个类型为 Object 的名为“Content”的属性。在 Windows 窗体和 ASP.NET 中,此属性通常为一个字符串 – 不过,这会限制您可以在按钮中输入的内容类型。按钮的内容可以是简单的字符串、复杂的数据对象或整个元素树。如果是数据对象,可以使用数据模板构造显示内容。

    摘要

    WPF 旨在帮助您创建动态的数据驱动的演示系统。系统的每一部分均可通过驱动行为的属性集来创建对象。数据绑定是系统的基础部分,在每一层中均进行了集成。

    传统的应用程序创建一个显示内容,然后绑定到某些数据。在 WPF 中,关于控件的所有内容、显示内容的所有方面都是由某种类型的数据绑定生成的。通过在按钮内部创建复合控件并将其显示绑定到按钮的内容属性,就会显示按钮内的文本。

    当开始开发基于 WPF 的应用程序时,您应对其感到非常熟悉。在其中设置属性、使用对象和数据绑定的方式与您使用 Windows 窗体或 ASP.NET 是极其相似的。如果对 WPF 体系结构有更深的了解,您就能够创建更丰富的应用程序,这些应用程序在根本上会将数据视为应用程序的核心驱动力。

    1. WPF基础之XAML

    本主题介绍可扩展应用程序标记语言 (XAML) 语言的功能,并演示如何使用 XAML 编写 Windows Presentation Foundation (WPF) 应用程序。本主题专门介绍了 Windows Presentation Foundation (WPF) 实现的 XAML。XAML 本身是比 Windows Presentation Foundation (WPF) 更广泛的一个语言概念。

    具有流控制支持的声明性语言
    XAML 简化了为 .NET Framework 编程模型创建 UI 的过程。您可以在声明性 XAML 标记中创建可见的 UI 元素,然后使用代码隐藏文件(通过分部类定义与标记相连接)将 UI 定义与运行时逻辑相分离。在 XAML 中混合代码和标记的功能很重要,因为 XML 本身是声明性的,不会为流控制真正建议一个模型。基于 XML 的声明性语言非常直观,可以为用户(尤其是具有 Web 设计和技术背景的人员)创建从原型到生产的各种界面。与其他大多数标记语言不同,XAML 直接呈现托管对象的实例化。这种常规设计原则简化了使用 XAML 创建的对象的代码和调试访问。

    XAML 文件是指通常使用 .xaml 扩展名的 XML 文件。

    下面的 XAML 示例演示了小标记在创建作为 UI 一部分的按钮时的必要性。创建的按钮通过主题样式获得默认的可视化表示形式,通过其类设计获得默认的行为。

    XAML 对象元素
    XAML 有一组规则,这些规则将对象元素映射为类或结构,将属性 (Attribute) 映射为属性 (Property) 或事件,并将 XML 命名空间映射为 CLR 命名空间。XAML 元素映射为被引用程序集中定义的 Microsoft .NET 类型,而属性 (Attribute) 则映射为这些类型的成员。

    上面的示例指定了两个对象元素:<STACKPANEL>(具有一个结束标记)和<BUTTON>同样具有多个属性;下一节将介绍属性)。字符串 StackPanel 和 Button 都将映射为某个类的名称,该类由 WPF 定义并且是 WPF 程序集的一部分。在指定对象元素标记时,可以为 XAML 处理创建一条指令,以便在加载 XAML 页时创建指定类的一个新实例。每个实例都是通过调用基础类或结构的默认构造函数并对结果进行存储而创建的。为了可用作 XAML 中的对象元素,该类或结构必须公开一个公共的默认(无参数)构造函数。

    设置属性
    XAML 中的属性是通过使用各种可能的语法在对象元素上设置属性来设置的。根据所设置的属性的特征,给定属性可使用的语法会有所不同。

    通过设置属性值,可以为对象元素添加功能或特征。对象元素的基础对象实例的初始状态基于默认的构造函数行为。通常,您的应用程序将使用其他一些实例,而不是任何给定对象的完全默认的实例。

    属性语法
    在 XAML 中,属性 (Property) 常常可以表示为属性 (Attribute)。属性 (Attribute) 语法是最简单的属性 (Property) 设置语法,并将成为过去使用标记语言的开发人员可以使用的最直观的语法。例如,以下标记将创建一个具有红色文本和蓝色背景的按钮,还会创建指定为 Content 的显示文本。

    属性元素语法
    对于一个对象元素的某些属性 (Property),属性 (Attribute) 语法是不可能实现的,因为提供属性 (Property) 值所需的对象或信息不能充分地表示为简单的字符串。对于这些情况,可以使用另一个语法,即属性元素语法。属性元素语法用标记的内容设置包含元素的引用的属性。一般而言,内容就是作为属性值的类型的某个对象(值设置实例通常被指定为另一个对象元素)。属性元素本身的语法为 <类型名称.属性>。指定内容之后,必须用一个结束标记结束属性元素,就像其他任何元素(语法为 )一样。对于同时支持属性 (Attribute) 和属性 (Property) 元素语法的属性 (Property),尽管这两种语法的细微之处(如空白处理)略有不同,但它们的结果通常是一样的。如果可以使用属性 (Attribute) 语法,那么使用属性 (Attribute) 语法通常更为方便,且能够实现更为精简的标记,但这只是一个风格的问题,而不属于技术限制。下面的示例演示了在前面的属性 (Attribute) 语法示例中设置的相同属性 (Property),但这次对 Button 的所有属性 (Property) 使用了属性 (Property) 元素语法。

    XAML 的属性 (Property) 元素语法表示了与标记的基本 XML 解释之间的巨大背离。对于 XML,<类型名称.属性> 代表了另一个元素,该元素仅表示一个子元素,而与 TypeName 父级之间没有必然的隐含关系。在 XAML 中,<类型名称.Property> 直接表示 Property 是类型名称 的属性(由属性元素内容设置),而绝不会是一个名称相似(碰巧名称中有一个点)但却截然不同的元素。

    属性和类继承
    作为 WPF 元素的XAML 属性 (Attribute) 而出现的属性 (Property) 通常从基类继承而来。例如,在上一个示例中,如果您要查看类定义、反射结果或文档,Background 属性并不是在 Button 类上直接声明的属性。相反,Background 是从基 Control 类继承而来。

    WPF XAML 元素的类继承行为是与标记的基本 XML 解释之间的另一个巨大背离。使用类继承(尤其是中间基类为抽象类时)的另一个原因在于,通过 XML 编程常用的架构类型(如 DTD 或 XSD 格式)几乎不可能准确且完整地表示 XAML 元素及其允许属性集。另外,XAML 中的“X”表示“extensible”(可扩展),而可扩展性破坏了“什么是用于 WPF 的 XAML”的任何给定表示形式的完整性。

    引用值和标记扩展
    标记扩展是一个 XAML 概念。在属性语法中,花括号({ 和 })表示标记扩展用法。此用法指示 XAML 处理不要像通常那样将属性值视为一个字符串或者可直接转换为文本字符串的值。

    当属性采用引用类型值时,这些属性常常需要属性元素语法(始终创建一个新实例)或通过标记扩展的对象引用。标记扩展用法有可能会返回现有实例,因此可以更加多样化,或者产生较少的对象系统开销。

    当使用标记扩展提供属性值时,应改为由相关标记扩展的后备类中的逻辑提供属性值。WPF 应用程序编程中最常用的标记扩展是 Binding(用于数据绑定表达式)以及资源引用 StaticResource 和 DynamicResource。通过使用标记扩展,即使属性 (Property) 不支持对直接对象实例化使用属性 (Attribute) 语法,也可以使用属性 (Attribute) 语法为属性 (Property) 提供引用值;或者使特定行为能够符合必须用属性 (Property) 类型值填充 XAML 属性 (Property) 这一常规行为要求。

    例如,下面的示例使用属性 (Attribute) 语法设置 Style 属性 (Property) 的值。Style 属性 (Property) 采用了 Style 类的一个实例,这是默认情况下不能在属性 (Attribute) 语法字符串中指定的引用类型。但在本例中,属性 (Attribute) 引用了特定的标记扩展 StaticResource。当处理该标记扩展时,它返回对以前在资源字典中作为键控资源进行实例化的某个样式的引用。

    资源只是 WPF 或 XAML 启用的一种标记扩展用法。

    支持 Typeconverter 的属性值
    在“属性语法”一节中,曾提到属性值必须能够使用字符串进行设置。对字符串如何转换为其他对象类型或基元值的基本本机处理取决于 String 类型本身。但是很多 WPF 类型或这些类型的成员扩展了基本字符串属性处理行为,因此更复杂的对象类型的实例可通过字符串指定为属性值。在代码级别,此处理是通过指定处理字符串属性值的 CLR 类型转换器来完成的。常用于指示矩形区域尺寸(如 Margin)的 Thickness 结构类型是这样一个类型的示例:它具有针对采用该类型的所有属性 (Property) 公开的一个特殊的、支持类型转换器的属性 (Attribute) 语法,以便于在 XAML 标记中使用。下面的示例使用支持类型转换器的属性 (Attribute) 语法来为 Margin 提供值:

    上面的属性 (Attribute) 语法示例与下面更为详细的语法示例等效,但在下面的示例中,Margin 是通过包含 Thickness 对象元素的属性 (Property) 元素语法设置的,而且 Thickness 的四个关键属性 (Property) 设置为新实例的属性 (Attribute):

    是使用支持类型转换器的语法,还是使用更详细的等效语法,通常只是编码风格的选择问题,但支持转换器的语法有助于生成更简洁的标记。(但是,有一些对象只能采用类型转换器将属性设置为该类型,因为类型对象本身并没有默认的构造函数。例如,Cursor。)

    集合类型和 XAML 集合属性
    XAML 指定了一个语言功能,通过该功能,可以从标记中特意省略表示集合类型的对象元素。当 XAML 处理器处理采用了集合类型的属性时,将隐式创建相应集合类型的实例,即使标记中不存在该集合的对象元素也是如此。在集合类型的 SDK 参考页中,特意省略集合对象元素的这种语法在 XAML 语法部分中有时候称为“隐式集合语法”。

    隐式集合语法适用于实现 IList 或 IDictionary 的类型,或者适用于数组。

    您已经在 XAML 资源示例中看到了未调用的集合对象元素的隐式集合语法的示例:

    除了根元素外,页面上作为另一个元素的子元素而嵌套的每个对象元素实际上都是下列一种或两种情况下的元素:父元素的隐式集合属性的一个成员,或者为父元素指定 XAML 内容属性值的元素(XAML 内容属性将在下一节进行讨论)。换言之,一个标记页上的父元素与子元素之间的关系实际上就是一个根对象,而根对象下面的每个对象元素要么是为父元素提供属性值的一个实例,要么是同样作为父元素的集合类型属性值的集合中的一项。在资源示例的案例中,Resources 属性采用 ResourceDictionary 类型的一个对象。下面的示例在语法上与显式指定的 ResourceDictionary 的对象元素等效。

    Resources 集合是许多常见的 WPF 框架级元素上存在的集合属性的一个示例。在 XAML 中设置此属性需要使用属性元素语法。属性元素中的每个被包含的对象元素都成为集合(IDictionary 实现)中的一个项。虽然集合类型本身通常没有包含项的属性或索引器,但是该属性不能在标记中指定;它完全是隐含的。对于 ResourceDictionary,该属性是 Item 索引器。

    XAML 内容属性
    XAML 指定了一个语言功能,通过该功能,任何可以用作 XAML 对象元素的类都可以确切指定其属性之一作为该类实例的 XAML 内容属性。当 XAML 处理器处理具有 XAML 内容属性的对象元素时,该对象元素的任何 XML 子元素都被当作包含在一个表示该内容属性的隐式属性元素标记中来处理。在标记中,可以省略 XAML 内容属性的属性元素语法。在标记中指定的任何子元素都将成为 XAML 内容属性的值。

    您已经看过了未调用的 XAML 内容属性的示例:本主题中的第一个示例。

    这里,Button 是 StackPanel 的子元素。这是一个简单直观的标记,其中出于两个不同的原因省略了两个标记。

    省略的 StackPanel.Children 属性元素: StackPanel 从 Panel 派生。Panel 将 Panel..::.Children 定义为其 XAML 内容属性。Panel 的所有派生类因而具有该 XAML 内容属性,而 Panel..::.Children 的属性元素可省略。

    省略的 UIElementCollection 对象元素: Panel..::.Children 属性采用类型 UIElementCollection,该类型实现 IList。因此,根据为集合定义的 XAML 规则,可以省略 UIElementCollection 对象元素标记。在这种情况下,UIElementCollection 实际上不能实例化为一个对象元素。您甚至无法显式声明该集合对象。这是因为 UIElementCollection 不公开默认的构造函数。其他几个 WPF 集合类型也不公开对象元素用法的构造函数,因为 XAML 集合语法处理仍然允许它们在 XAML 中隐式工作。这就是 UIElementCollection 对象元素在示例中显示为已被注释的原因;如果未被注释,示例将不能编译。

    内部文本和 XAML 内容属性
    StackPanel / Button 示例还有另一种变体。

    请注意为 Button 指定的显示文本如何发生变化。前面已在属性 (Attribute) 语法中指定了 Content 属性 (Property);这次显示字符串是 Button 对象元素中的内部文本。此语法可行,因为 Content 是 Button 基类 ContentControl 的 XAML 内容属性。元素中的字符串根据 Content 属性的属性类型(即 Object)进行计算。Object 不会尝试任何字符串类型转换,因此 Content 属性的值变成了文本字符串值。或者,Button 中的内容可以是任何单个 Object。Button 等控件通常为类定义 XAML 内容属性,因此 XAML 内容属性可用于 UI 和显示文本,或用于控件合成,或同时用于此两者。

    对于流程文档模型和本地化而言,在元素中放置字符串作为内容以生成与其他常见标记语言类似的标记的功能特别重要。

    XAML 内容属性值必须连续
    XAML 内容属性的值必须完全在该对象元素的其他任何属性元素之前或之后指定。不管 XAML 内容属性的值指定为字符串还是指定为一个或多个对象都是如此。例如,下面的标记无法进行编译:

    这在本质上是非法的,因为如果此语法是通过使用内容属性的属性元素语法而变为显式的,则内容属性将设置两次:

    一个类似的非法示例是,如果内容属性是一个集合,则子元素是与属性元素交错的:

    内容模型
    从语法上讲,可能支持将类用作 XAML 元素,但只有放置到整体内容模型或元素树中的所需位置时,该元素才能在应用程序或页面上正常运行。例如,MenuItem 通常只应作为 MenuBase 派生类(如 Menu)的子级放置。特定元素的内容模型在可用作 XAML 元素的控件和其他 WPF 类的类页面上的备注中进行说明。对于具有更复杂内容模型的某些控件,内容模型作为单独的概念主题进行说明。

    XAML 中的大小写和空白
    XAML 区分大小写。按名称与程序集中的基础类型进行比较或者与类型的成员进行比较时,必须使用正确的大小写指定所有对象元素、属性 (Property) 元素和属性 (Attribute) 名称。属性的值并不总是区分大小写。值是否区分大小写将取决于与采用该值的属性关联的类型转换器行为,或取决于属性值类型。例如,采用 Boolean 类型的属性可以采用 true 或 True 作为等效值,但只是因为 Boolean 的默认字符串类型转换已经允许这些值作为等效值。

    XAML 处理器和序列化程序将忽略或删除所有无意义的空白,并规范化任何有意义的空白。只有当您在 XAML 内容属性中指定字符串时,才会体现此行为的重要性。简言之,XAML 将空格、换行符和制表符转化为空格,如果它们出现在一个连续字符串的任一端,则保留一个空格。

    有关 XAML 语法的更多信息
    隐式集合语法和 XAML 内容属性都是允许省略某些推断标记的 XAML 语言功能。这些功能的目的是在编写或检查标记时使页面上的元素的父子关系更明显。

    如果您正在创建自定义类,并且正在考虑是否允许使用 XAML,XAML 语法术语主题也是一个很好的起点。

    XAML 根元素和 xmlns
    一个 XAML 文件只能有一个根元素,这样才能成为格式正确的 XML 文件和有效的 XAML 文件。通常,应选择属于应用程序模型一部分的元素(例如,为页面选择 Window 或 Page,为外部字典选择 ResourceDictionary,或为应用程序定义根选择 Application)。下面的示例演示 WPF 页面的典型 XAML 文件的根元素,其中的根元素为 Page。

    根元素还包含属性 xmlns 和 xmlns:x。这些属性向 XAML 处理器指明哪些命名空间包含标记将要引用的元素的元素定义。xmlns 属性专门指示默认的 xmlns 命名空间。在默认的 xmlns 命名空间中,可以不使用前缀指定标记中的对象元素。对于大多数 WPF 应用程序方案以及 SDK 的 WPF 部分中给出的几乎所有示例,默认的 xmlns 命名空间均映射为 WPF 命名空间 http://schemas.microsoft.com/winfx/2006/xaml/presentation。xmlns:x 属性指示另外一个 xmlns 命名空间,该命名空间映射 XAML 语言命名空间 http://schemas.microsoft.com/winfx/2006/xaml。在具有此映射的文件的标记中引用时,XAML 规范定义的必需语言组件带有 x: 前缀。使用 xmlns 定义用法范围和映射的这种做法符合 XML 1.0 规范。请注意,xmlns 属性仅在每页的根元素上和应用程序定义上(如果在标记中提供了应用程序定义)才是严格必需的。xmlns 定义将应用于根的所有子元素。(此行为仍然符合 xmlns 的 XML 1.0 规范。)xmlns 属性还允许出现在根下面的其他元素上,并且将应用于定义元素的任何子元素。但是,此用法并不典型,因为频繁定义或重新定义 xmlns 命名空间可能会导致 XAML 标记样式难以阅读。

    由于存在属于项目生成文件一部分的配置,因此可以知道 WPF 程序集包含的某些类型支持 WPF 到默认 xmlns 的映射。程序集还映射到目标文件中。因此,为了引用来自 WPF 程序集的 XAML 元素,只需映射 xmlns 即可。对于您自己的自定义程序集,或者除 WPF 之外的程序集,可以将该程序集指定为 xmlns 映射的一部分。通常,可选择其他前缀,但是也可以选择其他 xmlns 作为默认值,然后将 WPF 映射到前缀。

    x: 前缀
    在前面的根元素示例中,前缀 x: 用于映射 XAML xmlns http://schemas.microsoft.com/winfx/2006/xaml。在此 SDK 的项目模板、示例以及文档中,此 x: 前缀将用于映射 XAML xmlns。x: 前缀/XAML xmlns 包含多个将在 XAML 中频繁用到的编程构造。下面列出了将用到的最常见 x: 前缀/XAML xmlns 编程构造:

    x:Key:为 ResourceDictionary 中的每个资源设置一个唯一的键。在应用程序标记中看到的所有 x: 用法中,x:Key 可能占到 90%。

    x:Class:向为 XAML 页提供代码隐藏的类指定 CLR 命名空间和类名。必须具有这样一个类才能支持代码隐藏,也正是由于这个原因,即使没有资源,您也几乎总是会看到映射的 x:。

    x:Name:处理对象元素后,为运行时代码中存在的实例指定运行时对象名称。在不支持等效的 WPF 框架级Name 属性的情况下命名元素时,可以使用 x:Name。某些动画方案中会发生这种情况。

    x:Static:启用一个获取静态值的值引用,该静态值只能是一个 XAML 可设置属性。

    x:Type:根据类型名称构造一个 Type 引用。它用于指定采用 Type 的属性 (Attribute),如 Style..::.TargetType,不过在许多情况下属性 (Property) 本身具有字符串到 Type 的转换功能,因此使用 x:Type 是可选的。

    x: 前缀/XAML xmlns 中还有其他一些不太常见的编程构造。
    事件和 XAML 代码隐藏
    大多数 WPF 应用程序都是既包括标记,又包括代码隐藏。在一个项目中,XAML 被编写为 .xaml 文件,而使用 CLR 语言(如 Microsoft Visual Basic .NET 或 C#)编写代码隐藏文件。编译 XAML 文件时,每个 XAML 页的 XAML 代码隐藏文件的位置是通过指定一个命名空间和类作为 XAML 页的根元素的 x:Class 属性来确定的。

    在目前已介绍的示例中,您已看到几个按钮,但还没有一个按钮具有任何关联的逻辑行为。为对象元素添加行为的主要应用程序级机制是使用元素类的现有事件,并为在运行时引发该事件时调用的该事件编写特定的处理程序。事件名称以及要使用的处理程序的名称在标记中指定,而实现处理程序的代码在代码隐藏中定义。

    请注意,代码隐藏文件使用命名空间 MyNamespace 并将 MyPageCode 声明为该命名空间内的一个分部类。这相当于在标记根中提供的 MyNamespace.MyPageCode 的 x:Class 属性值。编译器将通过从根元素类型派生一个类,自动为编译的任何 XAML 页创建一个分部类。当您提供也会定义同一分部类的代码隐藏时,将在与编译的应用程序相同的命名空间和类中组合生成的代码。

    如果您不想创建单独的代码隐藏文件,还可以将代码内联到 XAML 文件中。但是,内联代码是一种缺少多样性的方法,有很多的限制。

    事件属性语法
    当您在标记中通过事件指定行为时,通常使用属性语法来附加处理程序。在其中指定事件属性的对象元素则变成侦听事件以及调用处理程序的实例。您要处理的具体事件的名称是属性名。属性值是您要定义的处理程序的方法名。然后您必须在代码隐藏中提供处理程序实现,并使处理程序基于该事件的委托。您使用编程语言(如 Microsoft Visual Basic .NET 或 C#)在代码隐藏中编写处理程序。

    引发事件时,每个 WPF 事件都将报告事件数据。事件处理程序可以访问这些事件数据。在前面的示例中,处理程序通过事件数据获取所报告的事件源,然后在该事件源上设置属性。

    路由事件
    路由事件是一个特殊的事件功能,该功能是 WPF 特有的并且是它的基础。路由事件允许一个元素处理另一个元素引发的事件,只要这些元素通过元素树关系连接起来。当使用 XAML 属性指定事件处理时,可以在任何元素(包括未在类成员表中列出该特定事件的元素)上侦听和处理路由事件。这是通过使用所属类名限定事件名属性来实现的。例如,在当前所讨论的 StackPanel / Button 示例中,父 StackPanel 可以通过在 StackPanel 对象元素上指定属性 Button.Click,并使用处理程序名作为属性值,为子元素按钮的 Click 事件注册一个处理程序。

    x:Name
    默认情况下,通过处理对象元素而创建的对象实例没有可供您在代码中使用的唯一标识符或固有的对象引用。当您在代码中调用构造函数时,几乎总是使用构造函数结果为构造的实例设置一个变量,以便以后在代码中引用该实例。为了对通过标记定义创建的对象进行标准化访问,XAML 定义了 x:Name 属性。您可以在任何对象元素上设置 x:Name 属性的值。在代码隐藏文件中,您选择的标识符等效于引用所构造的实例的实例变量。在任何方面,命名元素都像它们是对象实例一样工作(此名称只是引用该实例),并且代码隐藏文件可以引用该命名元素来处理应用程序内的运行时交互。

    WPF 框架级 XAML 元素继承 Name 属性 (Property),该属性等效于 XAML 定义的 x:Name 属性 (Attribute)。其他某些类也为 x:Name(通常也定义为 Name 属性)提供属性级等效项。一般而言,如果您在所选元素的成员表中找不到 Name 属性,可以改用 x:Name。

    下面的示例在 StackPanel 元素上设置 Name。然后,该 StackPanel 中的 Button 上的处理程序通过由 Name 设置的实例引用 buttonContainer 来引用 StackPanel。

    就像变量一样,实例的名称受范围概念的控制,因此可以在可预测的某个范围内强制名称唯一。定义页面的主要标记表示一个唯一的名称范围,而该名称范围的边界就是该页面的根元素。但是,其他标记源(如样式或样式中的模板)可以在运行时与页面交互,这种标记源常常具有其自己的名称范围,这些名称范围不一定与页面的名称范围相连接。

    附加属性和附加事件
    XAML 指定了一个语言功能,该功能允许在任何元素上指定某些属性或事件,而不管要为其设置属性或事件的元素的成员表中是否存在该属性或元素。该功能的属性版本称为附加属性,事件版本称为附加事件。从概念上讲,您可以将附加属性和附加事件认为是可以在任何元素/类上设置的全局成员,而不管其类层次结构如何。

    通常通过属性 (Attribute) 语法来使用 XAML 中的附加属性 (Property)。在属性 (Attribute) 语法中,您可以按照所有者类型.属性名 的形式指定附加属性 (Property)。表面上,这与属性元素用法类似,但在这种情况下,您指定的所有者类型 始终是一种与要为其设置附加属性的对象元素不同的类型。所有者类型 这种类型提供 XAML 处理器获取或设置附加属性值所需要的访问器方法。使用附加属性的最常见方案是使子元素能够向其父元素报告属性值。

    下面的示例演示了 DockPanel..::.Dock 附加属性。DockPanel 类为 DockPanel..::.Dock 定义访问器,因此拥有附加属性。DockPanel 类还包括一个逻辑,该逻辑迭代其子元素并具体检查每个元素是否具有 DockPanel..::.Dock 设置值。如果找到一个值,将在布局过程中使用该值定位子元素。使用 DockPanel..::.Dock 附加属性和这种定位功能事实上是 DockPanel 类的激动人心的一面。

    在 Windows Presentation Foundation (WPF) 中,所有附加属性还作为依赖项属性来实现。

    附加事件使用类似的所有者类型.事件名 属性语法形式。就像非附加事件一样,XAML 中的附加事件的属性值指定在元素上处理事件时调用的处理程序方法的名称。

    使用附加事件的一种方案适用于可在任何元素(如鼠标按钮)上处理的设备输入事件。例如,Mouse..::.MouseDown 就是这样一个附加事件。但是,大多数 WPF 框架级元素可以使用此事件,而无需使用附加事件。这是因为基元素类 UIElement 可为 Mouse..::.MouseDown 附加事件创建一个别名,并在 UIElement 成员表中公开该别名(为 MouseDown)。因此,通常不需要在 XAML 页或 Windows Presentation Foundation (WPF) 应用程序编程中指定附加事件语法。例外情况包括,您使用的是自定义元素,或者使用并非从 UIElement 派生但仍然具有可视化表示形式的对象元素(这些情况很少见)。在 WPF 中,所有附加事件还作为路由事件来实现。ContentElement 也为输入事件公开别名,供流程文档模型使用。

    XAML 页面根元素剖析
    下表显示了一个典型的 XAML 页面根元素分解结构,并显示了本主题中介绍的根元素的具体属性:

    基类和 XAML
    基础 XAML 及其架构是一个类集合,这些类对应于 CLR 对象以及要在 XAML 中使用的标记元素。但是,并不是所有的类都能映射到元素。抽象类(如 ButtonBase)和某些非抽象基类在 CLR 对象模型中用于继承,并且不支持对应的 XAML 标记。基类(包括抽象类)对于 XAML 开发仍然很重要,因为每个具体的 XAML 元素都从其层次结构中的某个基类继承成员。通常,这些成员包括可以设置为元素属性 (Attribute) 的属性 (Property),或者可以处理的事件。FrameworkElement 是 WPF 在 WPF 框架级的具体 UI 基类。设计 UI 时,您将使用各种形状、面板、修饰器或控件类,它们全部从 FrameworkElement 派生而来。有一个相关的基类 FrameworkContentElement,它使用可在 FrameworkElement 中特意镜像 API 的 API,支持适合流布局表示形式的面向文档的元素。元素级的属性 (Attribute) 和 CLR 对象模型的组合提供了一组通用的属性 (Property),可以在大多数具体的 XAML 元素上设置这些属性 (Property),而不管确切的元素类型及其基础类是什么。

    XAML 安全性
    XAML 是一种直接表示对象实例化和执行的标记语言。因此,使用 XAML 创建的元素能够像等效的生成代码那样与系统资源进行交互(如网络访问、文件系统 IO)。

    WPF 支持 .NET 安全框架代码访问安全性 (CAS)。这意味着在 Internet 区域中运行的 WPF 内容具有更少的执行权限。“松散 XAML”(由 XAML 查看器在加载时解释的未编译 XAML 的页面)和 XAML 浏览器应用程序 (XBAP) 通常在此 Internet 区域中运行,并且使用相同的权限集。但是,加载到完全受信任的应用程序中的 XAML 与宿主应用程序具有相同的系统资源访问权限。有关更多信息,请参见 Windows Presentation Foundation 部分信任安全性。

    从代码中加载 XAML
    XAML 可用于定义整个 UI,但有时也适合只使用 XAML 定义 UI 的一部分。利用此功能可以实现部分自定义、在本地存储信息、使用 XAML 提供业务对象或者各种可能的方案。这些方案的关键是 XamlReader 类及其 Load 方法。输入是一个 XAML 文件,而输出是一个对象,它表示从该标记中创建的对象的整个运行时树。然后您可以插入该对象,作为应用程序中已存在的另一个对象的属性。只要该属性在具有最终显示功能并且将通知执行引擎已在应用程序中添加新内容的内容模型中是一个合适的属性,您就可以通过以 XAML 形式加载来轻松地修改正在运行的应用程序的内容。请注意,通常只在完全受信任的应用程序中使用此功能,因为将文件加载到正在运行的应用程序中会带来明显的安全隐患。

    接下来的内容
    本主题简单介绍了 XAML 语法概念和术语。

    如果您尚未了解这些内容,请尝试阅读 Windows Presentation Foundation 入门教程。当您真正创建本教程中介绍的标记应用程序时,其中的练习将帮助您进一步了解本主题中介绍的许多概念。

    WPF 使用一个特定的应用程序模型,该模型基于 Application 类。

    生成 WPF 应用程序 (WPF) 为您详细介绍了如何通过命令行以及使用 Microsoft Visual Studio 生成包含 XAML 的应用程序。

    依赖项属性概述详细介绍了 Windows Presentation Foundation (WPF) 中属性的多样性,并介绍了依赖项属性的概念。

    最后,SDK 中还包含一个称为 XAMLPad 的 XAML 编辑工具。您可以使用此工具实时体验 XAML。

    1. WPF基础之基元素

    Windows Presentation Foundation (WPF) 中的大部分类都从四个类派生而来,这四个类在 SDK 文档中常常被称为基元素类。这些类包括 UIElement、FrameworkElement、ContentElement 和 FrameworkContentElement。DependencyObject 也是一个相关类,因为它是 UIElement 和 ContentElement 的通用基类。

    WPF 类中的基元素 API
    UIElement 和 ContentElement 都是从 DependencyObject 派生而来,但途径略有不同。此级别上的拆分涉及到 UIElement 或 ContentElement 如何在用户界面上使用,以及它们在应用程序起到什么作用。UIElement 在其类层次结构中也有 Visual,该类为 Windows Presentation Foundation (WPF) 公开较低级别的图形支持。Visual 通过定义独立的矩形屏幕区域来提供呈现框架。实际上,UIElement 适用于支持大型数据模型的元素,这些元素用于在可以称为矩形屏幕区域的区域内进行呈现和布局,在该区域内,内容模型特意设置得更加开放,以允许不同的元素进行组合。ContentElement 不是从 Visual 派生的;它的模型由其他对象(例如,阅读器或查看器,用来解释元素并生成完整的 Visual 供 Windows Presentation Foundation (WPF) 使用)来使用 ContentElement。某些 UIElement 类可用作内容宿主:它们为一个或多个 ContentElement 类(如 DocumentViewer)提供宿主和呈现。ContentElement 用作以下元素的基类:所具有的对象模型较小,并且多用于寻址可能宿主在 UIElement 中的文本、信息或文档内容。

    框架级和核心级
    UIElement 用作 FrameworkElement 的基类,ContentElement 用作 FrameworkContentElement 的基类。对于此下一级类,原因是要支持与 WPF 框架级相分离的 WPF 核心级,这种分离还存在于 API 如何在 PresentationCore 和 PresentationFramework 程序集之间进行划分。WPF 框架级为基本应用程序需要提供了一个更完整的解决方案,包括用于表示的布局管理器的实现。WPF 核心级提供了一种方法,以充分利用 WPF,而又不至于产生附加程序集开销。对于大多数典型的应用程序开发方案而言,这些级别之间的区别很少有影响,而且一般情况下应将 WPF API 视为一个整体,而无需担心 WPF 框架级与 WPF 核心级之间有何区别。如果您的应用程序设计选择替换大量 WPF 框架级功能,例如,如果您的整体解决方案已经有其自己的用户界面 (UI) 组合和布局实现,则可能需要了解级别之间的差异。

    选择从哪个元素派生
    创建用于扩展 WPF 的自定义类的最实用方法是从某个 WPF 类中派生,这样您可以通过现有的类层次结构获得尽可能多的所需功能。本节列出了三个最重要的元素类附带的功能,以帮助您决定要从哪个类进行派生。

    如果您要实现控件(这的确是从 WPF 类派生的更常见的原因之一),您可能需要从以下类中派生:实际控件、控件系列基类或至少是 Control 基类。

    如果您不是创建控件,并且需要从层次结构中较高的类进行派生,则可以参考下列各节的内容,了解每个基元素类定义了哪些特征。

    如果您创建从 DependencyObject 派生的类,则将继承以下功能:

    GetValue 和 SetValue 支持以及一般的属性系统支持。

    使用依赖项属性以及作为依赖项属性实现的附加属性的能力。

    如果您创建从 UIElement 派生的类,则除了能够继承 DependencyObject 提供的功能外,还将继承以下功能:

    对动画属性值的基本支持。

    对基本输入事件和命令的支持。

    可以重写以便为布局系统提供信息的虚方法。

    如果您创建从 FrameworkElement 派生的类,则除了能够继承 UIElement 提供的功能外,还将继承以下功能:

    对样式设置和演示图板的支持。

    对数据绑定的支持。

    对动态资源引用的支持。

    对属性值继承以及元数据中有助于向框架服务报告属性的相关情况(如数据绑定、样式或布局的框架实现)的其他标志的支持。

    逻辑树的概念。

    对布局系统的实际 WPF 框架级实现的支持,包括 OnPropertyChanged 重写(该重写可以检测到影响布局的属性更改)。

    如果您创建从 ContentElement 派生的类,则除了能够继承 DependencyObject 提供的功能外,还将继承以下功能:

    对动画的支持。

    对基本输入事件和命令的支持。

    如果您创建从 FrameworkContentElement 派生的类,则除了能够继承 ContentElement 提供的功能外,还将获得以下功能:

    对样式设置和演示图板的支持。

    对数据绑定的支持。

    对动态资源引用的支持。

    对属性值继承以及元数据中有助于向框架服务报告属性情况(如数据绑定、样式或布局的框架实现)的其他标志的支持。

    您不会继承对布局系统修改(如 ArrangeOverride)的访问权限。布局系统实现只在 FrameworkElement 上提供。但是,您会继承 OnPropertyChanged 重写(可以检测影响布局的属性更改并将这些更改报告给任何内容宿主)。

    记录了各种类的内容模型。如果您要找到一个合适的类以便从该类进行派生,其内容模型是一个应该考虑的可能因素。

    其他基类

    DispatcherObject
    DispatcherObject 为 WPF 线程模型提供支持,并允许为 WPF 应用程序创建的所有对象与 Dispatcher 相关联。即使您不从 UIElement, DependencyObject 或 Visual 派生,也应考虑从 DispatcherObject 派生,以获得此线程模型支持。

    Visual
    Visual 实现二维对象在近似矩形的区域中通常需要具有可视化表示的概念。Visual 的实际呈现发生在其他类中(不是独立的),但是 Visual 类提供了一个由各种级别的呈现处理使用的已知类型。Visual 实现命中测试,但它不公开报告命中测试结果的事件(这些都位于 UIElement 中)。

    Freezable
    Freezable 通过在出于性能原因需要不可变对象时提供为对象生成副本的途径,来模拟可变对象的不变性。Freezable 类型为某些图形元素(如几何形状、画笔以及动画)提供了一个通用的基础。值得注意的是,Freezable 不是一个 Visual;当应用 Freezable 以填充另一个对象的属性值时,它包含的属性将变成子属性,而这些子属性可能会影响呈现。

    Animatable
    Animatable 是一个 Freezable 派生类,它特别添加了动画控件层和某些实用工具成员,从而使当前动画的属性可以与未动画的属性区分开。

    Control
    Control 是称为控件或组件(取决于技术)的对象类型的理想基类。一般而言,WPF 控件类是直接表示 UI 控件或积极参与控件组合的类。Control 实现的主要功能是控件模板化。

    1. WPF基础之属性系统

    Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行库 (CLR) 属性的功能。这些服务通常统称为 WPF 属性系统。由 WPF 属性系统支持的属性称为依赖项属性。本概述介绍 WPF 属性系统以及依赖项属性的功能,这包括如何在可扩展应用程序标记语言 (XAML) 中和代码中使用现有的依赖项属性。本概述还介绍了依赖项属性所特有的方面(如依赖项属性元数据),并说明了如何在自定义类中创建自己的依赖项属性。

    先决条件
    本主题假设您在 CLR 和面向对象的编程方面有一些基础知识。若要采用本主题中的示例,还应当了解 XAML 并知道如何编写 WPF 应用程序。

    依赖项属性和 CLR 属性
    在 WPF 中,属性通常公开为公共语言运行库 (CLR) 属性。在基本级别,您可以在根本不知道这些属性实现为依赖项属性的情况下直接与它们交互。但是,您应当熟悉 WPF 属性系统的部分或全部功能,才能利用这些功能。

    依赖项属性的用途在于提供一种方法来基于其他输入的值计算属性值。这些其他输入可以包括系统属性(如主题和用户首选项)、实时属性确定机制(如数据绑定和动画/演示图板)、重用模板(如资源和样式)或者通过与元素树中其他元素的父子关系来公开的值。另外,可以通过实现依赖项属性来提供独立验证、默认值、监视其他属性的更改的回调以及可以基于可能的运行时信息来强制指定属性值的系统。派生类还可以通过重写依赖项属性元数据(而不是重写现有属性的实际实现或者创建新属性)来更改现有属性的某些具体特征。

    在 SDK 参考中,可以根据某个属性的托管引用页上是否存在“依赖项属性信息”部分来确定该属性是否为依赖项属性。“依赖项属性信息”部分包括一个指向该依赖项属性的 DependencyProperty 标识符字段的链接,还包括一个为该属性设置的元数据选项的列表、每个类的重写信息以及其他详细信息。

    依赖项属性支持 CLR 属性
    依赖项属性和 WPF 属性系统通过提供一个支持属性的类型来扩展属性功能,这是使用私有字段支持该属性的标准模式的替代实现方法。该类型的名称是 DependencyProperty。定义 WPF 属性系统的另一个重要类型是 DependencyObject。DependencyObject 定义可以注册和拥有依赖项属性的基类。

    下面汇集了在本软件开发工具包 (SDK) 文档中,在讨论依赖项属性时所使用的术语:

    依赖项属性:一个由 DependencyProperty 支持的属性。

    依赖项属性标识符:一个 DependencyProperty 实例,在注册依赖项属性时作为返回值获得,之后将存储为一个类成员。在与 WPF 属性系统交互的许多 API 中,此标识符用作一个参数。

    CLR“包装”:属性的实际 get 和 set 实现。这些实现通过在 GetValue 和 SetValue 调用中使用依赖项属性标识符来合并此标识符,从而使用 WPF 属性系统为属性提供支持。

    下面的示例定义 IsSpinning 依赖项属性,并说明 DependencyProperty 标识符与它所支持的属性之间的关系。

    属性以及支持它的 DependencyProperty 字段的命名约定非常重要。字段总是与属性同名,但其后面追加了 Property 后缀。

    设置属性值
    可以在代码或 XAML 中设置属性。

    在 XAML 中设置属性值
    下面的 XAML 示例将按钮的背景色指定为红色。该示例演示了这样一种情况:在所生成的代码中,XAML 加载器将 XAML 属性的简单字符串值的类型转换为 WPF 类型(一种 Color,通过 SolidColorBrush)。

    XAML 支持各种设置属性的语法格式。要对特定的属性使用哪种语法取决于该属性所使用的值类型以及其他因素(例如,是否存在类型转换器)。

    作为非属性语法的示例,下面的 XAML 示例显示了另一种按钮背景。这一次不是设置简单的纯色,而是将背景设置为图像,用一个元素表示该图像并将该图像的源指定为嵌套元素的属性。这是属性元素语法的示例。

    在代码中设置属性
    在代码中设置依赖项属性值通常只是调用由 CLR“包装”公开的 set 实现。

    获取属性值实质上也是在调用 get“包装”实现:

    您还可以直接调用属性系统 API GetValue 和 SetValue。如果您使用的是现有属性,则上述操作通常不是必需的(使用包装会更方便,并能够更好地向开发人员工具公开属性)。但是在某些情况下适合直接调用 API。

    还可以在 XAML 中设置属性,然后通过代码隐藏在代码中访问这些属性。

    由依赖项属性提供的属性功能
    依赖项属性提供用来扩展属性功能的功能,这与字段支持的属性相反。每个这样的功能通常都表示或支持整套 WPF 功能中的特定功能:

    资源

    数据绑定

    样式

    动画

    元数据重写

    属性值继承

    WPF 设计器集成

    资源
    依赖项属性值可以通过引用资源来设置。资源通常指定为页面根元素或应用程序的子元素,通过这些位置可以最方便地访问资源。下面的示例演示如何定义 SolidColorBrush 资源。

    在定义了某个资源之后,可以引用该资源并使用它来提供属性值:

    这个特定的资源称为 DynamicResource 标记扩展(在 XAML 中,可以使用静态或动态资源引用)。若要使用动态资源引用,必须设置为依赖项属性,因此它是由 WPF 属性系统明确启用的动态资源引用用法。

    说明:
    资源被视为本地值,这意味着,如果您设置另一个本地值,该资源引用将被消除。
     
    数据绑定
    依赖项属性可以通过数据绑定来引用值。数据绑定通过特定的标记扩展语法(在 XAML 中)或 Binding 对象(在代码中)来工作。使用数据绑定,最终属性值的确定将延迟到运行时,在运行时,将从数据源获取属性值。

    下面的示例在 XAML 中使用一个绑定,为 Button 设置 Content 属性。该绑定使用一个继承的数据上下文和一个 XmlDataProvider 数据源(未显示出来)。绑定本身通过数据源中的 XPath 指定所需的源属性。

     

    绑定被视为本地值,这意味着,如果您设置另一个本地值,该绑定将被消除。

    依赖项属性或 DependencyObject 类本身并不支持 INotifyPropertyChanged,以便为数据绑定操作生成有关 DependencyObject 源属性值变化的通知。

    样式
    样式和模板是使用依赖项属性的两个主要激发方案。在设置定义应用程序用户界面 (UI) 的属性时,样式尤其有用。样式在 XAML 中通常定义为资源。样式与属性系统交互,因为它们通常包含特定属性的“setter”,以及基于另一个属性的实时值更改属性值的“trigger”。

    下面的示例创建一个非常简单的样式(该样式将在 Resources 字典中定义,未显示出来),然后将该样式直接应用于 Button 的 Style 属性。样式中的 setter 将带样式的 Button 的 Background 属性设置为 green。

     

    动画
    可以对依赖项属性进行动画处理。在应用和运行动画时,经过动画处理的值的操作优先级将高于该属性以其他方式具有的任何值(如本地值)。

    下面的示例对 Button 属性的 Background 进行动画处理(在技术上,Background 是通过使用属性元素语法将空白 SolidColorBrush 指定为 Background 来进行动画处理的,之后,该 SolidColorBrush 的 Color 属性就是直接进行动画处理的属性)。

     

    元数据重写
    在从最初注册依赖项属性的类派生时,可以通过重写依赖项属性的元数据来更改该属性的某些行为。对元数据的重写依赖于 DependencyProperty 标识符。重写元数据不需要重新实现属性。元数据的变化是由属性系统在本机处理的;对于所有从基类继承的属性,每个类都有可能基于每个类型保留元数据。

    下面的示例重写依赖项属性 DefaultStyleKey 的元数据。重写这个特定的依赖项属性的元数据是某个实现模式的一部分,该模式创建可以使用主题中的默认样式的控件。

     

    属性值继承
    元素可以从其在树中的父级继承依赖项属性的值。

    说明:
    属性值继承行为并未针对所有的依赖项属性在全局启用,因为继承的计算时间确实会对性能产生一定的影响。属性值继承通常只有在特定方案指出适合使用属性值继承时才对属性启用。可以通过在 SDK 参考中查看某个依赖项属性的“依赖项属性信息”部分,来确定该依赖项属性是否继承属性值。

    下面的示例演示一个绑定,并设置指定绑定(在前面的绑定示例中未显示出来)的源的 DataContext 属性。DataContext 属性的值继承,因此子元素中的任何后续绑定都不必遵守在父级 StackPanel 元素中指定为 DataContext 的源。

     

    WPF 设计器集成
    如果自定义控件具有实现为依赖项属性的属性,则它将收到相应的 Visual Studio Windows Presentation Foundation (WPF) 设计器支持。一个示例就是能够在“属性”窗口中编辑直接依赖项属性和附加依赖项属性。
    依赖项属性值优先级
    当您获取依赖项属性的值时,可能会获得通过其他参与 WPF 属性系统且基于属性的任一输入而在该属性上设置的值。由于存在依赖项属性值优先级,使得属性获取值的方式的各种方案得以按可预测的方式交互。

    请看下面的示例。该示例包括一个应用于所有按钮及其 Background 属性的样式,但是之后还指定了一个具有在本地设置的 Background 值的按钮。

    说明:
    SDK 文档在讨论依赖项属性时有时会使用“本地值”或“本地设置的值”等术语。本地设置的值是指在代码中直接为对象实例设置的属性 (Property) 值,或者在 XAML 中设置为元素属性 (Attribute) 的属性 (Property) 值。 

    实际上,对于第一个按钮,该属性设置了两次,但是仅应用了一个值,即,具有最高优先级的值。本地设置的值具有最高优先级(对于正在运行的动画除外,但是在本示例中没有应用动画),因此,对于第一个按钮的背景将使用本地设置的值,而不使用样式 setter 值。第二个按钮没有本地值(而且没有其他比样式 setter 优先级更高的值),因此该按钮中的背景将来自样式 setter。

     

    为什么存在依赖项属性优先级?
    通常,您不会希望总是应用样式,而且不希望样式遮盖单个元素的哪怕一个本地设置值(否则,通常将很难使用样式或元素)。因此,来自样式的值的操作优先级将低于本地设置的值。
    说明:
    在 WPF 元素定义了许多非依赖项属性的属性。一般说来,只有在需要支持至少一个由属性系统启用的方案(数据绑定、样式、动画、默认值支持、继承、附加属性或失效)时,才将属性实现为依赖项属性。

    了解有关依赖项属性的更多信息
    附加属性是一种类型的属性,它支持 XAML 中的专用语法。附加属性通常与公共语言运行库 (CLR) 属性不具有 1:1 对应关系,而且不一定是依赖项属性。附加属性的典型用途是使子元素可以向其父元素报告属性值,即使父元素和子元素的类成员列表中均没有该属性也是如此。一个主要方案是,使子元素可以将其在 UI 中的表示方式通知给父级。
    组件开发人员或应用程序开发人员可能希望创建自己的依赖项属性,以便实现数据绑定或样式支持之类的功能,或者实现对失效和强制指定值的支持。
    通常,依赖项属性应当被视为公共属性,这些公共属性可以由任何具有实例访问权限的调用方访问,或至少可被这样的调用方发现。

    1. WPF基础之路由事件

    本主题描述 Windows Presentation Foundation (WPF) 中路由事件的概念。本主题定义路由事件术语,描述路由事件如何通过元素树来路由,概述如何处理路由事件,并介绍如何创建您自己的自定义路由事件。

    先决条件
    本主题假设您对如下内容有基本的了解:公共语言运行库 (CLR)、面向对象的编程以及如何用树的概念来说明 WPF 元素之间的关系。为了按照本主题中的示例操作,您还应当了解可扩展应用程序标记语言 (XAML) 并知道如何编写非常基本的 WPF 应用程序或页。
    什么是路由事件?
    可以从功能或实现的角度来考虑路由事件。此处对这两种定义均进行了说明,因为用户当中有的认为前者更有用,而有的则认为后者更有用。

    功能定义:路由事件是一种可以针对元素树中的多个侦听器(而不是仅针对引发该事件的对象)调用处理程序的事件。

    实现定义:路由事件是一个 CLR 事件,可以由 RoutedEvent 类的实例提供支持并由 Windows Presentation Foundation (WPF) 事件系统来处理。

    典型的 WPF 应用程序中包含许多元素。无论这些元素是在代码中创建的还是在 XAML 中声明的,它们都由共同所在的元素树关联起来。根据事件的定义,事件路由可以按两种方向之一传播,但是通常会在元素树中从源元素向上“冒泡”,直到它到达元素树的根(通常是页面或窗口)。如果您以前用过 DHTML 对象模型,则可能会熟悉这个冒泡概念。

    请考虑下面的简单元素树:

    此元素树生成类似如下的内容:


    在这个简化的元素树中,Click 事件的源是某个 Button 元素,而所单击的 Button 是有机会处理该事件的第一个元素。但是,如果附加到 Button 的任何处理程序均未作用于该事件,则该事件将向上冒泡到元素树中的 Button 父级(即 StackPanel)。该事件可能会冒泡到 Border,然后会到达元素树的页面根(未显示出来)。

    换言之,此 Click 事件的事件路由为:

    Button-->StackPanel-->Border-->...

    路由事件的顶级方案
    下面简要概述了需运用路由事件的方案,以及为什么典型的 CLR 事件不适合这些方案:

    控件的撰写和封装:WPF 中的各个控件都有一个丰富的内容模型。例如,可以将图像放在 Button 的内部,这会有效地扩展按钮的可视化树。但是,所添加的图像不得中断命中测试行为(该行为会使按钮响应对图像内容的 Click),即使用户所单击的像素在技术上属于该图像也是如此

    单一处理程序附加点:在 Windows 窗体中,必须多次附加同一个处理程序,才能处理可能是从多个元素引发的事件。路由事件使您可以只附加该处理程序一次(像上例中那样),并在必要时使用处理程序逻辑来确定该事件源自何处。例如,这可以是前面显示的 XAML 的处理程序:

    类处理:路由事件允许使用由类定义的静态处理程序。这个类处理程序能够抢在任何附加的实例处理程序之前来处理事件。

    引用事件,而不反射:某些代码和标记技术需要能标识特定事件的方法。路由事件创建 RoutedEvent 字段作为标识符,以此提供不需要静态反射或运行时反射的可靠的事件标识技术。

    路由事件的实现方式
    路由事件是一个 CLR 事件,它由 RoutedEvent 类提供支持并用 WPF 事件系统注册。从注册中获取的 RoutedEvent 实例通常保留为某种类的 public static readonly 字段成员,该类进行了注册并因此“拥有”路由事件。与同名 CLR 事件(有时称为“包装”事件)的连接是通过重写 CLR 事件的 add 和 remove 实现来完成的。通常,add 和 remove 保留为隐式默认值,该默认值使用特定于语言的相应事件语法来添加和移除该事件的处理程序。路由事件的支持和连接机制在概念上与以下机制相似:依赖项属性是一个 CLR 属性,该属性由 DependencyProperty 类提供支持并用 WPF 属性系统注册。

    下面的示例演示自定义 Tap 路由事件的声明,其中包括注册和公开 RoutedEvent 标识符字段以及对 Tap CLR 事件进行 add 和 remove 实现。

    路由事件处理程序和 XAML
    若要使用 XAML 为某个事件添加处理程序,请将该事件的名称声明为用作事件侦听器的元素上的属性。该属性的值是所实现的处理程序方法的名称,该方法必须存在于代码隐藏文件的分部类中。

    用来添加标准 CLR 事件处理程序的 XAML 语法与用来添加路由事件处理程序的语法相同,因为您实际上是在向下面具有路由事件实现的 CLR 事件包装中添加处理程序。

    路由策略

    路由事件使用以下三个路由策略之一:

    冒泡:针对事件源调用事件处理程序。路由事件随后会路由到后续的父元素,直到到达元素树的根。大多数路由事件都使用冒泡路由策略。冒泡路由事件通常用来报告来自不同控件或其他 UI 元素的输入或状态变化。

    直接:只有源元素本身才有机会调用处理程序以进行响应。这与 Windows 窗体用于事件的“路由”相似。但是,与标准 CLR 事件不同的是,直接路由事件支持类处理(类处理将在下一节中介绍)而且可以由 EventSetter 和 EventTrigger 使用。

    隧道:最初将在元素树的根处调用事件处理程序。随后,路由事件将朝着路由事件的源节点元素(即引发路由事件的元素)方向,沿路由线路传播到后续的子元素。在合成控件的过程中通常会使用或处理隧道路由事件,这样,就可以有意地禁止显示复合部件中的事件,或者将其替换为特定于整个控件的事件。在 WPF 中提供的输入事件通常是以隧道/冒泡对实现的。隧道事件有时又称作 Preview 事件,这是由隧道/冒泡对所使用的命名约定决定的。

    为什么使用路由事件?
    作为应用程序开发人员,您不需要始终了解或关注正在处理的事件是否作为路由事件实现。路由事件具有特殊的行为,但是,如果您在引发该行为的元素上处理事件,则该行为通常会不可见。

    如果您使用以下任一建议方案,路由事件的功能将得到充分发挥:在公用根处定义公用处理程序、合成自己的控件或者定义您自己的自定义控件类。

    路由事件侦听器和路由事件源不必在其层次结构中共享公用事件。任何 UIElement 或 ContentElement 可以是任一路由事件的事件侦听器。因此,您可以使用在整个工作 API 集内可用的全套路由事件作为概念“接口”,应用程序中的不同元素凭借这个接口来交换事件信息。路由事件的这个“接口”概念特别适用于输入事件。

    路由事件还可以用来通过元素树进行通信,因为事件的事件数据会永存到路由中的每个元素中。一个元素可以更改事件数据中的某项内容,该更改将对于路由中的下一个元素可用。

    之所以将任何给定的 WPF 事件作为路由事件实现(而不是作为标准 CLR 事件实现),除了路由方面的原因,还有两个其他原因。如果您要实现自己的事件,则可能也需要考虑这两个因素:

    某些 WPF 样式和模板功能(如 EventSetter 和 EventTrigger)要求所引用的事件是路由事件。前面提到的事件标识符方案就是这样的。

    路由事件支持类处理机制,类可以凭借该机制来指定静态方法,这些静态方法能够在任何已注册的实例程序访问路由事件之前,处理这些路由事件。这在控件设计中非常有用,因为您的类可以强制执行事件驱动的类行为,以防它们在处理实例上的事件时被意外禁止。

    本主题将用单独的章节来讨论每个考虑因素。

    为路由事件添加和实现事件处理程序
    若要在 XAML 中添加事件处理程序,只需将相应的事件名称作为一个属性添加到某个元素中,并将该属性的值设置为用来实现相应委托的事件处理程序的名称,如下面的示例中所示。

    b1SetColor 是所实现的处理程序的名称,该处理程序中包含用来处理 Click 事件的代码。b1SetColor 必须具有与 RoutedEventHandler 委托相同的签名,该委托是 Click 事件的事件处理程序委托。所有路由事件处理程序委托的第一个参数都指定要向其中添加事件处理程序的元素,第二个参数指定事件的数据。

    RoutedEventHandler 是基本的路由事件处理程序委托。对于针对某些控件或方案而专门处理的路由事件,要用于路由事件处理程序的委托还可能会变得更加专用化,因此它们可以传输专用的事件数据。例如,在常见的输入方案中,可以处理 DragEnter 路由事件。您的处理程序应当实现 DragEventHandler 委托。借助更具体的委托,可以对处理程序中的 DragEventArgs 进行处理,并读取 Data 属性,该属性中包含拖动操作的剪贴板内容。

    在用代码创建的应用程序中为路由事件添加处理程序非常简单。路由事件处理程序始终可以通过帮助器方法 AddHandler 来添加,现有支持为 add 调用的也是此方法。但是,现有的 WPF 路由事件通常借助于支持机制来实现 add 和 remove 逻辑,这些逻辑允许使用特定于语言的事件语法来添加路由事件的处理程序,特定于语言的事件语法比帮助器方法更直观。下面是帮助器方法的示例用法:

    下一个示例演示 C# 运算符语法(visual Basic 的运算符语法稍有不同,因为它以不同的方法来处理取消引用):

    如果使用的是 Visual Basic,则还可以使用 Handles 关键字,将处理程序作为处理程序声明的一部分来添加。

    已处理”概念
    所有的路由事件都共享一个公用的事件数据基类 RoutedEventArgs。RoutedEventArgs 定义了一个采用布尔值的 Handled 属性。Handled 属性的目的在于,允许路由中的任何事件处理程序通过将 Handled 的值设置为 true 来将路由事件标记为“已处理”。处理程序在路由路径上的某个元素处对共享事件数据进行处理之后,这些数据将再次报告给路由路径上的每个侦听器。

    Handled 的值影响路由事件在沿路由线路向远处传播时的报告或处理方式。在路由事件的事件数据中,如果 Handled 为 true,则通常不再为该特定事件实例调用负责在其他元素上侦听该路由事件的处理程序。这条规则对以下两类处理程序均适用:在 XAML 中附加的处理程序;由语言特定的事件处理程序附加语法(如 += 或 Handles)添加的处理程序。对于最常见的处理程序方案,如果将 Handled 设置为 true,以此将事件标记为“已处理”,则将“停止”隧道路由或冒泡路由,同时,类处理程序在某个路由点处处理的所有事件的路由也将“停止”。

    但是,侦听器仍可以凭借“handledEventsToo”机制来运行处理程序,以便在事件数据中的 Handled 为 true 时响应路由事件。换言之,将事件数据标记为“已处理”并不会真的停止事件路由。您只能在代码或 EventSetter 中使用 handledEventsToo 机制:

    在代码中,不使用适用于一般 CLR 事件的特定于语言的事件语法,而是通过调用 WPF 方法 AddHandler(RoutedEvent, Delegate, Boolean) 来添加处理程序。使用此方法时,请将 handledEventsToo 的值指定为 true。

    在 EventSetter 中,请将 HandledEventsToo 属性设置为 true。

    除了 Handled 状态在路由事件中生成的行为以外,Handled 概念还暗示您应当如何设计自己的应用程序和编写事件处理程序代码。可以将 Handled 概念化为由路由事件公开的简单协议。此协议的具体使用方法由您来决定,但是需要按照如下方式来对 Handled 值的预期使用方式进行概念设计:

    如果路由事件标记为“已处理”,则它不必由该路由中的其他元素再次处理。

    如果路由事件未标记为“已处理”,则说明该路由中前面的其他侦听器已经选择了不注册处理程序,或者已经注册的处理程序选择不操作事件数据并将 Handled 设置为 true。(或者,当前的侦听器很可能就是路由中的第一个点。) 当前侦听器上的处理程序现在有三个可能的操作方案:

    不执行任何操作;该事件保持未处理状态,该事件将路由到下一个侦听器。

    执行代码以响应该事件,但是所执行的操作被视为不足以保证将事件标记为“已处理”。该事件将路由到下一个侦听器。

    执行代码以响应该事件。在传递到处理程序的事件数据中将该事件标记为“已处理”,因为所执行的操作被视为不足以保证将该事件标记为“已处理”。该事件仍将路由到下一个侦听器,但是,由于其事件数据中存在 Handled=true,因此只有 handledEventsToo 侦听器才有机会调用进一步的处理程序。

    这个概念设计是通过前面提到的路由行为来加强的:即使路由中前面的处理程序已经将 Handled 设置为 true,也会增加为所调用的路由事件附加处理程序的难度(尽管仍可以在代码或样式中实现这一目的)。

    有关 Handled、路由事件的类处理的更多信息,以及针对何时适合将路由事件标记为 Handled 的建议,请参见将路由事件标记为已处理以及类处理。

    在应用程序中,相当常见的做法是只针对引发冒泡路由事件的对象来处理该事件,而根本不考虑事件的路由特征。但是,在事件数据中将路由事件标记为“已处理”仍是一个不错的做法,因为这样可以防止元素树中位置更高的元素也对同一个路由事件附加了处理程序而出现意外的副作用。

    类处理程序
    如果您定义的类是以某种方式从 DependencyObject 派生的,那么对于作为类的已声明或已继承事件成员的路由事件,还可以定义和附加一个类处理程序。每当路由事件到达其路由中的元素实例时,都会先调用类处理程序,然后再调用附加到该类某个实例的任何实例侦听器处理程序。

    有些 WPF 控件对某些路由事件具有固有的类处理。路由事件可能看起来从未引发过,但实际上正对其进行类处理,如果您使用某些技术的话,路由事件可能仍由实例处理程序进行处理。同样,许多基类和控件都公开可用来重写类处理行为的虚方法。

    WPF 中的附加事件
    XAML 语言还定义了一个名为“附加事件”的特殊类型的事件。使用附加事件,可以将特定事件的处理程序添加到任意元素中。正在处理该事件的元素不必定义或继承附加事件,可能引发这个特定事件的对象和用来处理实例的目标也都不必将该事件定义为类成员或将其作为类成员来“拥有”。

    WPF 输入系统广泛地使用附加事件。但是,几乎所有的附加事件都是通过基本元素转发的。输入事件随后会显示为等效的、作为基本元素类成员的非附加路由事件。例如,通过针对该 UIElement 使用 MouseDown(而不是在 XAML 或代码中处理附加事件语法),可以针对任何给定的 UIElement 更方便地处理基础附加事件 Mouse..::.MouseDown。

    XAML 中的限定事件名称
    为子元素所引发的路由事件附加处理程序是另一个语法用法,它与类型名称.事件名称 附加事件语法相似,但它并非严格意义上的附加事件用法。可以向公用父级附加处理程序以利用事件路由,即使公用父级可能不将相关的路由事件作为其成员也是如此。请再次考虑下面的示例:

    在这里,在其中添加处理程序的父元素侦听器是 StackPanel。但是,它正在为已经声明而且将由 Button 类(实际上是 ButtonBase,但是可以由 Button 通过继承来使用)引发的路由事件添加处理程序。Button“拥有”该事件,但是路由事件系统允许将任何路由事件的处理程序附加到任何 UIElement 或 ContentElement 实例侦听器,该侦听器可能会以其他方式为公共语言运行库 (CLR) 事件附加侦听器。对于这些限定的事件属性名来说,默认的 xmlns 命名空间通常是默认的 WPF xmlns 命名空间,但是您还可以为自定义路由事件指定带有前缀的命名空间。

    WPF 输入事件
    路由事件在 WPF 平台中的常见用法之一是用于事件输入。在 WPF 中,按照约定,隧道路由事件的名称以单词“Preview”开头。输入事件通常成对出现,一个是冒泡事件,另一个是隧道事件。例如,KeyDown 事件和 PreviewKeyDown 事件具有相同的签名,前者是冒泡输入事件,后者是隧道输入事件。偶尔,输入事件只有冒泡版本,或者有可能只有直接路由版本。在本文档中,当存在具有替换路由策略的类似路由事件时,路由事件主题交叉引用它们,而且托管引用页面中的各个节阐释每个路由事件的路由策略。

    实现成对出现的 WPF 输入事件的目的在于,使来自输入的单个用户操作(如按鼠标按钮)按顺序引发该对中的两个路由事件。首先引发隧道事件并沿路由传播,然后引发冒泡事件并沿其路由传播。顾名思义,这两个事件会共享同一个事件数据实例,因为用来引发冒泡事件的实现类中的 RaiseEvent 方法调用会侦听隧道事件中的事件数据并在新引发的事件中重用它。具有隧道事件处理程序的侦听器首先获得将路由事件标记为“已处理”的机会(先是类处理程序,后是实例处理程序)。如果隧道路由中的某个元素将路由事件标记为“已处理”,则会针对冒泡事件发送已经处理的事件数据,而且将不调用为等效的冒泡输入事件附加的典型处理程序。已处理的冒泡事件看起来好像尚未引发过。此处理行为对于控件合成非常有用,因为此时您可能希望所有基于命中测试的输入事件或者所有基于焦点的输入事件都由最终的控件(而不是它的复合部件)报告。作为可支持控件类的代码的一部分,最后一个控件元素靠近合成链中的根,因此将有机会首先对隧道事件进行类处理,或许还有机会将该路由事件“替换”为更特定于控件的事件。

    为了说明输入事件处理的工作方式,请考虑下面的输入事件示例。在下面的树插图中,leaf element #2 是先后发生的 PreviewMouseDown 事件和 MouseDown 事件的源。

    输入事件的冒泡和隧道
     
    事件的处理顺序如下所示:

    针对根元素处理 PreviewMouseDown(隧道)。

    针对中间元素 1 处理 PreviewMouseDown(隧道)。

    针对源元素 2 处理 PreviewMouseDown(隧道)。

    针对源元素 2 处理 MouseDown(冒泡)。

    针对中间元素 1 处理 MouseDown(冒泡)。

    针对根元素处理 MouseDown(冒泡)。

    路由事件处理程序委托提供对以下两个对象的引用:引发该事件的对象以及在其中调用处理程序的对象。在其中调用处理程序的对象是由 sender 参数报告的对象。首先在其中引发事件的对象是由事件数据中的 Source 属性报告的。路由事件仍可以由同一个对象引发和处理,在这种情况下,sender 和 Source 是相同的(事件处理示例列表中的步骤 3 和 4 就是这样的情况)。

    由于存在隧道和冒泡,因此父元素接收 Source 作为其子元素之一的输入事件。当有必要知道源元素是哪个元素时,可以通过访问 Source 属性来标识源元素。

    通常,一旦将输入事件标记为 Handled,就将不进一步调用处理程序。通常,一旦调用了用来对输入事件的含义进行特定于应用程序的逻辑处理的处理程序,就应当将输入事件标记为“已处理”。

    对于这个有关 Handled 状态的通用声明有一个例外,那就是,注册为有意忽略事件数据 Handled 状态的输入事件处理程序仍将在其路由中被调用。

    通常,隧道事件和冒泡事件之间的共享事件数据模型以及先引发隧道事件后引发冒泡事件等概念并非对于所有的路由事件都适用。该行为的实现取决于 WPF 输入设备选择引发和连接输入事件对的具体方式。实现自己的输入事件是一个高级方案,但是您也可以选择针对自己的输入事件遵循该模型。

    一些类选择对某些输入事件进行类处理,其目的通常是重新定义用户驱动的特定输入事件在该控件中的含义并引发新事件。

           EventSetter 和 EventTrigger
    在样式中,可以通过使用 EventSetter 在标记中包括某个预先声明的 XAML 事件处理语法。在应用样式时,所引用的处理程序会添加到带样式的实例中。只能针对路由事件声明 EventSetter。下面是一个示例。请注意,此处引用的 b1SetColor 方法位于代码隐藏文件中。

    这样做的好处在于,样式有可能包含大量可应用于应用程序中任何按钮的其他信息,让 EventSetter 成为该样式的一部分甚至可以提高代码在标记级别的重用率。而且,EventSetter 还进一步从通用的应用程序和页面标记中提取处理程序方法的名称。

    另一个将 WPF 的路由事件和动画功能结合在一起的专用语法是 EventTrigger。与 EventSetter 一样,只有路由事件可以用于 EventTrigger。通常将 EventTrigger 声明为样式的一部分,但是还可以在页面级元素上将 EventTrigger 声明为 Triggers 集合的一部分或者在 ControlTemplate 中对其进行声明。使用 EventTrigger,可以指定当路由事件到达其路由中的某个元素(这个元素针对该事件声明了 EventTrigger)时将运行的 Storyboard。与只是处理事件并且会导致它启动现有演示图板相比,EventTrigger 的好处在于,EventTrigger 对演示图板及其运行时行为提供更好的控制。

    本主题主要从以下角度讨论路由事件:描述基本概念;就如何以及何时响应各种基元素和基控件中已经存在的路由事件提供指南。但是,您可以为自定义类创建自己的路由事件,还可以创建所有必要的支持(如专用的事件数据类和委托)。路由事件的所有者可以是任何类,但是路由事件只有在由 UIElement 或 ContentElement 派生类引发和处理之后才有用。

    可扩展应用程序标记语言 (XAML) 定义了一个语言组件和称为“附加事件”的事件类型。附加事件的概念允许您针对特定事件为任意元素(而不是为实际定义或继承该事件的元素)添加处理程序。在这种情况下,对象既不会引发该事件,目标处理实例也不会定义或“拥有”该事件。

    先决条件
    本主题假定您已阅读路由事件概述和 XAML 概述。

    附加事件语法
    附加事件具有一种 XAML 语法和编码模式,后备代码必须使用该语法和编码模式才支持附加事件的使用。

    在 XAML 语法中,不仅可以通过事件名称来指定附加事件,而且还可以通过用点 (.) 分隔的事件拥有类型加上事件名称来指定。因为事件名称是使用其拥有类型的名称限定的,所以附加事件语法允许将任何附加事件附加到可以实例化的任何元素上。

    例如,下面是为自定义 NeedsCleaning 附加事件附加处理程序的 XAML 语法:

    请注意 aqua: 前缀;该前缀在本例中是必需的,因为附加事件是来自自定义映射 xmlns 的自定义事件。

    如何在 WPF 中实现附加事件
    在 WPF 中,附加事件由 RoutedEvent 字段来支持,并在引发后通过元素树进行路由。通常,附加事件的源(引发该事件的对象)是系统或服务源,所以运行引发该事件的代码的对象并不是元素树的直接组成部分。

    附加事件的方案
    在 WPF 中,附加事件存在于具有服务级抽象的某些功能区域,例如,对于通过静态 Mouse 类或 Validation 类实现的事件。与服务交互或使用服务的类可以在附加事件语法中使用该事件,也可以选择将附加事件作为路由事件来实现(这是类如何集成服务功能的一部分)。

    尽管 WPF 定义了许多附加事件,但您直接使用或处理附加事件的方案却很有限。一般情况下,附加事件用于实现体系结构目的,但随后即被转发给非附加(使用 CLR 事件“包装”提供支持)路由事件。

    例如,通过针对该 UIElement 使用 MouseDown(而不是在 XAML 或代码中处理附加事件语法),可以针对任何给定的 UIElement 更方便地处理基础附加事件 Mouse..::.MouseDown。附加事件用于实现体系结构目的,因为它允许进一部扩展输入设备。假设的设备只需引发 Mouse..::.MouseDown 即可模拟鼠标输入,而不需要从 Mouse 派生。但是,此方案会涉及事件的代码处理,而附加事件的 XAML 处理则与此方案无关。

    在 WPF 中处理附加事件
    处理附加事件的过程以及您将要编写的处理程序代码与路由事件基本相同。

    一般情况下,WPF 附加事件与 WPF 路由事件并没有太大的区别。不同之处在于如何确定事件的源,以及如何通过类将事件作为成员进行公开。(这还将影响 XAML 处理程序语法。)

    但是,正如前文所述,现有的 WPF 附加事件并不是专门用于在 WPF 中进行处理。该事件的目的常常是实现合成元素,以便向合成中的父元素报告某个状态,在这种情况下,该事件常常在代码中引发,并且还依赖于相关父类中的类处理。例如,Selector 中的项应引发附加的 Selected 事件,该事件随后将由 Selector 类进行类处理,然后可能由 Selector 类转换为不同的路由事件 SelectionChanged。

    将您自己的附加事件定义为路由事件
    如果您从通用 WPF 基类派生,则可以通过在类中包括某些模式方法并使用基类中已经存在的实用工具方法来实现您自己的附加事件。

    模式如下:

    带有两个参数的方法 Add*Handler。第一个参数必须标识事件,而标识的事件必须与方法名称中带有 * 的名称相匹配。第二个参数是要添加的处理程序。该方法必须是公共且静态的,没有任何返回值。

    带有两个参数的方法 Remove*Handler。第一个参数必须标识事件,而标识的事件必须与方法名称中带有 * 的名称相匹配。第二个参数是要移除的处理程序。该方法必须是公共且静态的,没有任何返回值。

    当针对某个元素声明附加事件处理程序属性时,Add*Handler 访问器方法可以加快 XAML 处理。Add*Handler 和 Remove*Handler 方法还可实现对附加事件的事件处理程序存储区的代码访问。

    这个普通模式对于框架中的实际实现还不够精确,因为任何给定的 XAML 读取器实现都可能采用不同的架构在支持语言和体系结构中标识基础事件。这是 WPF 将附加事件作为路由事件来实现的原因之一;用于事件的标识符 (RoutedEvent) 已经由 WPF 事件系统进行定义。而且,路由一个事件也是对附加事件的 XAML 语言级概念的自然实现扩展。

    WPF 附加事件的 Add*Handler 实现包括将路由事件和处理程序用作参数来调用 AddHandler。

    此实现策略和路由事件系统一般将附加事件的处理限制为 UIElement 派生类或 ContentElement 派生类,因为只有这些类才具有 AddHandler 实现。

    例如,下面的代码按照 WPF 将附加事件作为路由事件进行声明的附加事件策略为所有者类 Aquarium 定义 NeedsCleaning 附加事件。

    请注意,用来确定附加事件标识符字段的方法 RegisterRoutedEvent 实际上与用来注册非附加路由事件的方法相同。附加事件和路由事件都是在集中管理的内部存储区中进行注册的。此事件存储区实现则实现了路由事件概述中讨论的“事件作为界面”概念方面的注意事项。

    引发 WPF 附加事件
    通常不需要从代码中引发 WPF 定义的现有附加事件。这些事件采用一般的“服务”概念模型,而服务类(如 InputManager)则负责引发事件。

    但是,如果您根据将 RoutedEvent 作为附加事件基础的 WPF 模型来定义自定义附加事件,则可以使用 RaiseEvent 从任何 UIElement 或 ContentElement 中引发附加事件。引发路由事件(不管是否附加)要求您在元素树中声明一个特定的元素作为事件源;该事件源被报告为 RaiseEvent 调用方。您的服务负责决定将哪个元素报告为元素树中的事件源。

    1. WPF基础之布局系统

    本主题描述 Windows Presentation Foundation (WPF) 布局系统。了解在构造外观醒目、性能优良的用户界面时如何以及何时进行布局计算是非常重要的。

    布局系统
    术语“布局”描述测量和排列 Panel 元素的 Children 集合的成员、然后在屏幕上绘制它们的过程。这是一个计算密集型过程,即 Children 集合越大,执行的计算次数就越多。根据拥有该集合的 Panel 元素所定义的布局行为,还可能会增加复杂性。如果不需要较为复杂的 Panel(如 Grid),则可以使用构造相对简单的布局(如 Canvas),这种布局可产生更佳的性能。

    每当子 UIElement 改变其位置时,布局系统就可能触发一个新的处理过程。因此,了解哪些事件会调用布局系统就很重要,因为不必要的调用可能导致应用程序性能变差。

    简单地说,布局是一个递归系统,实现在屏幕上对元素进行大小调整、定位和绘制。布局系统为 Children 集合的每个成员完成两个处理过程:测量处理过程和排列处理过程。每个子 Panel 均提供自己的 MeasureOverride 和 ArrangeOverride 方法,以实现自己特定的布局行为。不论何时调用布局系统,都会发生以下系列事件。

    子 UIElement 通过首先测量它的核心属性来开始布局过程。

    计算在 FrameworkElement 上定义的大小调整属性,例如 Width、Height 和 Margin。

    应用 Panel 特定逻辑,例如 Dock 方向或堆栈 Orientation。

    测量所有子级后排列内容。

    Children 集合绘制到屏幕。

    如果其他 Children 添加到集合、应用 LayoutTransform 或调用 UpdateLayout 方法,会再次调用此过程。

    下面的小节将更详尽地定义此过程及其调用方式。

    元素边界框
    在 Windows Presentation Foundation (WPF) 中构思应用程序布局时,了解环绕所有元素的边界框非常重要。这有助于您理解布局系统的行为。布局系统使用的每个 FrameworkElement 可以被视为是嵌入到布局分区中的矩形。LayoutInformation 类会公开,可以返回元素布局分配的几何边界或槽。矩形的大小是由系统通过计算可用屏幕空间、任意约束的大小、布局特定属性(如边距和填充)及父 Panel 元素的个别行为来确定的。通过处理此数据,系统将能够计算给定的 Panel 的所有子级的位置。牢记在父元素上定义的哪些大小调整特性(如 Border)会影响其子级,这非常重要。

    例如,请考虑下面的简单布局方案。

    可以使用以下可扩展应用程序标记语言 (XAML) 来实现此布局。

    此单独的 TextBlock 元素是在 Grid 内承载的,而文本仅填充在其所在列的左上角,为 TextBlock 分配的空间实际更大。可以使用 GetLayoutSlot 方法检索任意 FrameworkElement 的边界框。使用此方法,TextBlock 元素的边界框是叠加的,这可能是因为 TextBlock 是在 Grid(它是允许共享布局坐标的 Panel 元素)内承载的。

    现在很明显该元素由白线环绕,分配给 TextBlock 元素的分区实际远远大于其填充的空间。由于还有其他元素添加到 Grid,此分配可能会收缩或扩展,这取决于所添加元素的类型和大小。

    将会使用 GetLayoutSlot 方法(它是一项用于显示元素边界框的有用技术)返回 TextBlock 的布局槽并将其转换成 Path。

    测量和排列子控件
    当呈现 Window 对象的内容时,会自动调用布局系统。为了显示内容,窗口的 Content 必须定义根 Panel(用于定义框架,Children 是按框架在屏幕上组织的)。

    布局的第一个处理过程是测量处理过程,将在此对 Children 集合的每个成员进行计算。此过程将以调用 Measure 方法开始。此方法将在父 Panel 元素的实现中调用,无需为要出现的布局显式调用该方法。

    首先,将计算 UIElement 的本机大小属性,如 Clip 和 Visibility。这将生成一个名为 constraintSize 的传递给 MeasureCore 的值。

    其次,会处理在 FrameworkElement 上定义的框架属性,这将影响 constraintSize 的值。这些属性旨在描述基础 UIElement 的大小调整特性,例如其 Height、Width、Margin 和 Style。上述每个属性均可能改变显示元素所必需的空间。然后,将用 constraintSize 作为一个参数调用 MeasureOverride。

    说明:
    在 Height、Width、ActualHeight 和 ActualWidth 的属性之间存在着差异。例如,ActualHeight 属性是基于其他高度输入和布局系统的计算值。该值是由布局系统本身基于实际呈现处理过程设置的,因此可能稍微小于属性(例如作为输入更改基础的 Height)的设置值。

    由于 ActualHeight 是一个计算值,所以您应该知道,作为布局系统多个操作的结果,该值可能有多次或不断增加的报告的更改。布局系统可能正在计算子元素所需的测量空间、父元素的约束等。

    测量处理过程的最终目标是让子级确定其 DesiredSize,这是在 MeasureCore 调用期间发生的。该值由 Measure 存储,以便在内容排列过程期间使用。

    此排列过程将以调用 Arrange 方法开始。在排列处理过程期间,父 Panel 元素生成一个代表子级边界的矩形。该值会传递给 ArrangeCore 方法以便进行处理。

    ArrangeCore 方法计算子级的 DesiredSize,计算可能影响该元素呈现大小的任何其他边距,并生成 arrangeSize(作为参数传递给 Panel 的 ArrangeOverride)。ArrangeOverride 生成子级的 finalSize,最后,ArrangeCore 方法执行偏移属性(例如边距和对齐方式)的最终计算,并将子级放在其布局槽内。子级无需(且通常不会)填充整个分配空间。然后,控件返回到父 Panel,至此布局过程完成。

     面板元素和自定义布局行为
    Windows Presentation Foundation (WPF) 包括 Panel 元素的派生套件,可以实现许多复杂的布局。常见方案(如堆栈元素)可以使用 StackPanel 元素方便地实现,而较为复杂和自由流动的布局可以使用 Canvas 来实现。

    下表概括了可用的布局元素。

    面板名称
    说明

    Canvas
    定义一个区域,在此区域内,您可以使用相对于 Canvas 区域的坐标显式定位子元素。

    DockPanel
    定义一个区域,在此区域中,您可以使子元素互相水平或垂直排列。

    Grid
    定义由行和列组成的灵活网格区域。

    StackPanel
    将子元素排列成一行(可沿水平或垂直方向)。

    VirtualizingPanel
    为“虚拟化”其子数据集合的 Panel 元素提供一个框架。这是一个抽象类。

    WrapPanel
    从左至右按顺序位置定位子元素,在包含框的边缘处将内容断开至下一行。后续排序按照从上至下或从右至左的顺序进行,具体取决于 Orientation 属性的值。
    对于其所需应用程序布局不可能使用任意预定义的 Panel 元素来实现的方案,您可以通过从 Panel 继承、并重写 MeasureOverride 和 ArrangeOverride 方法来实现自定义布局行为。

    布局性能注意事项
    布局是一个递归过程。Children 集合中的每个子元素会在每次调用系统期间得到处理。因此,应避免在不必要时触发系统。以下提示有助于实现更高的性能。

    其值可能导致布局系统被初始化的相关性属性会标记为公共标志。AffectsMeasure 和 AffectsArrange 提供有关哪个属性值更改会强制执行布局系统的递归更新的有用提示。通常,任何可能影响元素边界框大小的属性应将 AffectsMeasure 标志设置为 true。

    LayoutTransform 可能是影响用户界面 (UI) 内容的非常有用的方式。不过,如果转换的效果无需对其他元素的位置施加影响,则最好改为使用 RenderTransform,因为 RenderTransform 不会调用布局系统。LayoutTransform 会应用其转换,并强制对帐户执行递归布局更新,以获取受影响元素的新位置。

    避免不必要地调用 UpdateLayout。此方法强制递归布局更新,但常常却是不必要的。除非您确认需要进行完整更新,否则请依赖布局系统来为您调用此方法。

    当处理大型 Children 集合时,请考虑使用 VirtualizingStackPanel 而非常规 StackPanel。通过“虚拟化”子元素,VirtualizingStackPanel 仅在内存中保留当前位于父 ViewPort 内的对象。因此,在大多数情况下性能会得到极大改进。

    1. WPF基础之样式设置和模板化

    Windows Presentation Foundation (WPF) 样式设置和模板化是指一套功能(样式、模板、触发器和演示图板),应用程序、文档或用户界面 (UI) 的设计人员使用这些功能可以创建更好的视觉效果,也可以对其产品的统一外观进行标准化。尽管作者或设计人员可以对应用程序的外观逐个进行大量自定义操作,他们还是需要一个功能强大的样式设置和模板化模型,以便在应用程序内部和应用程序之间维护和共享外观。Windows Presentation Foundation (WPF) 就提供了这样的模型。

    WPF 样式设置模型的另一个功能是实现表示形式与逻辑的分离。这意味着,在开发人员使用 C# 或 Visual Basic 进行逻辑编程时,设计人员只需使用 XAML 即可设计程序的外观。

    本概述将讨论样式设置和模板化示例简介应用程序,该应用程序具有两个 TextBlock 元素,以及一个绑定到图像列表的 ListBox 控件:


    本概述主要讨论该应用程序的样式设置和模板化方面,而不讨论任何数据绑定概念。

    另外,了解资源也很重要,正是资源使得样式和模板得以重用。

    样式基础知识
    您可以将 Style 看作是将一组属性值应用到多个元素的捷径。例如,考虑下面的 TextBlock 元素及其默认外观:


    通过直接对每个 TextBlock 元素设置 FontSize 和 FontFamily 等属性可以更改默认外观。但是,如果希望 TextBlock 元素可以共享某些属性,则可以在 XAML 文件的 Resources 节中创建一个 Style,如下所示:

    将样式的 TargetType 设置为 TextBlock 类型时,该样式会应用于窗口中的所有 TextBlock 元素。

    现在,TextBlock 元素的外观如下:

    扩展样式
    您可能希望两个 TextBlock 元素共享某些属性值(如 FontFamily 和居中的 HorizontalAlignment),同时还希望文本“我的图片”具有某些其他属性。在第一个样式的基础上创建一个新样式可以达到这一目的,如下所示:

    请注意,已为上面的样式提供了 x:Key。若要应用该样式,请将 TextBlock 的 Style 属性设置为 x:Key 值,如下所示:

     现在,此 TextBlock 样式的 HorizontalAlignment 值为 Center,FontFamily 值为 Comic Sans MS,FontSize 值为 26,Foreground 值设置为示例所示的 LinearGradientBrush。请注意,我们已重写了基础样式的 FontSize 值。如果有多个 Setter 对 Style 的同一属性进行设置,则最后声明的 Setter 优先。

    下面演示 TextBlock 元素现在的外观:

    此 TitleText 样式扩展了为 TextBlock 类型创建的样式。此外,使用 x:Key 值也可以扩展具有 x:Key 的样式。

    TargetType 属性 (Property) 和 x:Key 属性 (Attribute) 的关系
    如第一个示例所示,如果将 TargetType 属性设置为 TextBlock 而不为样式分配 x:Key,样式就会应用于所有 TextBlock 元素。这种情况下,x:Key 隐式设置为 {x:Type TextBlock}。这意味着,如果将 x:Key 值显式设置为 {x:Type TextBlock} 之外的任何值,Style 就不会自动应用于所有 TextBlock 元素。此时,必须通过使用 x:Key 值,将样式显式应用于 TextBlock 元素。如果样式位于资源部分,并且未设置样式的 TargetType 属性,则必须提供 x:Key。

    除了提供 x:Key 的默认值之外,TargetType 属性还指定要应用 setter 属性的类型。如果未指定 TargetType,则必须通过语法 Property="ClassName.Property",用类名限定 Setter 对象的属性。例如,必须将 Property 设置为 "TextBlock.FontSize" 或 "Control.FontSize",而不要设置 Property="FontSize"。

    此外,还应注意,很多 WPF 控件都是由其他 WPF 控件组合而成的。如果创建要应用于某个类型的所有控件的样式,可能会得到意想不到的结果。例如,如果针对 Window 中的 TextBlock 类型创建一个样式,则该样式会应用于窗口中的所有 TextBlock 控件,即使 TextBlock 是另一个控件(如 ListBox)的组成部分也不例外。

    样式和资源
    任何派生自 FrameworkElement 或 FrameworkContentElement 的元素都可以使用样式。声明样式的最常见方式是将样式作为 XAML 文件的 Resources 节中的资源,如前面的示例所示。由于样式是资源,因此同样遵循所有资源都适用的范围规则:样式的声明位置决定样式的应用范围。例如,如果在应用程序定义 XAML 文件的根元素中声明样式,则样式可在应用程序范围内使用。如果创建的是导航应用程序,并在该应用程序的某个 XAML 文件中声明样式,则该样式只能在该 XAML 文件中使用。

    以编程方式设置样式
    若要以编程方式向元素分配命名样式,请从资源集合中获取该样式,然后将其分配给元素的 Style 属性。请注意,资源集合中的项是 Object 类型,因此,将检索到的样式分配给 Style 属性之前,必须将该样式强制转换为 Style。例如,若要对名为 textblock1 的 TextBlock 设置定义的 TitleText 样式,请执行以下操作:

    请注意,样式一旦应用,便会密封并且无法更改。如果要动态更改已应用的样式,必须创建一个新样式来替换现有样式。

    您可以创建一个根据自定义逻辑选择要应用的样式的对象。

    绑定、动态资源和事件处理程序
    请注意,使用 Setter.Value 属性可以指定 绑定标记扩展或 DynamicResource 标记扩展。

    到目前为止,我们只讨论了如何使用 setter 设置属性值。在样式中也可以指定事件处理程序。

    数据模板
    在本示例应用程序中,有一个绑定到照片列表的 ListBox 控件。

    此 ListBox 当前的外观如下所示:


    大多数控件都具有某种类型的内容,这些内容通常来自绑定到的数据。在本示例中,数据为照片列表。在 WPF 中,使用 DataTemplate 可以定义数据的可视表示形式。基本上,输入 DataTemplate 的内容决定了数据在呈现的应用程序中的外观。

    在我们的示例应用程序中,每个自定义 Photo 对象都具有一个字符串类型的 Source 属性,该属性指定图像的文件路径。当前,照片对象显示为文件路径。

    对于要显示为图像的照片,可以将 DataTemplate 作为资源创建:

    请注意,DataType 属性与 Style 的 TargetType 属性非常相似。如果 DataTemplate 位于资源部分,并且将 DataType 属性指定为某个类型,也不为其分配 x:Key,则只要该类型出现,便会应用 DataTemplate。任何时候都可以为 DataTemplate 分配 x:Key,然后将其设置为 DataTemplate 类型的属性(如 ItemTemplate 属性或 ContentTemplate 属性)的 StaticResource。

    实质上,上面示例的 DataTemplate 确定只要存在 Photo 对象,该对象就应作为 Image 显示在 Border 中。通过此 DataTemplate,应用程序现在的外观如下:


    数据模板化模型还提供其他功能。例如,如果要使用 HeaderedItemsControl 类型(如 Menu 或 TreeView)显示包含其他集合的集合数据,则可以使用 HierarchicalDataTemplate。另一个数据模板化功能是 DataTemplateSelector,利用这一功能可以根据自定义逻辑选择要使用的 DataTemplate。有关更多信息,请参见数据模板概述,该概述对不同的数据模板化功能进行了更加深入的讨论。

    控件模板

    请注意,我们的照片显示为图片,我们要水平显示这些照片,而不是垂直显示;我们希望 ListBox 是水平的。

    不使用 ControlTemplate
    首先,要使 ListBox 水平,不一定要使用 ControlTemplate,明确这一点很重要。ListBox 具有 ItemsPanel 属性,利用该属性可以设置 ItemsPanelTemplate,即控制 ListBox 的项的布局的模板。一种方法是只创建 ListBox 样式,然后设置 ItemsPanel 属性,如下例所示:

    例表明,除了替换 ControlTemplate 之外,可能还有其他方法,这取决于具体的情况。在本示例中,如果希望获得具有其他属性(如圆角)的水平 ListBox,则需要使用 ListBox 的 ControlTemplate。

    在提供示例来演示具体操作之前,首先需要说明 ControlTemplate 的概念。

    什么是 ControlTemplate?
    大多数控件都具有外观和行为。以按钮为例:外观是可以按下的凸起区域,行为是在响应单击时所引发的 Click 事件。

    有时,控件可以提供所需行为,但不具有所需外观。到目前为止,我们已经演示了可以使用样式 setter 来设置属性值,从而影响控件的外观。但是,若要更改控件的结构,或对组成控件的组件设置属性值,就需要使用 ControlTemplate。

    在 WPF 中,控件的 ControlTemplate 定义控件的外观。通过为控件定义新的 ControlTemplate 可以更改控件的结构和外观。很多情况下,这种方法都足够灵活,您不需要自己编写自定义控件。如果没有为控件定义自己的 ControlTemplate,则可以获取与系统主题匹配的默认模板,该模板向 Button 控件提供默认外观。

    请注意:一旦为控件创建 ControlTemplate,就会替换整个 ControlTemplate。例如,可以通过以下方式定义 Button ControlTemplate。

    请注意,ContentPresenter 元素只标记 Button 的 Content 应出现在何处。后面有一节专门展开详细讨论。

    应用此模板之后,Button 显示为 Ellipse:


    请记住,当 Button 具有焦点或按下时,其外观都是将替换的按钮的默认外观的组成部分。因此,您可能希望定义按钮按下时的外观,这取决于您的具体需要。

    如果要创建 ControlTemplate,使用 ControlTemplate 示例 是最好的入门方法。如果确实需要查看控件的组成部分,可以查看位于主题的主题文件,也可以使用 XAMLPad(随 Windows 软件开发工具包 (SDK) 安装的应用程序)的 Show Visual Tree 功能。

    创建 ControlTemplate
    现在,继续演示示例,我们创建一个 ControlTemplate,它定义一个水平的圆角 ListBox。若要替换控件的 ControlTemplate,请将 Template 属性设置为新的 ControlTemplate。

    以这种方式设置 Template 属性,实际上与使用 Style 设置其他控件属性没有区别:您将 Style 用作一个工具来帮助设置 Template 属性。也就是说,设置 ControlTemplate 的另一种方法是直接设置控件的 Template 属性。如果以这种方式设置,则会在 Resources 节中创建一个 ControlTemplate,并为它提供 x:Key,然后将它作为静态资源使用。

    如上例所示,ControlTemplate 类具有 TargetType 属性,该属性类似于 Style 类的 TargetType 属性。但要注意,与 Style 和 DataTemplate 不同,ControlTemplate 对象没有隐式键的概念。换言之,如果有一个独立 ControlTemplate,其 TargetType 属性设置为某个类型,则 ControlTemplate 不会自动应用于该类型。另请注意,如果模板定义包含 ContentPresenter,则 ControlTemplate 需要 TargetType 属性。

    尝试使用 ControlTemplate。例如,用 WrapPanel 替换 StackPanel,将 ScrollViewer 的 HorizontalScrollBarVisibility 属性设置为 Disabled,然后将 ListBox 的宽度设置为 300。(只有第一行空间不足时,WrapPanel 才会将项放置到下一行。如果没有将 ScrollViewer 的 HorizontalScrollBarVisibility 属性设置为 Disabled,由于可以滚动到末尾,则第一行不会空间不足。因此,WrapPanel 不会对项进行换行。)

    IsItemsHost 属性
    在此示例中,一个必需的重要属性是 IsItemsHost 属性。IsItemsHost 属性用于指示在 ItemsControl(如处理项列表的 ListBox 控件)的模板中,生成的元素应放在什么位置。如果将 StackPanel 的这一属性设置为 true,则添加到 ListBox 的所有项都将进入 StackPanel。请注意,此属性只对 Panel 类型有效。

    ItemsPresenter 和 ContentPresenter
    请注意,如果以这种方式在 ControlTemplate 中指定一个面板并将其标记为 IsItemsHost,控件的用户不使用 ControlTemplate 就无法替换 ItemsPanel。因此,除非您确信必须使用模板才能替换面板,否则不要采用这种方式。此外,您也可以使用 ItemsPresenter 元素来标记项的位置,然后通过设置 ItemsPanel 属性来指定 ItemsPanelTemplate。ItemsPanelTemplate 页提供了一个示例,为您演示如何操作。

    如果要创建 ContentControl(如 Button)的模板,则对应元素为 ContentPresenter。同样,将此元素放置到 ContentControl 类型的 ControlTemplate 中,可以指示内容应在什么位置显示,如什么是 ControlTemplate? 一节中的示例所示。有关其他示例,请参见 Label ControlTemplate 示例和 ListBoxItem ControlTemplate 示例。

    TemplateBinding
    在上一示例中,需要注意的另一个重点是设置为 {TemplateBinding ListBox.Background} 的 Background 值。它只是指示 Border 的 Background 应与 ListBox 上设置的 Background 值同步。TemplatBinding 与 Binding 类似。实际上,TemplatBinding 比 Binding 更有效,但功能更弱;使用 TemplatBinding 等效于使用 Source 属性设置为 RelativeSource.TemplatedParent 的 Binding。

    若要使控件用户能够控制某些属性的值,可以在 ControlTemplate 中使用 TemplateBinding。TemplateBinding 是一个由 TemplateBindingExtension 类表示的标记扩展。

    您可能已经注意到,DataTemplate 和 ControlTemplate 的相似之处在于它们的内容变成了对象的外观。通过 ListBox ControlTemplate 定义,应用程序现在的外观如下:


    触发器
    Style、ControlTemplate 和 DataTemplate 都具有 Triggers 属性,该熟悉可以包含一组触发器。某个属性值更改时,或某个事件引发时,触发器会相应地设置属性或启动操作(如动画操作)。

    属性触发器
    为了演示如何使用触发器来设置属性,我们将每个 ListBoxItem 都设置为部分透明(除非它被选中)。

    下面的样式将 ListBoxItem 的 Opacity 值设置为 0.5。但是,当 IsSelected 属性为 true 时,Opacity 设置为 1.0:

    此示例使用 Trigger 来设置属性值,但请注意,Trigger 类还具有 EnterActions 和 ExitActions 属性,通过这两个属性,触发器可以执行操作。

    请注意,我们还将 ListBoxItem 的 MaxHeight 属性设置为 75。在下面的屏幕快照中,选中的项是第三项:

    EventTrigger 和 Storyboard
    我们刚刚演示了 Trigger 根据某个属性的值来设置属性值或启动操作。另一种类型的触发器是 EventTrigger,它根据事件的引发来启动一组操作。例如,下面的 EventTrigger 对象指定当鼠标指针进入 ListBoxItem 时,MaxHeight 属性在 0.2 秒时间内以动画方式增大为值 90。当鼠标离开该项时,该属性在 1 秒时间内还原为原始值。请注意为何无需为 MouseLeave 动画指定 To 值。这是因为动画能够跟踪原始值。

    在下面的屏幕快照中,鼠标指向第三项:


    MultiTrigger、DataTrigger 和 MultiDataTrigger
    除了 Trigger 和 EventTrigger 之外,还有其他类型的触发器。通过 MultiTrigger,可以根据多个条件来设置属性值。如果条件的属性是经过数据绑定的,则可以使用 DataTrigger 和 MultiDataTrigger。

    共享的资源和主题
    典型 Windows Presentation Foundation (WPF) 应用程序可能具有多个在整个应用程序范围内应用的用户界面 (UI) 资源。概括地说,这组资源可视为应用程序的主题。通过使用封装为 ResourceDictionary 类的资源字典,Windows Presentation Foundation (WPF) 支持将用户界面 (UI) 资源打包为主题。

    Windows Presentation Foundation (WPF) 主题是使用样式设置和模板化机制定义的,Windows Presentation Foundation (WPF) 公开该机制,用于自定义任何元素的可视对象。

    Windows Presentation Foundation (WPF) 主题资源存储在嵌入式资源字典中。这些资源字典必须嵌入到已签名的程序集中,它们既可以嵌入到代码自身所在的程序集中,也可以嵌入到并行程序集中。对于包含 Windows Presentation Foundation (WPF) 控件的程序集 PresentationFramework.dll,主题资源在一系列并行程序集中。

    搜索元素样式时,主题是最后查找的位置。通常,搜索首先沿元素树向上查找相应资源,然后在应用程序资源集合中查找,最后查询系统。这为应用程序的作者提供了机会,让他们可以在到达主题之前,在树或应用程序级重新定义任何对象的样式。

    您可以将各资源字典分别定义为单个文件,这样就可以在多个应用程序中重用某个主题。通过定义多个提供相同类型资源、但具有不同值的资源字典,也可以创建可交换的主题。在设计应用程序外观时,建议在应用程序级重新定义这些样式或其他资源。

    若要在多个应用程序中共享一组资源(包括样式和模板),可以创建一个 XAML 文件并定义一个 ResourceDictionary。例如,请看下面的屏幕快照,它显示了使用 ControlTemplates 设置样式的示例的一部分:


    如果查看示例中的 XAML 文件,您会注意到所有文件都包含以下内容:

    这是共享的 shared.xaml,该文件定义一个 ResourceDictionary,该资源字典包含一组样式和画笔资源,使示例中的控件具有了一致的外观。

    1. 详谈WPF开发中的数据虚拟化

    UI虚拟化

    当一个WPF的ItemControl被绑定到一个大型集合的数据源时,如果可以UI虚拟化,该控件将只为那些在可以看到的项创见可视化的容器(加上面和下面的少许)。这是一个完整集合中有代表性的一小部分。用户移动滚动条时,将为那些滚动到可视区域的项创建新的可视化容器,那些不再可见的项的容器将被销毁。当容器设置为循环使用时,它将再使用可视化容器代替不断的创建和销毁可视化容器,避免对象的实例化和垃圾回收器的过度工作。

    数据虚拟化

    数据虚拟化是指绑定到ItemControl的真实的数据对象的归档虚拟化的时间段。数据虚拟化不是由WPF提供的。作为对比,基本数据对象的小集合对内存的消耗不是很多;但是,大集合的内存消耗是非常严重的。另外,真实的检索数据(例如,从数据库)和实例化数据对象是很耗时的,尤其当是网络数据调用时。因此,我们希望使用数据虚拟化机制来限制检索的数据的数量和在内存中生成数据对象的数量。

    解决方案

    总览

    这个解决方案是只在ItemControl绑定到IList接口的实现时起作用,而不是IEumerable的实现,它并不枚举整个列表,而只是读取需要显示的项。它使用Count属性判断集合的大小,推测并设置滚动的范围。然后使用列表索引重新确定要在屏幕上显示的项。因此,创建一个可以报告具有大量项的,并且可以只检索需要的项的IList。

    IItemsProvider 为了利用这个解决方案,下面的数据源必须能提供集合中项的数量,并且能够提供完整集合的小块(或页)。这需要在IItemsProvider接口封装。

    ///
    /// Represents a provider of collection details.
    ///
    /// The type of items in the collection.
    public interface IItemsProvider
    {
    ///
    /// Fetches the total number of items available.
    ///
    ///
    int FetchCount();
    ///
    /// Fetches a range of items.
    ///
    /// The start index.
    /// The number of items to fetch.
    ///
    IList FetchRange(int startIndex, int count);
    }

    如果下面的查询是一个数据库查询,它是一个利用大多数据库供应商都提供的COUNT()聚集函数和OFFSET与LIMIT表达式的一个IItemProvider接口的一个简单实现。

    VirtualizingCollection 这是一个执行数据虚拟化的IList的实现。VirtualizingCollection(T)把整个集合分装到一定数量的页中。根据需要把页加载到内存中,在不需要时从释放。

    下面讨论我们有兴趣的部分。详细信息请参考附件中的源代码项目。

    IList实现的第一个方面是实现Count属性。它通常被ItemsControl用来确定集合的大小,并呈现适当的滚动条。

    private int _count = -1;
    public virtual int Count
    {
    get
    {
    if (_count == -1)
    {
    LoadCount();
    }
    return _count;
    }
    protected set
    {
    _count = value;
    }
    }
    protected virtual void LoadCount()
    {
    Count = FetchCount();
    }
    protected int FetchCount()
    {
    return ItemsProvider.FetchCount();
    }

    Count属性使用延迟和懒惰加载(lazy loading)模式。它使用特殊值-1作为未加载的标识。当第一次读取它时,它从ItemsProvider加载其实际的数量。

    IList接口的实现的另一个重要方面是索引的实现。

    private int _count = -1;
    public virtual int Count
    {
    get
    {
    if (_count == -1)
    {
    LoadCount();
    }
    return _count;
    }
    protected set
    {
    _count = value;
    }
    }
    protected virtual void LoadCount()
    {
    Count = FetchCount();
    }
    protected int FetchCount()
    {
    return ItemsProvider.FetchCount();
    }

    这个索引是这个解决方案的一个聪明的操作。首先,它必须确定请求的项在哪个页(pageIndex)中,在页中的位置(pageOffset),然后调用RequestPage()方法请求该页。

    附加的步骤是然后根据pageOffset加载后一页或前一页。这基于一个假设,如果用户正在浏览第0页,那么他们有很高的机率接下来要滚动浏览第1页。提前把数据取来,就可以无延迟的显示。

    然后调用CleanUpPages()清除(或卸载)所有不再使用的页。

    最后,放置页不可用的一个防御性的检查, 当RequestPage没有同步操作时是必要的,例如在子类AsyncVirtualizingCollection中。

    // ...
    private readonly Dictionary> _pages =
    new Dictionary>();
    private readonly Dictionary _pageTouchTimes =
    new Dictionary();
    protected virtual void RequestPage(int pageIndex)
    {
    if (!_pages.ContainsKey(pageIndex))
    {
    _pages.Add(pageIndex, null);
    _pageTouchTimes.Add(pageIndex, DateTime.Now);
    LoadPage(pageIndex);
    }
    else
    {
    _pageTouchTimes[pageIndex] = DateTime.Now;
    }
    }
    protected virtual void PopulatePage(int pageIndex, IList page)
    {
    if (_pages.ContainsKey(pageIndex))
    _pages[pageIndex] = page;
    }
    public void CleanUpPages()
    {
    List keys = new List(_pageTouchTimes.Keys);
    foreach (int key in keys)
    {
    // page 0 is a special case, since the WPF ItemsControl
    // accesses the first item frequently
    if ( key != 0 && (DateTime.Now -
    _pageTouchTimes[key]).TotalMilliseconds > PageTimeout )
    {
    _pages.Remove(key);
    _pageTouchTimes.Remove(key);
    }
    }
    }

    页存储在以页索引为键的字典(Dictionary)中。一个附加的字典(Dictionary)记录着每个页的最后存取时间,它用于在CleanUpPages()方法中移除较长时间没有存取的页。

    protected virtual void LoadPage(int pageIndex)
    {
    PopulatePage(pageIndex, FetchPage(pageIndex));
    }
    protected IList FetchPage(int pageIndex)
    {
    return

    为完成该解决方案,FetchPage()执行从ItemProvider中抓取数据,LoadPage()方法完成调用PopulatePage方法获取页并把该页存储到字典(Dictionary)中的工作。

    看起来好象有一些太多的不全逻辑的方法(a few too many inconsequential methods),但这样设计是有原因的:每一个方法做且只做一件事,有助于提高代码的可读性,并使在子类中进行功能扩展和维护变得容易,下面可以看到。

    类VirtualizingCollection实现了数据虚拟化的基本目标。不幸的是,在使用中,它有一个严重不足:数据抓取方法是全部同步执行的。这就是说它们要在UI线程中执行,造成一个缓慢的程序

    AsyncVirtualizingCollection类AsyncVirtualizingCollection继承自VirtualizingCollection,重载了Load方法,以实现数据的异步加载。

    WPF中异步数据源的关键是在数据抓取完成后必须通知UI的数据绑定。在规则的对象中,是通过实现INotifyPropertyChanged接口实现的。对一个集合的实现,需要紧密的关系,INotifyCollectionChanged。那是ObservableCollection要使用的接口。

    public event NotifyCollectionChangedEventHandler CollectionChanged;
    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
    NotifyCollectionChangedEventHandler h = CollectionChanged;
    if (h != null)
    h(this, e);
    }
    private void FireCollectionReset()
    {
    NotifyCollectionChangedEventArgs e =
    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
    OnCollectionChanged(e);
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
    PropertyChangedEventHandler h = PropertyChanged;
    if (h != null)
    h(this, e);
    }
    private void FirePropertyChanged(string propertyName)
    {
    PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
    OnPropertyChanged(e);
    }

    实现了INotifyCollectionChanged接口和INotifyPropertyChanged接口。提供数据绑定弹性最大化。这个实现没有任何要注意的。

    protected override void LoadCount()
    {
    Count = 0;
    IsLoading = true;
    ThreadPool.QueueUserWorkItem(LoadCountWork);
    }
    private void LoadCountWork(object args)
    {
    int count = FetchCount();
    SynchronizationContext.Send(LoadCountCompleted, count);
    }
    private void LoadCountCompleted(object args)
    {
    Count = (int)args;
    IsLoading = false;
    FireCollectionReset();
    }

    在重载的LoadCount()方法中,抓取是由ThreadPool(线程池)异步调用的。一旦完成,就会重置Count,UI的更新是由INotifyCollectionChanged接口调用FireCollectionReset方法实现的。注意LoadCountCompleted方法会在UI线程通过SynchronizationContext再一次被调用。假定集合的实例在UI线程中被创建,SynchronationContext属性就会被设置。

    protected override void LoadPage(int index){IsLoading = true;

    ThreadPool.QueueUserWorkItem(LoadPageWork, index);}

    private void LoadPageWork(object args){   

    int pageIndex = (int)args;    IList page = FetchPage(pageIndex);

    SynchronizationContext.Send(LoadPageCompleted, new object[]{pageIndex, page});}

    private void LoadPageCompleted(object args){int pageIndex=(int)((object[]) args)[0];

    IList page = (IList)((object[])args)[1];    PopulatePage(pageIndex, page);

    IsLoading = false;    FireCollectionReset();}

    页数据的加载遵循相同的惯例,再一次调用FireCollectionReset方法更新用户UI。

    也要注意IsLoading属性是一个简单的标识,可以用来告知UI集合正在加载。当IsLoading改变后,由INotifyPropertyChanged机制调用FirePropertyChanged方法更新UI。

    public bool IsLoading{ get{ return _isLoading; }   

    set {if ( value != _isLoading ){  _isLoading = value;

    FirePropertyChanged("IsLoading");}    }}

    演示项目

    为了演示这个解决方案,我创建了一个简单的示例项目(包括附加的源代码项目)。

    首先,创建一个IItemsProvider的一个实现,它通过使用线程休眠来模拟网络或磁盘行为的延迟提供虚拟数据。

    public class DemoCustomerProvider : IItemsProvider
    {
    private readonly int _count;
    private readonly int _fetchDelay;
    public DemoCustomerProvider(int count, int fetchDelay)
    {
    _count = count;
    _fetchDelay = fetchDelay;
    }
    public int FetchCount()
    {
    Thread.Sleep(_fetchDelay);
    return _count;
    }
    public IList FetchRange(int startIndex, int count)
    {
    Thread.Sleep(_fetchDelay);
    List list = new List();
    for( int i=startIndex; i
            {
    Customer customer = new Customer {Id = i+1, Name = "Customer " + (i+1)};
    list.Add(customer);
    }
    return list;
    }
    }

    普遍存在的Customer(消费者)对象作为集合中的项。

    为了允许用户试验不同的列表实现,创建一个包含ListView的简单WPF窗体。

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    
    Title="Data Virtualization Demo - By Paul McClean" Height="600" Width="600">
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
                      TextAlignment="Right" VerticalAlignment="Center"/>
    
    
    
                      Text="1000000" Width="60" VerticalAlignment="Center"/>
    
    
    
                      TextAlignment="Right" VerticalAlignment="Center"/>
    
    
    
                      Text="1000" Width="60" VerticalAlignment="Center"/>
    
    
    
    
    
    
    
    
    
    
    
    
    
                          TextAlignment="Right" VerticalAlignment="Center"/>
    
    
    
                          Margin="5" Content="List(T)" VerticalAlignment="Center"/>
    
    
    
                          Margin="5" Content="VirtualizingList(T)" 
    
    VerticalAlignment="Center"/>
    
    
    
                          Margin="5" Content="AsyncVirtualizingList(T)" 
    
    IsChecked="True" VerticalAlignment="Center"/>
    
    
    
    
    
    
    
                          TextAlignment="Right" VerticalAlignment="Center"/>
    
    
    
                          Text="100" Width="60" VerticalAlignment="Center"/>
    
    
    
                          TextAlignment="Right" VerticalAlignment="Center"/>
    
    
    
                          Text="30" Width="60" VerticalAlignment="Center"/>
    
    
    
    
    
    
    
    
    
    
    
                  VerticalAlignment="Center"/>
    
    
    
                  Width="80" VerticalAlignment="Center"/>

     

     

    XAML语法

    1. XAML语法术语

    本主题定义用来描述可扩展应用程序标记语言 (XAML) 语法的各个元素的术语。这些术语将在本软件开发工具包 (SDK) 的其余部分中经常用到。本主题扩展了 XAML 概述中所介绍的基本术语。

    XAML 语法术语的起源
    此处定义的 XAML 语法术语在 XAML 语言规范中也有定义或引用。XAML 是一种基于 XML 且遵循 XML 结构规则的语言。其中的术语共享自或基于描述 XML 语言或 XML 文档对象模型 (DOM) 时的常用术语。

     对象元素语法
    对象元素语法是一种 XAML 标记语法,它通过声明 XML 元素来实例化公共语言运行库 (CLR) 类或结构。此语法与其他标记语言(例如 HTML)的元素语法相似。对象元素语法以左尖括号 (<) 开头,其后紧跟正进行实例化的类或结构的类型名称。类型名称后面可以有零个或多个空格,对于对象元素还可以声明零个或多个属性,并用一个或多个空格来分隔每个“属性名="值"”对。最后,必须存在下列一种情况:

    元素和标记必须用正斜杠 (/) 和紧跟的右尖括号 (>) 结尾。

    开始标记必须以右尖括号 (>) 结尾。其他对象元素、属性元素或内部文本可以跟在开始标记后面。对象元素还必须存在等效的结束标记,并与其他开始标记/结束标记对形成正确的嵌套和平衡。

    例如,下面的示例是一个对象元素语法,该语法实例化 Button 类的一个新实例,而且还指定了一个 Name 属性及其值:

    下面的示例是一个还包括可扩展应用程序标记语言 (XAML) 内容属性语法的对象元素语法。其中包含的内部文本将用来设置 TextBox 可扩展应用程序标记语言 (XAML) 内容属性 Text。

     

    属性语法
    属性语法是一种 XAML 标记语法,该语法通过针对元素声明属性 (Attribute) 来设置属性 (Property) 值或者命名事件的事件处理程序。元素总是通过对象元素语法来声明。属性名必须与属性或事件的 CLR 成员名称相匹配。属性名后面是赋值运算符 (=)。属性值必须是一个用双引号 (") 引起来的字符串。

    为了能够通过属性语法进行设置,属性必须是公共的、可读写的,而且必须具有一个可以由 XAML 处理器实例化或引用的属性值类型。对于事件来说,事件必须是公共的而且必须具有一个公共委托。属性或事件必须是由包含对象元素实例化的类或结构的成员。

    属性值由下面的操作之一,按照如下处理顺序进行填充:

    如果 XAML 处理器遇到一个大括号,或者遇到一个从 MarkupExtension 派生的对象元素,则将首先计算所引用的标记扩展(而不是将该扩展作为字符串来处理),而且将使用由标记扩展返回的对象。在许多情况下,由标记扩展返回的对象将是对现有对象的引用,或者是一个将计算推迟到运行时的表达式,而不是一个新对象。

    如果该属性 (Property) 是用指定的 TypeConverter 声明的,或者该属性 (Property) 的值类型是用属性 (Attribute) 化 TypeConverter 声明的,则该属性 (Attribute) 的字符串值将作为转换输入提交到类型转换器,该转换器将返回一个新的对象实例。

    如果没有 TypeConverter,则将尝试直接转换为属性类型。最后一个级别是直接在基元类型之间转换,或者在枚举中检查名称(这将返回匹配的值)。

    例如,在使用上面所显示的标记时,可以使用下面的属性 (Attribute) 语法示例为 Name 属性 (Property) 赋予字符串值:

    Name 属性是 Button 类的成员表的成员。Button 是用来定义 Name 的 FrameworkElement 类的派生类。

    属性值的处理
    包含在左引号和右引号之间的字符串值是由 XAML 处理器处理的。对于属性来说,默认处理行为是由基础 CLR 属性的类型确定的。如果该属性 (Property) 是基元类型,则会基于字符串到相关基元类型的隐式转换来赋予属性 (Attribute) 值。如果该属性是一个枚举,则字符串会被视为由该枚举定义的名称,而且将从枚举中返回匹配的值。如果该属性 (Property) 既不是基元类型又不是枚举,则属性 (Attribute) 值必须由针对该属性 (Property) 本身或者目标类型声明的类型转换器来处理。类型转换器必须提供一个能够接受字符串的转换机制,该转换机制必须生成基础 CLR 属性类型的实例。还可以通过标记扩展来推迟转换步骤。

    枚举属性值
    XAML 中的枚举值由 Enum 结构的本机方法在内部处理。

    对于无标志的枚举值,本机行为是处理属性值的字符串并将它解析为某个枚举值。您不必像在代码中那样指定格式为枚举.值 的枚举,而是仅指定值,枚举 将从所设置属性的类型推断。如果您指定格式为枚举.值 的属性,它将无法正确解析。

    对于按标志枚举,该行为基于 Enum..::.Parse 方法。您可以通过用逗号分隔每个值来为按标志枚举指定多个值。但是,您不能合并不按标志的枚举值。例如,不能试图使用逗号语法来创建作用于无标志枚举多个条件的 Trigger:

    在 WPF 中,能够支持 XAML 中可设置属性的按标志枚举极为罕见。但是,StyleSimulations 就是这样的一个枚举。例如,可以使用逗号分隔的按标志属性语法来修改在 Glyphs 类的“Remarks”(备注)部分中提供的示例;StyleSimulations = "BoldSimulation" 可能会变成 StyleSimulations = "BoldSimulation,ItalicSimulation"。KeyBinding..::.Modifiers 是另一个属性,在该属性中可以指定多个枚举值。但是,此属性是一个特例,因为 ModifierKeys 枚举支持其自身的类型转换器。修饰符的类型转换器使用加号 (+) 而不是逗号 (,) 作为分隔符,因此在标记中支持用更传统的语法来表示组合键(如“Ctrl+Alt”)。

    属性引用和事件成员名称引用
    在指定属性 (Attribute) 时,可以引用您已经为包含对象元素实例化的 CLR 类型的成员表中存在的任何属性 (Property) 或事件。

    或者,可以独立于包含对象元素来引用附加属性或附加事件。

    对于可通过默认命名空间访问的任何对象中的任何事件,还可以通过使用类型名.事件 部分限定名来命名。此语法支持为路由事件附加处理程序,在路由事件中,处理程序旨在处理子元素中的事件路由,但是父元素在其成员表中并不拥有该事件。此语法与附加事件语法相似,但此处的事件不是真正的附加事件。相反,您引用的是具有限定名称的事件。

    属性 (Property) 名有时作为属性 (Attribute) 值提供,而不是作为属性 (Attribute) 名提供,属性 (Property) 名还可以包括限定符,例如以所有者类型.依赖项属性名称 格式指定的属性。在 XAML 中编写样式或模板时,此情况较为常见。以属性 (Attribute) 值形式提供的属性 (Property) 名具有不同的处理规则,这些规则由所设置的属性 (Property) 类型以及某些上下文因素(如样式或模板是否具有目标类型)来控制。

    当属性 (Attribute) 值描述属性 (Property) 之间的关系时,也可以使用属性 (Property) 名。此功能可用于数据绑定和演示图板目标,而且由 PropertyPath 类及其类型转换器启用。

    隐式集合元素会在逻辑树中创建一个成员,即使它在标记中不显示为元素也是如此。通常,所拥有类型的构造函数针对作为其属性之一的集合执行实例化,这会将该集合添加到树中。

    说明:
    由 WPF XAML 处理器执行的集合检测功能不支持泛型列表和字典接口(IList<(Of <(T>)>) 和 IDictionary<(Of <(TKey, TValue>)>))。但是,可以将 List<(Of <(T>)>) 类用作基类(因为它直接实现 IList),或者将 Dictionary<(Of <(TKey, TValue>)>) 用作基类(因为它直接实现 IDictionary)。

     XAML 内容语法
    XAML 内容语法仅在将 ContentPropertyAttribute 指定为其类声明一部分的类上启用。ContentPropertyAttribute 需要一个按名称指定属性的参数,而该属性名称被定义为这种类型元素(包括派生的类)的内容属性。为此指定的属性是元素的 XAML 内容属性。在由 XAML 处理器处理时,在元素的开始标记和结束标记之间找到的任何子元素或内部文本将被指定为该 XAML 内容属性的值。元素的属性元素标记不按照这种方式赋值;它们是先进行处理,而且不被视为“内容”。

    正如对于任何其他属性一样,对象的 XAML 内容属性将属于特定类型。该类型可以是 Object 类型。该内容属性的类型可帮助定义对象的内容模型。例如,鉴于任何对象都可以变为内容,Object 的类型是松散的,但是即使这种松散类型也要求内容必须是单个对象。该单个对象可以是集合对象,但是即便如此,也只能将一个这样的集合对象指定为内容。

    特定类型的内容模型在该类型的类页面上进行描述,或者编写成类型系列的单独概念性主题中并与每个相关的类型引用建立链接。

    集合类型的内容语法
    为了接受多个对象元素(或内部文本)作为内容,内容属性的类型必须是明确的集合类型。与集合类型的属性元素语法相似,XAML 处理器必须标识作为集合类型的类型。如果某个元素具有 XAML 内容属性,则该 XAML 内容属性的类型是集合。不必在标记中将隐含集合类型指定为对象元素,也不必将 XAML 内容属性指定为属性元素。因此,标记中明显的内容模型现在可以将多个子元素作为指定为内容。下面是 Panel 子类的内容语法。所有的 Panel 派生类都建立要成为 Children 的 XAML 内容属性,这需要一个类型为 UIElementCollection 的值。

    请注意,标记中既不需要 Children 的属性元素也不需要 UIElementCollection 的元素。这是 XAML 的设计特征,其目的在于,使用直接的父-子元素关系将那些用来定义 UI 的递归包含的元素更直观地表示为嵌套元素树,而不必对属性元素标记或集合对象进行外部干预。实际上,按照设计,UIElementCollection 在标记中不能指定为对象元素。由于 UIElementCollection 唯一的用途就是作为隐式集合,因此它不公开公共的默认构造函数,因此不能实例化为对象元素。

    在具有内容属性的对象中混合使用属性元素和对象元素
    XAML 规范声明 XAML 处理器可以进行如下强制:用来填充某个对象元素中 XAML 内容属性的对象元素必须是连续的,而且不得混合使用。对于混合使用属性元素和内容的这一限制是由 WPF XAML 处理器强制的。

    可以将子对象元素作为某个对象元素中的第一个直接标记,然后可以引入属性元素。也可以指定一个或多个属性元素,接着指定内容,然后指定多个属性元素。但是,一旦内容后面跟有属性元素,您就不能进一步引入任何内容,而只能引入其他属性元素。

    这个内容/属性元素顺序要求不适用于用作内容的内部文本。然而,这仍然是使内部文本保持连续的不错的标记样式,原因是,如果属性元素与内部文本交错分布,则很难直观地检测标记中的大量空白。

     附加属性
    附加属性是 XAML 中引入的一个编程概念,借此,属性可以由类型拥有和定义,但可以在任何元素上设置。附加属性所面向的主要方案就是,允许元素树中的子元素向父元素报告信息,而不要求使用在所有的元素之间广泛共享的对象模型。相反,附加属性可以由任何父元素用来向子元素报告信息。

    附加属性使用的语法在表面上与属性元素语法非常相似,因为您还需要指定类型名.属性名 组合。二者有两个重要的差异:

    即使在通过属性语法设置附加属性时,也可以使用类型名.属性名 组合。只有附加属性 (Property) 才要求属性 (Attribute) 语法中使用限定属性 (Property) 名。

    对于附加属性还可以使用属性元素语法。但是,对于典型的属性元素语法,您指定的类型名 是包含属性元素的对象元素。如果您引用的是附加属性,则类型名 是用来定义附加属性的类,而不是包含对象元素。

     附加事件
    附加事件是 XAML 中引入的另一个编程概念,事件可以由类型定义,但是处理程序可以附加到任何对象上。用来定义附加事件的类型通常是用来定义服务的静态类型,这些附加事件有时由用来公开服务的类型中的路由事件别名公开。附加事件的处理程序是通过属性语法指定的。正如对于附加事件一样,可以扩展附加事件的属性语法,以便允许使用类型名.事件名,其中类型名 是为附加事件基础结构提供 Add 和 Remove 事件处理程序访问器的类,事件名 是事件名称。

     XML 命名空间
    上面的所有语法示例均未指定默认命名空间以外的命名空间。在典型的 WPF 应用程序中,默认命名空间被指定为 WPF 命名空间。您可以指定默认命名空间以外的命名空间,而且仍使用实质上同类的语法,但是,只要命名了无法在默认命名空间中访问的类,该类的名称就必须以用来映射对应 CLR 命名空间的 XML 命名空间的前缀作为开头。例如, 是一种用来实例化 MyElement 类的实例的对象元素语法,其中包含该类的 CLR 命名空间(可能还有包含该命名空间的外部程序集)以前映射到 custom 前缀。

    标记扩展
    XAML 定义了一个标记扩展编程实体,该实体允许从 XAML 处理器对属性或对象元素的常规处理中进行转义,将该处理转给支持类。WPF 对 XAML 处理器的实现将 MarkupExtension 抽象类用作由 WPF 支持的所有标记扩展的基础。在使用属性语法时,用来标识 XAML 处理器的标记扩展的字符是左大括号 ({),其后是右大括号 (}) 以外的任何字符。左大括号后面的第一个字符串必须引用用来提供特定扩展行为的类,如果子字符串“Extension”是实际类名的一部分,则该引用可以省略这个子字符串。该类后面可能会出现一个空格,该空格后面的每个字符都可以由所实现的扩展用作输入,直到遇到右大括号。在使用属性语法时,WPF 中标记扩展的主要用途是提供一种方法来引用其他已经存在的对象,或者将引用转给将在运行时计算的对象。例如,可以指定用 {Binding} 标记扩展来代替给定的属性通常将使用的值类型,从而完成简单的数据绑定。对于无法以其他方式使用属性 (Attribute) 语法的属性 (Property),许多标记扩展都允许使用属性 (Attribute) 语法。例如,Style 对象是一个相对复杂的引用类型,其中包含几个其他属性,每个属性都还采用 byref 对象(而非基元)。但是样式通常作为资源来创建,之后将通过请求资源的两个标记扩展之一来引用。该扩展将对属性 (Property) 值的计算推迟到资源查找时,允许在属性 (Attribute) 语法中提供 Style 属性 (Property) 的值并采用 Style 类型,如下所示:

    My button 

    在这里,StaticResource 用来标识 StaticResourceExtension 类,该类提供标记扩展实现。下一个字符串 MyStyle 用作非默认 StaticResourceExtension 构造函数的输入,在该构造函数中,从扩展字符串提取的参数用来声明所请求的 ResourceKey。MyStyle 应当是定义为资源的 Style 的 x:Key 属性 值。StaticResource 标记扩展用法要求使用该资源,在加载时通过静态资源查找逻辑来提供 Style 属性值。

    可选的和不建议的 XAML 用法
    属性元素的可选用法
    属性元素的可选用法包括,具体地“拼出”由 XAML 处理器视为隐式的元素内容属性。例如,当您声明 Menu 的内容时,可以选择将 Menu 的 Items 集合显式声明为 属性元素标记,并将每个 MenuItem 放在 中,而不是使用隐式的 XAML 处理器行为(即,Menu 的所有子元素都必须是 MenuItem 而且放在 Items 集合中)。有时,这个可选用法可以帮助以可视方式阐明标记中所表示的对象结构。或者,属性元素的隐式用法有时可以避免使用在技术上具有功能但是在视觉上容易引起混淆(如在属性值中嵌套标记扩展)的标记。

    typeName.memberName 全限定属性
    使用属性的类型名.成员名 格式实际上比仅仅使用路由事件的情况更为普遍,但是,在其他应用程序中,如果只是为了实现标记样式和可读性,则该格式是多余的,您应当避免使用它。在下面的示例中,对 Background 属性的三个引用是完全等效的:

    Button.Background 之所以适用,是因为在 Button 上对于该属性的查找是成功的(Background 是从 Control 继承的),而且 Button 是对象元素的类或者是基类。Control.Background 之所以适用,是因为 Control 类实际上定义 Background,而且 Control 是一个 Button 基类。

    但是,下面的类型名.成员名 格式示例并不适用,因此显示为已注释掉:

    Label 是 Control 的另一个派生类,而且,如果在 Label 对象元素中指定了 Label.Background,则该用法将适用。但是,由于 Label 不是 Button 的类或基类,因此指定的 XAML 处理器行为是随后以附加属性形式处理 Label.Background。Label.Background 不是附加属性,因此该用法将失败。

    baseTypeName.memberName 属性元素
    与类型名.成员名 格式如何适用于属性语法相似,基类型名称.成员名 语法适用于属性元素语法。例如,下面的语法适用:

    在这里,即使属性元素包含在 Button 中,属性元素也会以 Control.Background 形式提供。

    但是,正如属性的类型名.成员名 格式一样,基类型名称.成员名 在标记中是很差的样式,您应当避免将其用于设置样式。

    1. 代码隐藏和XAML

    代码隐藏是一个术语,用于描述与将 XAML 页编译为应用程序时由 XAML 处理器创建的代码联接的代码。本主题描述代码隐藏的要求以及在 XAML 中的代码的可选内联代码机制。

    先决条件
    本主题假设您已阅读 XAML 概述并已基本了解 CLR 和面向对象的编程。

    代码隐藏、事件处理程序和分部类要求
    分部类必须派生自用作根元素的类的类型。您可以在代码隐藏的分部类定义中将派生留空,但编译的结果会假定页根作为分部类的基类,即使在没有指定的情况下也是如此(因为分部类的标记部分确实将页根指定为基)。

    编写的事件处理程序必须是 x:Class 标识的命名空间中的分部类所定义的实例方法。您不能限定事件处理程序的名称来指示 XAML 处理器在其他类范围中查找该处理程序,也不能将静态方法用作事件处理程序。

    事件处理程序必须与相应事件的委托匹配。

    专门针对 Microsoft Visual Basic .NET 语言,您可以使用特定于语言的 Handles 关键字将处理程序与处理程序声明中的实例和事件关联,而不是在 XAML 中将处理程序附加到属性。但是,这一技术确实存在一些限制,因为 Handles 不支持 WPF 事件系统的所有特定功能,例如某些路由事件方案或附加事件。

    x:Code
    x:Code 是在 XAML 中定义的一种指令元素。x:Code 指令元素可以包含内联编程代码。内联定义的代码可以与同一页中的 XAML 进行交互。下面的示例阐释了内联 C# 代码。请注意,该代码位于 x:Code 元素内,并且必须包围在 内,以便针对 XML 对内容进行转义,这样 XAML 处理器(解释 XAML 架构或 WPF 架构时)不会试图按原义将内容解释为 XML。

     

    内联代码限制
    应注意对基于 XAML 的应用程序避免或限制使用内联代码。在体系结构和编码原理方面,保留标记和代码隐藏之间的独立性可以更显著地区分设计人员和开发人员这两个角色。从更为技术性的角度看,为内联代码编写的代码更难编写,因为您总是要写入 XAML 页的生成的分部类中,并且只能使用默认的命名空间映射。因为不能添加 using 语句,因此必须完全限定您所进行的大量 API 调用。默认的 WPF 映射包括在 WPF 程序集中出现的大多数但并非全部的 CLR 命名空间;您必须完全限定对其他命名空间中包含的 API 的调用。此外,您还不能在内联代码中定义多个类,并且所有代码实体必须作为生成的分部类中的一个成员或变量存在。其他特定于语言的编程功能(例如宏或对全局变量或生成变量的 #ifdef)也不可用。

    1. XAML和自定义类

    可扩展应用程序标记语言 (XAML) 支持使用任何公共语言运行库 (CLR) 语言定义自定义类或结构,然后使用 XAML 标记(包括在同一标记文件中混合使用 Windows Presentation Foundation (WPF) 定义的 XAML 和自定义类的 XAML 标记)访问该类的功能。本主题讨论自定义类要用作 XAML 元素时所必须满足的要求。

    应用程序或程序集中的自定义类
    可以使用两种不同的方法定义 XAML 中使用的自定义类:在生成主 Windows Presentation Foundation (WPF) 应用程序的代码隐藏或其他代码中定义,或者在单独的程序集(如用作类库的可执行文件或 DLL)中定义为类。这些方法中的每一种都有特定的优点和缺点。

    创建类库的优点是,任何这样的自定义类都可以在许多可能不同的应用程序中共享。单独的类库也使应用程序的版本问题更易控制,而且也简化了在 XAML 页上创建要用作根元素的类这一过程。

    在应用程序中定义自定义类的优点是,此方法是相对轻量的方法,可最大限度减少当引入主可执行文件之外的单独程序集时遇到的部署和测试问题。但是,一个显著的缺点是,不能将同一程序集中定义的类用作 XAML 页的根元素。

    无论是在相同还是不同程序集中定义自定义类,都需要在 CLR 命名空间和 XML 命名空间之间映射这些自定义类才能在 XAML 中使用它们。

    自定义类作为 XAML 元素的要求
    类要能够实例化为对象元素,必须满足以下要求:

    自定义类必须是公共的且支持默认(无参数)公共构造函数。(托管代码结构隐式支持这样的构造函数。)

    自定义类不能是嵌套类(嵌套类和其语法中的“点”会干扰其他 WPF 功能,例如附加属性)。

    除了启用对象元素语法外,还应对将该对象用作其值类型的任何其他公共属性启用属性元素语法。这是因为,对象现在可以实例化为对象元素,而且可以填充此类属性的属性元素值。

    自定义类的属性 (Property) 作为 XAML 属性 (Attribute) 的要求
    属性必须引用按值类型(如基元),或者为在类级别具有默认构造函数或专用类型转换器的类型使用类。

    或者,属性可以引用抽象类类型或接口。对于抽象类或接口,运行时的期望是,属性值必须使用实现该接口的实际类实例或从该抽象类派生的类实例填充。

    属性可以在抽象类上声明,但只能在从抽象类派生的实际类上设置,因为创建类的对象元素完全要求有效的默认构造函数和可实例化的类。

    启用了类型转换器的属性语法
    如果在类级别提供专用的属性化类型转换器,则应用的类型转换将为任何需要实例化该类型的属性启用属性语法。类型转换器不启用该类型的对象元素用法;只有当存在该类型的默认构造函数时才会启用对象元素用法。所以,启用了类型转换器的属性一般而言在属性语法中不可用,除非该类型本身也支持对象元素语法。这一点的一个例外是,可以指定属性元素语法,但允许该属性元素包含一个字符串。该用法实质上相当于属性语法用法,这样的用法不常见,除非需要对属性值进行更可靠的空白处理。例如,以下用法是接受字符串的属性 (property) 元素用法和等效的属性 (attribute) 用法:

     
    允许使用属性语法,但通过 XAML 禁止使用包含对象元素的属性元素语法的属性示例有各种接受 Cursor 类型的属性。Cursor 类有专用类型转换器 CursorConverter,但未公开默认构造函数,因此 Cursor 属性只能通过属性语法进行设置,即使实际 Cursor 类型为引用类型。

    每个属性类型转换器
    此外,属性本身也可以在属性级别声明类型转换器。对于基于适当类型的 ConvertFrom 操作,通过将属性 (attribute) 的传入字符串值作为输入进行处理,这将启用“mini language”,它将实例化内联属性 (property) 类型的对象。通常,这样做是为了提供方便的访问器,而不是作为在 XAML 中设置属性的唯一手段。但是,对于想要使用现有 CLR 类型(不提供默认构造函数或属性化类型转换器)的属性,也可以使用类型转换器。例如,WPF  API 中接受 CultureInfo 类型的某些属性。在此情况下,WPF 使用现有的 Microsoft .NET Framework CultureInfo 类型来更好地处理与早期版本框架的兼容性以及早期版本框架中使用的迁移方案,但 CultureInfo 类型不支持将必要的构造函数或类型级别的类型转换直接用作 XAML 属性值。

    公开具有 XAML 用法的属性时,特别当您是控件作者时,尤其应考虑使用依赖项属性支持该属性。使用 XAML 处理器的现有 Windows Presentation Foundation (WPF) 实现时更是如此,因为使用 DependencyProperty 支持可以提高性能。对于用户期望的 XAML 可访问属性,依赖项属性将公开该属性的属性系统功能。这些功能包括动画、数据绑定和样式支持。

    编写和属性化类型转换器
    您可能经常需要编写自定义 TypeConverter 派生类,以便为属性类型提供类型转换。

    有关自定义类事件的 XAML 事件处理程序属性语法的要求
    若要将事件用作 CLR 事件,必须在支持默认构造函数的类上或可以在派生类中访问事件的抽象类上,将该事件公开为公共事件。为了可方便地用作路由事件,CLR 事件应实现显式 add 和 remove 方法,这两种方法分别添加和移除 CLR 事件签名的处理程序,并将这些处理程序转发到 AddHandler 和 RemoveHandler 方法。这些方法在事件所附加到的实例的路由事件处理程序存储区中添加或删除处理程序。

    说明:
    可以使用 AddHandler 直接注册路由事件的处理程序,而不用特意定义用于公开路由事件的 CLR 事件。通常建议不要这样做,因为该事件不会对附加处理程序启用 XAML 属性语法,并且所生成的类将为类对象模型提供不够透明的 XAML 视图。

    编写集合属性
    接受集合类型的属性具有的 XAML 语法允许您指定要添加到集合的对象。此语法有两种显著功能。

    不需要在对象元素语法中指定属于集合对象的对象。如果在 XAML 中指定接受集合类型的属性,则隐式存在该集合类型。

    该集合属性的子元素将被处理为集合的成员。通常,代码对集合成员的访问通过集合方法(如 Add)或集合索引器属性执行。但 XAML 语法不支持方法或索引器。对于生成元素树的操作,集合明显是很常见的要求,您需要在声明性 XAML 中通过某种方法填充这些集合。因此,处理集合属性的子元素的方法是将这些子元素添加到将作为集合属性类型值的集合中。

    WPF XAML 处理器对集合属性的构成内容使用以下定义。属性的属性类型必须实现以下内容之一:

    实现 IList。

    实现 IDictionary 或等效泛型 (IDictionary<(Of <(TKey, TValue>)>))。

    从 Array 派生
    实现 IAddChild(WPF 定义的接口)。

    这每一种类型都具有 Add 方法,XAML 处理器使用该方法向基础集合中添加项。

    说明:
    由 WPF XAML 处理器执行的集合检测功能不支持泛型列表和字典接口(IList<(Of <(T>)>) 和 IDictionary<(Of <(TKey, TValue>)>))。但是,可以将 List<(Of <(T>)>) 类用作基类(因为它直接实现 IList),或者将 Dictionary<(Of <(TKey, TValue>)>) 用作基类(因为它直接实现 IDictionary)。

    声明接受集合的属性时,务必注意在该类型的新实例中初始化属性值的方式。如果未将属性实现为依赖项属性,则使属性使用可调用集合类型构造函数的支持字段是合适的。如果属性是依赖项属性,则可能需要将集合属性初始化为默认类型构造函数的一部分。这是因为依赖项属性从元数据接受其默认值,您通常不会希望集合属性的初始值是静态的共享集合(每个包含类型实例都应有集合实例)。

    您可以为集合属性实现自定义集合类型。由于集合属性隐式进行处理,因此自定义集合类型不需要提供默认构造函数就可以在 XAML 中隐式使用。但是,也可以选择为集合类型提供默认构造函数。这可能是一种值得的做法,因为除非确实提供了默认构造函数,否则不能显式将集合声明为对象元素。一些标记作者可能喜欢将显式集合视作一种标记样式。另外,在创建将集合类型用作属性值的新对象时,默认构造函数可以简化初始化要求。

     声明 XAML 内容属性
    XAML 语言定义了 XAML 内容属性的概念。对象语法中可用的每个类恰好有一个 XAML 内容属性。若要将属性声明为类的 XAML 内容属性,请将 ContentPropertyAttribute 作为类定义的一部分进行应用。在属性中将要使用的 XAML 内容属性的名称指定为 Name。

    您可以将集合属性指定为 XAML 内容属性。这将导致使用该属性,由此对象元素可以有一个或多个子元素,而没有任何插入集合对象元素或属性元素标记。这些元素然后被作为 XAML 内容属性的值进行处理,并添加到支持集合实例中。

    一些现有的 WPF XAML 内容属性使用 Object 的属性类型。这将使 XAML 内容属性可接受基元值(如 String),并可接受单个引用对象值。如果遵从此模型,则您的类型将负责类型确定和可能类型的处理。使用 Object 类型模型的一般原因有两种,一种是支持将对象内容添加为字符串的简单方式(接受默认呈现处理),另一种是支持添加对象内容(指定非默认呈现)的高级方式。

     序列化 XAML
    对于某些情况,例如如果您是控件作者,则可能还需要确保 XAML 中可实例化的任何对象表示形式也可以反序列化到等效 XAML。本主题中不介绍序列化要求。

    1. 标记扩展和XAML

    本主题介绍可扩展应用程序标记语言 (XAML) 的标记扩展概念,包括其语法规则、用途以及底层的类对象模型。

     XAML 处理器和标记扩展
    XAML 处理器是指可根据其规范(通过编译或解释)将 XAML 接受为语言、并且可以生成结果基础类以供运行时对象模型使用(也是根据 XAML 规范)的任意程序。默认情况下,此类处理器要么将属性值解释为一个文本字符串,要么基于属性类型或该属性特定的类型转换器将该属性值转换为对象。不过,有时也存在要求其他行为的情况。例如,可能指示 XAML 处理器:某个属性的值应该是对已构造对象或静态对象的引用。或者指示 XAML 处理器使用向对象的构造函数提供非默认参数的语法。相对于指定的 XAML 处理器默认行为,这是一种反常行为。

     基本标记扩展语法
    可以实现标记扩展以便为属性 (Attribute) 用法中的属性 (Property) 和/或属性 (Property) 元素用法中的属性 (Property) 提供值。

    当用于提供属性 (Attribute) 值时,将标记扩展与 XAML 处理器区分开来的语法就是左右大括号({ 和 })。然后,由紧跟在左大括号后面的字符串标记来标识标记扩展的类型。

    当用在属性元素语法中时,标记扩展在外观上与其他任何用于提供属性元素值的元素相同,即:一个将标记扩展类作为一个元素引用并以尖括号 (<>) 括起的 XAML 元素声明。

     特定于 WPF 的标记扩展
    WPF 编程中最常用的标记扩展是支持资源引用的标记扩展(StaticResource 和 DynamicResource)以及支持数据绑定的标记扩展 (Binding)。

    StaticResource 通过替换已定义资源的值来为 XAML 属性提供值。

    DynamicResource 通过将值推迟为对资源的运行时引用来为 XAML 属性提供值。动态资源引用强制在每次访问此类资源时都重新进行查找。

    Binding 按应用于元素的数据上下文来为属性提供数据绑定值。此标记扩展相对复杂,因为它会启用大量内联语法来指定数据绑定。

    RelativeSource 为可以在运行时元素树中定位若干可能关系的 Binding 提供源信息。对于在多用途模板中创建的绑定,或在未充分了解周围的元素树的情况下以代码创建的绑定,上述标记扩展会提供专用源。

    通过 TemplateBinding,控件模板可以使用来自要利用该模板的类的对象模型定义属性中的模板化属性的值。

    XAML 定义的标记扩展
    有几个标记扩展并非是 XAML 的 WPF 应用程序所特有的,而是属于 XAML 语言的规范和命名空间的一部分。它们通常由语法中的 x: 前缀标识,如您在常见用法中所见到的一样。这些标记扩展的 WPF 实现使用相同的 MarkupExtension 基类来提供实现。

    说明:
    x: 前缀在 XAML 应用程序或文档的根元素中用于 XAML 命名空间的典型命名空间映射。例如,Microsoft Visual Studio 2005 模板使用此 x: 映射启动 XAML 文件。您可以在自己的 xmlns 映射中选择一个不同的前缀标记,但是本文档采用默认的 x: 映射,作为标识那些属于 XAML 命名空间已定义部分的实体的一种方式,这与 WPF 命名空间或其他任意命名空间是相反的。

    x:Type 为命名类型提供 Type 对象。此标记扩展最常用于样式和模板。

    x:Static 从不直接属于属性值类型、但可以计算为该类型的值类型代码实体中生成静态值。

    x:Null 将 null 指定为 XAML 属性的值。

    在特意不使用基元素和控件模型提供的集合支持的情况下,x:Array 为 XAML 语法中常规数组的创建提供支持。

    有关标记扩展语法的更多信息

    *Extension 类

    每个标记扩展的行为都会通过从 MarkupExtension 派生的 *Extension 类通知给 XAML 处理器,并提供 ProvideValue 方法的实现。每个扩展的此方法都会定义在计算标记扩展后将返回哪个对象。通常,通过使用传递给标记扩展的各个字符串标记来对返回的对象进行实例化或设置。

    例如,StaticResourceExtension 类提供实际资源查找的图面实现,以便其 ProvideValue 实现返回请求的对象,该特定实现的输入是用于按其 x:Key 查找资源的字符串。如果您使用的是现有标记扩展,则其中的大部分实现详细信息都无关紧要。

    后续字符串标记的扩展类解释
    跟在标记扩展标识符后面、并且仍在括号内的字符串标记由 XAML 处理器通过以下方式之一解释:

    逗号始终代表各个标记的分隔符。因此,文本逗号无法传递给标记扩展。

    如果各个分隔的标记不包含任何等号,则每个标记都将被视为构造函数参数。每个构造函数参数都必须按该签名所期望的类型给出,并按照该签名所期望的顺序排列。

    说明:
    XAML 处理器必须调用与对的数量这一参数计数匹配的构造函数。为此,如果您要实现自定义标记扩展,请不要提供具有相同实参计数的多个形参;对于当存在多个具有相同形参计数的标记扩展构造函数时将发生的情况,尚未定义相应的行为。

    如果各个分隔的标记包含等号,则 XAML 处理器会首先为标记扩展调用默认构造函数。之后,每个“名称=值”对都会被解释为标记扩展上存在的属性名称以及赋给该属性的值。

    如果在标记扩展中的构造函数行为与属性设置行为之间存在并行结果,则您使用哪个行为都无关紧要。如果仅仅因为将“属性=值”对用于具有多个可设置属性的标记扩展就可以令您的标记意图性更强,并减小意外转置构造函数参数的可能性(当您指定“属性=值”对时,这些属性可以为任意顺序),那么这种用法更常见。另外,无法保证标记扩展提供设置每个可设置属性的构造函数参数。例如,Binding 是一个标记扩展,具有多个可以通过“属性=值”形式的扩展来设置的属性,但 Binding 仅支持两个构造函数,即默认构造函数和一个设置初始路径的构造函数。

    转义文本大括号
    XAML 处理器中的属性处理使用大括号作为标记扩展的指示符。必要时,还可以使用后面跟文本大括号的空大括号对来输入转义序列,从而生成文本大括号字符属性值。

    嵌套扩展语法
    支持多个标记扩展的嵌套,并且将首先计算每个标记扩展的最里层,例如:

    < P Property="Background">

    Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />

    有关标记扩展和属性元素语法的更多信息

    当用作填写属性元素值的对象元素时,标记扩展类在外观上与可用在 XAML 中的普通元素没有区别。这种情况下,普通元素与标记扩展之间的实际差异就在于标记扩展要么可计算为一个类型值,要么延迟为一个表达式,因此属性值的任何可能的类型错误的机制都将是不同的,这与后期绑定属性在其他编程模型中的处理方式类似。普通元素将计算为类型,而属性是在编译时直接设置的。

    当用在对象元素语法中以填充属性元素时,大部分标记扩展都不会包含内容或任何进一步的属性元素语法,这样您就可以关闭对象元素标记,而不提供任何子元素。不论何时 XAML 处理器遇到任何对象元素,都会调用该类的构造函数以实例化从已分析元素创建的对象。标记扩展类也一样;因此,如果您希望自己的标记扩展在对象元素语法中可用,就必须提供默认构造函数。有些现有标记扩展至少具有一个必需的属性值,为了使初始化有效,必须指定该值。在这种情况下,通常会将该属性值作为对象元素的属性特性来指定。在 XAML 命名空间 (x:) 语言功能和 WPF 命名空间 XAML 扩展参考页中,将指出具有必需属性的标记扩展(以及必需属性的名称)。参考页还将指出特定标记扩展是否禁止使用对象元素语法或属性语法。一个值得注意的情况是 x:Array 标记扩展,它无法支持属性语法,因为必须指定该数组的内容。数组内容的处理方式与常规对象一样,因此属性没有默认类型转换器是可行的。另外,x:Array 标记扩展需要 Type 参数。

    1. XAML命名空间和命名空间映射

    本主题进一步解释每个可扩展应用程序标记语言 (XAML) 文件的根标记中存在的两个命名空间映射及其用途,同时还介绍如何生成类似的映射,以便使用在您自己的代码中和/或单独的程序集中定义的元素。

    WPF 和 XAML 命名空间声明
    在许多可扩展应用程序标记语言 (XAML) 文件的根标记中的命名空间声明内,您都可以看到两个 xmlns 声明。第一个声明将整个 Windows Presentation Foundation (WPF) 命名空间映射为默认命名空间:

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    第二个声明映射单独的可扩展应用程序标记语言 (XAML) 命名空间,通常将其映射为 x: 前缀。

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    这些声明之间的关系是:XAML 实际上是语言标准,而 WPF 是将 XAML 作为语言使用的一个实现。XAML 语言指定一些为了兼容而假定要实现的语言元素,每个元素都应当能通过针对 XAML 命名空间执行的 XAML 处理器实现进行访问。WPF 实现为其自己的 API 保留默认命名空间,为 XAML 中需要的标记语法使用单独的映射前缀。按照约定,该前缀是 x:,此 x: 约定后面是项目模板、示例代码和此 SDK 中语言功能的文档。XAML 命名空间定义了许多常用功能,这些功能即使对于基本的 WPF 应用程序也是必需的。例如,若要通过分部类将任何代码隐藏加入 XAML 文件,您必须将该类命名为相关 XAML 文件的根元素中的 x:Class 属性。或者,在 XAML 页中定义的、您希望作为键控资源访问的任何元素应当对相关元素设置了 x:Key 属性。

    映射到自定义类和程序集
    您可以在 xmlns 前缀声明中使用一系列标记将命名空间映射到程序集,这与将标准 WPF 和 XAML 命名空间映射到前缀类似。

    语法使用下列可能的命名标记和值:

    clr-namespace: 在包含要作为元素公开的公共类型的程序集中声明的公共语言运行库 (CLR) 命名空间。

    assembly= 是指包含部分或全部引用的 CLR 命名空间的程序集。该值通常只是程序集的名称,而不是路径。该程序集的路径必须在生成编译的 XAML 的项目文件中以项目引用的形式建立。另外,为了合并版本管理和强名称签名,该值也可以是 AssemblyName 定义的字符串。

    请注意,分隔 clr-namespace 标记和其值的字符是冒号 (:),而分隔 assembly 标记和其值的字符是等号 (=)。这两个标记之间使用的字符是分号。例如:

    xmlns:custom="clr-namespace:SDKSample;assembly=SDKSampleLibrary"

    映射到当前程序集
    如果引用的 clr-namespace 是在引用自定义类的应用程序代码所在的程序集中定义的,则可以省略 assembly。这种情况的等效语法是指定 assembly=,等号后不需要任何字符串标记。

    如果自定义类是在同一程序集中定义的,则不能将其用作页的根元素。不需要映射分部类;如果您希望在 XAML 中将自定义类作为元素引用,只需要映射应用程序中页的非分部类。

     在程序集中将 CLR 命名空间映射为 XML 命名空间
    WPF 定义一个 CLR 属性,XAML 处理器使用它来将多个 CLR 命名空间映射到单个 XML 命名空间。XmlnsDefinitionAttribute 属性放置在生成程序集的源代码中的程序集级别。WPF 程序集源代码使用此属性将各种常见的命名空间(如 System.Windows 和 System.Windows.Controls)映射到 http://schemas.microsoft.com/winfx/2006/xaml/presentation 命名空间。

    XmlnsDefinitionAttribute 采用两个参数:XML 命名空间名称和 CLR 命名空间名称。可以存在多个 XmlnsDefinitionAttribute,这样便可以将多个 CLR 命名空间映射到同一 XML 命名空间。映射后,如果需要,还可以通过在分部类代码隐藏页中提供相应的 using 语句来引用这些命名空间的成员,而无需完全限定。

    1. WPF名称范围

    名称范围既是一种概念,也是用于存储对象的 XAML 定义名称及其实例等效项之间的关系的编程对象。加载 XAML 应用程序的页面时,即在 WPF 托管代码中创建了名称范围。作为编程对象的名称范围由 INameScope 接口定义,并且还由实际类 NameScope 实现。

     加载的 XAML 应用程序中的名称范围
    处理 XAML 页时,即对该页的根元素创建了名称范围。该页中指定的每个名称都会添加到相关的名称范围中。作为常见根元素(例如 Page 和 Window)的元素总是控制名称范围。如果在标记中某个元素(例如 FrameworkElement 或 FrameworkContentElement)是页的根元素,则 XAML 处理器会隐式添加一个 Page 根元素,以便 Page 可以提供一个名称范围。即使最初在 XAML 中没有定义 Name 或 x:Name 属性,也会创建一个名称范围。

    如果试图在任意名称范围中两次使用同一个名称,则会引发异常。对于具有代码隐藏并且是已编译的应用程序的一部分的 XAML,在创建页的已生成类时会引发该异常。

    将元素添加到已分析的元素树
    若要在初始的加载和处理之后向元素树添加任何元素,都必须对定义名称范围的类调用相应的 RegisterName 的实现。否则,无法通过 FindName 等方法按名称引用添加的对象。仅设置 Name 属性(或 x:Name 属性)不会将该名称注册到任何名称范围中。将命名的元素添加到具有名称范围的元素树中也不会将此名称注册到名称范围中。尽管名称范围可以嵌套,但通常您应该将名称注册到根元素上存在的名称范围中,这样您的名称范围位置便可与在等效的加载 XAML 页中可能已创建的名称范围并列。 应用程序开发人员最常用的方案是使用 RegisterName 将名称注册到当前根元素的名称范围中。RegisterName 是查找将作为动画运行的演示图板的一种重要方案的一部分。有关更多信息,请参见演示图板概述。如果您对同一逻辑树中的非根元素的元素调用 RegisterName,则该名称仍然会注册到最靠近根元素的元素,就好像对该根元素调用了 RegisterName 一样。

    代码中的名称范围
    对于以编程方式创建(而不是来自加载的 XAML)的应用程序,若要支持名称范围,根元素必须实现 INameScope、或者必须是 FrameworkElement 或 FrameworkContentElement 派生类。

    此外,对于不是由 XAML 处理器加载和处理的任何元素,默认情况下不会创建或初始化该对象的名称范围。必须为随后要向其中注册名称的任何元素显式创建新的名称范围。若要为元素创建名称范围,可调用静态 SetNameScope 方法。将该元素指定为 dependencyObject 参数,并且将新的 NameScope 构造函数调用指定为 value 参数。

    如果作为 SetNameScope 的 dependencyObject 提供的对象不是 INameScope 实现,也不是 FrameworkElement 或 FrameworkContentElement,那么,对任何子元素调用 RegisterName 均无效。如果您无法显式创建新的名称范围,则调用 RegisterName 将引发异常。

     样式和模板中的名称范围
    WPF 中的样式和模板提供了以简单的方法重新使用和重新应用内容的功能,但样式和模板可能还包括具有在模板级别指定的名称的元素。同一个模板可能在某个页中被多次重复使用。因此,样式和模板都定义其自己的名称范围,而与样式或模板所应用到的包含页无关。

    请看下面的示例:

    此处,同一个模板被应用到两个不同的按钮。如果模板没有独立的名称范围,则该模板中用到的 TheBorder 名称会导致名称冲突。模板的每个实例化都有其自已的名称范围,因此在本示例中每个实例化模板的名称范围都只包含一个名称。

    样式也具有其自己的名称范围,因此大多数情况下演示图板的某些部分会分配有特殊的名称。即使模板重新定义为控件自定义项的一部分,这些名称也会启用以具有此名称的元素为目标的控件特定行为。

    由于名称范围是独立的,因此在模板中查找命名的元素比在页中查找非模板化的元素更具有挑战性。首先需要确定所应用的模板,方法是获取该模板所应用到的控件的 Template 属性值。然后,调用 FindName 的模板版本,将模板所应用到的控件作为第二个参数进行传递。

    如果您是控件作者,您要生成一个约定,即所应用的模板中的特定命名元素是控件本身所定义的行为的目标,则可以在控件实现代码中使用 GetTemplateChild 方法。由于 GetTemplateChild 方法是受保护的,因此只有控件作者才可以访问它。

    如果您正在使用模板,并且需要用到该模板所应用到的名称范围,则获取 TemplatedParent,然后调用其中的 FindName。举一个使用模板的例子:您正在编写事件处理程序实现,其中该事件将从所应用的模板中的元素被引发。

     名称范围和名称相关的 API
    FrameworkElement 具有 FindName、RegisterName 和 UnregisterName 方法。如果您对其调用这些方法的元素拥有名称范围,则这些元素方法仅调入名称范围方法。否则,会查看其父元素是否具有名称范围,此进程会以递归方式继续执行,直到找到一个名称范围为止(由于 XAML 处理器行为的原因,可以保证在根元素有名称范围)。FrameworkContentElement 具有类似的行为,所不同的是 FrameworkContentElement 从不会有名称范围。在 FrameworkContentElement 上存在上述方法,因此这些调用最终可以转发到 FrameworkElement 父元素。

    SetNameScope 用于将新的名称范围映射到现有的对象。您可以多次调用 SetNameScope 以重置或清除名称范围,但这种用法并不常见。此外,通常不通过代码使用 GetNameScope。

    名称范围实现
    下面的类直接实现 INameScope:

    NameScope

    Style

    ResourceDictionary

    FrameworkTemplate

    ResourceDictionary 不使用名称范围,而是使用键,原因是它是一种字典哈希表实现。ResourceDictionary 之所以实现 INameScope,其唯一的原因是它可以对用户代码引发异常,从而有助于澄清真正的名称范围和 ResourceDictionary 处理键的方式之间的区别,此外,还可以保证名称范围不会被父元素特别应用到 ResourceDictionary。

    FrameworkTemplate 和 Style 通过显式接口定义实现 INameScope。这些显式实现允许这些名称范围在通过 INameScope 接口进行访问时表现为常规行为,这是名称范围与 WPF 内部进程进行通信的方式。但是,这些显式接口定义不是 FrameworkTemplate 和 Style 的常规名称范围的一部分,原因是几乎不需要对 FrameworkTemplate 和 Style 直接调用 INameScope 方法。

    下面的类都定义自己的名称范围,方法是使用 System.Windows..::.NameScope 帮助器类并通过 NameScope 附加属性连接到相应的名称范围实现:

    FrameworkElement

    FrameworkContentElement

    WPF控件开发

    1. WPF控件开发之控件概述

    Windows Presentation Foundation (WPF) 附带了许多几乎可以在所有 Windows 应用程序中使用的常见 UI 组件,其中包括 Button、Label、TextBox、Menu 和 ListBox。以前,这些对象被称为控件。不过,WPF SDK 仍继续使用术语“控件”,泛指任何代表应用程序中可见对象的类。请注意,类不必从 Control 类继承,即可具有可见外观。从 Control 类继承的类包含一个 ControlTemplate,允许控件的使用方在无需创建新子类的情况下根本改变控件的外观。 本主题讨论在 WPF 中使用控件(包括从 Control 类继承的控件以及不从该类继承的控件)的常见方式。

     创建控件的实例
    可以通过使用可扩展应用程序标记语言 (XAML) 或以代码形式向应用程序添加控件。 下面的示例演示如何创建一个向用户询问其姓名的简单应用程序。 此示例在 XAML 中创建六个控件:两个标签、两个文本框及两个按钮。所有控件都可以按相似的方式创建。

    下面的示例以代码的形式创建同一应用程序。为了简单起见,示例中不包括 Grid grid1 的创建。grid1 与前面的 XAML 示例内容具有相同的列和行定义。

     

    更改控件外观
    更改控件的外观以适应应用程序的外观,这是很常见的操作。可以根据您要达到的效果,通过执行以下操作之一来更改控件的外观:

    更改控件的属性值。

    为控件创建 Style。

    为控件创建新 ControlTemplate。

    更改控件的属性值
    许多控件具有允许您更改控件外观的属性,例如 Button 的 Background。可以在 XAML 和代码中设置值属性。下面的示例在 XAML 中设置 Button 的 Background、FontSize 和 FontWeight 属性。

    下面的示例在代码中设置相同的属性。

      

    为控件创建样式
    利用 WPF,通过创建 Style,您可以同时为许多控件指定相同的外观,而不是在应用程序中设置每个实例的属性。 下面的示例创建一个 Style,它可以应用于应用程序中的每个 Button。Style 定义通常是在 ResourceDictionary(例如 FrameworkElement 的 Resources)中以 XAML 形式定义的。

    通过将键分配给样式并在控件的 Style 属性中指定该键,还可将样式仅应用于某些特定类型的控件。

    创建 ControlTemplate
    利用 Style,可以一次为多个控件设置属性,但有时除了通过创建 Style 可执行的操作之外,您可能想要自定义 Control 的外观。从 Control 类继承的类具有 ControlTemplate,它用于定义 Control 的结构和外观。Control 的 Template 属性是公共的,因此您可以为 Control 指定非默认 ControlTemplate。通常,您可以为 Control 指定新的 ControlTemplate(而不是从控件继承)以自定义 Control 的外观。

    请考虑一个很常用的控件 Button。 Button 的主要行为是当用户单击它时让应用程序实现某些操作。 默认情况下,WPF 中的 Button 显示为一个凸出的矩形。 开发应用程序时,您可能会希望利用 Button 的行为(即通过处理按钮的单击事件),不过,除了通过更改按钮的属性可以执行的操作外,您也可以更改按钮的外观。 在这种情况下,您可以创建新 ControlTemplate。

    下面的示例创建 Button 的 ControlTemplate。 ControlTemplate 创建一个带圆角和渐变背景的 Button。 ControlTemplate 包含一个 Border,其 Background 为 LinearGradientBrush,它具有两个 GradientStop 对象。 第一个 GradientStop 使用数据绑定将 GradientStop 的 Color 属性绑定到按钮背景的颜色。 当设置 Button 的 Background 属性时,该值的颜色将用作第一个 GradientStop。此示例还创建一个 Trigger,用于在 IsPressed 为 true 时更改 Button 的外观。

      

    说明:
    为使此示例正常工作,Button 的 Background 属性必须设置为 SolidColorBrush。

     订阅事件
    您可以通过使用 XAML 或代码来订阅控件的事件,但只能在代码中处理事件。 下面的示例演示如何订阅 Button 的 Click 事件。

    下面的示例处理 Button 的 Click 事件。

    控件中的丰富内容
    从 Control 类继承的大多数类具有包含丰富内容的能力。例如,Label 可以包含任意对象,例如字符串、Image 或 Panel。 下列类支持丰富内容,可以用作 WPF 中大多数控件的基类。

    ContentControl - 从此类继承的类的部分示例包括有 Label、Button 和 ToolTip。

    ItemsControl - 从此类继承的类的部分示例包括有 ListBox、Menu 和 StatusBar。

    HeaderedContentControl - 从此类继承的类的部分示例包括有 TabItem、GroupBox 和 Expander。

    HeaderedItemsControl - 从此类继承的类的部分示例包括有 MenuItem、TreeViewItem 和 ToolBar。

    1. 使用XAML创建按钮

    本演练的目的在于介绍如何创建要在 Windows Presentation Foundation (WPF) 应用程序中使用的动画按钮。本演练使用样式和模板来创建自定义的按钮资源,通过该按钮资源可以重用代码并将按钮逻辑与按钮声明分开。本演练完全是用可扩展应用程序标记语言 (XAML) 编写的。
    重要说明:
    本演练指导您通过在 Microsoft Visual Studio 中键入或复制和粘贴可扩展应用程序标记语言 (XAML) 来逐步创建应用程序。

    下图显示的是已完成的按钮。

     创建基本按钮
    让我们首先从创建一个新项目并向窗口中添加几个按钮开始。

    创建新的 WPF 项目并向窗口中添加按钮
    启动 Visual Studio。

    创建新的 WPF 项目:在“文件”菜单上,指向“新建”,再单击“项目”。此时将打开下图所示的窗口。在左窗格中的“Visual C#”节点下面,选择“.NET Framework 3.0”。在右窗格中,选择“Windows 应用程序 (WPF)”模板。将该项目命名为“AnimatedButton”。这将创建应用程序的主干。

    添加基本的默认按钮:本演练中所需的全部文件均由该模板提供。通过在解决方案资源管理器中双击 Window1.xaml 文件来打开它。默认情况下,Window1.xaml 中存在一个 Grid 元素。移除 Grid 元素,并通过向 Window1.xaml 中键入或复制和粘贴以下突出显示的代码来向可扩展应用程序标记语言 (XAML) 页面中添加几个按钮:

    按 F5 运行该应用程序;您应当能够看到类似下图的一组按钮。

    现在您已经创建了基本按钮,这样就完成了在 Window1.xaml 文件中的工作。本演练其余部分重点介绍如何在 app.xaml 文件中为这些按钮定义样式和模板。

    设置基本属性
    接着,我们将会为这些按钮设置一些属性,以便控制按钮外观和布局。您将使用资源来为整个应用程序定义按钮属性,而不是为这些按钮单独设置属性。在概念上,应用程序资源与网页的外部级联样式表 (CSS) 相似;但是,资源远比级联样式表 (CSS) 强大,在本演练结束时您将明白这一点。

    使用样式为按钮设置基本属性
    定义 Application.Resources 块:打开 app.xaml 并添加下面突出显示的标记(如果尚未添加):    

    资源范围由资源的定义位置来确定。如果资源是在 app.xaml 文件的 Application.Resoureses 中定义的,则将允许从应用程序中的任何位置使用资源。

    创建一个样式并用该样式定义基本属性值:向 Application.Resources 块添加下面的标记。此标记创建一个应用于该应用程序中所有按钮的 Style,并将这些按钮的 Width 设置为 90,将 Margin 设置为 10:

    TargetType 属性指定将该样式应用于 Button 类型的所有对象。每个 Setter 都为 Style 设置不同的属性值。因此,此时该应用程序中的每个按钮的宽度都为 90,边距都为 10。如果您按 F5 运行该应用程序,则将看到下面的窗口。

    您还可以对样式进行更多的处理,这包括以各种方式微调对象的目标、指定复杂的属性值,甚至将样式作为其他样式的输入。

    为资源设置样式属性值:使用资源,可以通过一种简单的方式来重用通常定义的对象和值。为了使代码更加模块化,使用资源定义复杂值尤其有用。向 app.xaml 添加下面突出显示的标记。

    您已经在 Application.Resources 块的正下方创建了一个名为“GrayBlueGradientBrush”的资源。此资源将定义一个水平渐变。此资源可以在该应用程序中的任何位置(包括在 Background 属性的按钮样式 setter 内部中)用作属性值。现在,所有的按钮都具有此渐变的 Background 属性值。

    按 F5 运行该应用程序。其外观类似于下图:

    创建一个定义按钮外观的模板
    在本节中,将创建一个用来自定义按钮外观(表示)的模板。按钮表示是由几个赋予按钮独特外观的对象(包括矩形和其他组件)组成的。

    到目前为止,对应用程序中按钮外观的控制已限制为更改按钮的属性。如果您希望更彻底地改变按钮的外观,该怎么办?使用模板可以强有力地控制对象的表示。由于模板可以在样式中使用,因此您可以将模板应用于所有应用了样式的对象(在本演练中为按钮)。

    使用模板定义按钮的外观
    设置模板:由于控件(如 Button)具有 Template 属性,因此您可以像对 Style 中所设置的其他属性值那样,使用 Setter 来定义模板属性值。向按钮样式中添加下面突出显示的标记。

      

    更改按钮表示:此时,您需要定义模板。添加下面突出显示的标记。此标记指定了两个后跟 DockPanel 且具有圆角的 Rectangle 元素。DockPanel 用于承载按钮的 ContentPresenter。A ContentPresenter 显示按钮的内容。本演练中,内容为文本 ("Button 1", "Button 2", "Button 3")。所有模板组件(矩形和 DockPanel)都放置在一个 Grid 内。

    按 F5 运行该应用程序。其外观类似于下图:

    向模板中添加玻璃效果:接着将添加玻璃效果。首先创建一些用来生成玻璃渐变效果的资源。将这些渐变资源添加到 Application.Resources 块中的任何位置:

    这些资源将用作按钮模板的 Grid 中所插入的矩形的 Fill。向模板添加下面突出显示的标记。

    请注意,x:Name 属性为“glassCube”的矩形的 Opacity 为 0,因此,当您运行该示例时,将看到上面并没有覆盖玻璃矩形。这是由于我们将在以后向该模板中添加用户与该按钮交互时将触发的触发器。但是,您可以通过将 Opacity 值更改为 1 并运行该应用程序来查看按钮现在的外观。请参见下图。在转至下一步之前,请将 Opacity 更改回 0。

    创建按钮交互性
    在本节中,将创建属性触发器和事件触发器来更改属性值和运行动画,以响应用户操作(如将鼠标指针移到按钮上并单击)。

    添加交互性(鼠标悬停、鼠标离开和单击等)的一个简便方法就是在模板或样式内部定义触发器。若要创建 Trigger,需要定义一个属性“条件”,例如:按钮的 IsMouseOver 属性值等于 true。随后将定义在触发条件为 true 时所发生的 setter(操作)。

    创建按钮交互性
    添加模板触发器:向模板添加突出显示的标记。

    添加属性触发器:向 ControlTemplate.Triggers 块添加突出显示的标记:

    按 F5 运行应用程序,并查看在将鼠标指针移到按钮上时的效果。

    添加焦点触发器:接着,将添加一些类似的 setter 来处理当按钮具有焦点时(例如,当用户单击按钮之后)的情况。

    按 F5 运行应用程序,并单击其中的某个按钮。请注意,在单击该按钮之后,该按钮仍具有焦点,因此它仍将保持突出显示状态。如果您单击另一个按钮,则新按钮将获得焦点,而上一个按钮则失去焦点。

    为 MouseEnter 和 MouseLeave 添加动画:接着将向触发器添加一些动画。在 ControlTemplate.Triggers 块内部的任意位置添加下面的标记。

    当鼠标指针移到该按钮上时,玻璃矩形会收缩;当指针离开该按钮时,镜面矩形会返回到其正常大小。

    当指针移到该按钮上(引发 MouseEnter 事件)时会触发两个动画。这些动画会使玻璃矩形沿着 X 轴和 Y 轴收缩。请注意 DoubleAnimation 元素的属性:Duration 和 By。Duration 指定动画持续半秒多,By 指定玻璃收缩 10%。

    第二个事件触发器 (MouseLeave) 只是停止第一个触发器。当您停止某个 Storyboard 时,所有的动画属性都恢复到其默认值。因此,当用户将指针移开按钮时,该按钮将返回到鼠标指针移到其上之前的状态。

    添加单击按钮时的动画效果:最后一步是添加在用户单击按钮时将触发的触发器。在 ControlTemplate.Triggers 块内部的任意位置添加下面的标记:

    按 F5 运行应用程序,并单击其中的某个按钮。当您单击某个按钮时,玻璃矩形会旋转。

    摘要

    在本演练中,进行了下列练习:

    使 Style 面向对象类型 (Button)。

    使用 Style 控制整个应用程序中按钮的基本属性。

    创建要用于 Style setter 的属性值的资源(如渐变)。

    通过向按钮应用模板来自定义整个应用程序中按钮的外观。

    自定义按钮的行为(包括动画效果)来响应用户操作(如 MouseEnter、MouseLeave 和 Click)。

    1. WPF控件库之Button

    Button 控件响应来自鼠标、键盘、手写笔或其他输入设备的用户输入,并引发一个 Click 事件。Button 是一个基本的用户界面 (UI) 组件,可以包含简单内容(例如文本),也可以包含复杂内容,例如图像和 Panel 控件。

    处于默认状态、具有焦点和处于按下状态的按钮

    示例
    下面的示例创建两个 Button 控件。一个 Button 包含文本,另一个包含图像。当用户单击包含图像的 Button 时,另一个 Button 的背景和文本将发生变化。

    此示例使用标记创建 Button 控件,但使用代码编写 Click 事件处理程序。

    1. WPF控件库之Menu

    Menu 是一个控件,使用该控件可以对那些与命令或事件处理程序相关联的元素以分层方式进行组织。每个 Menu 可以包含多个 MenuItem 控件。每个 MenuItem 都可调用命令或调用 Click 事件处理程序。MenuItem 也可以有多个 MenuItem 元素作为子项,从而构成子菜单。

    下图演示了菜单控件的三种不同状态。默认状态是没有设备(如鼠标指针)停留在 Menu 上时的状态。当鼠标指针悬停在 Menu 上时显示焦点状态,当在 Menu 上单击鼠标按钮时显示按下状态。

    不同状态下的菜单

    使用 Menu 类,可以按照分层顺序对与命令和事件处理程序相关联的元素进行组织。每个 Menu 元素都包含一个由 MenuItem 元素组成的集合。

    Menu 控件
    Menu 控件表示一系列用来为应用程序指定命令或选项的项。通常,单击 MenuItem 将打开一个子菜单或者导致应用程序执行相应的命令。

    创建 Menu
    下面的示例将创建一个 Menu 来操作 TextBox 中的文本。Menu 包含多个使用 Command、IsCheckable 和 Header 属性以及 Checked、Unchecked 和 Click 事件的 MenuItem 对象。

    具有键盘快捷键的 MenuItem
    键盘快捷键是可以用键盘输入以调用 Menu 命令的字符组合。例如,“复制”的快捷键是 Ctrl+C。可以将以下两个属性与键盘快捷键和菜单项结合使用:InputGestureText 和 Command。

    InputGestureText
    下面的示例演示如何使用 InputGestureText 属性来将键盘快捷键文本分配给 MenuItem 控件。这只是将键盘快捷键放在菜单项中, 而不会将命令与 MenuItem 相关联。应用程序必须处理用户的输入才能执行该操作。

     

    命令
    下面的示例演示如何使用 Command 属性来将“打开”和“保存”命令与 MenuItem 控件相关联。该 Command 属性不但将命令与 MenuItem 相关联,而且还提供要用作快捷方式的输入笔势文本。

    MenuItem 类还有一个 CommandTarget 属性,该属性指定要在其中执行命令的元素。如果未设置 CommandTarget,则具有键盘焦点的元素将收到命令。

    Menu 样式
    使用控件样式设置,可以显著改变 Menu 控件的外观和行为,而不必编写自定义控件。除了设置可视化属性外,还可以向控件的各个部分应用 Style,通过属性更改控件各个部分的行为,向控件中添加额外的部分,或者改变控件的布局。下面的示例演示了几种用来将 Style 添加到 Menu 控件中的方法。

    第一个代码示例定义了一个名为 Simple 的 Style 来说明如何在您的样式中使用当前的系统设置。该代码将 MenuHighlightBrush 的颜色分配为菜单的背景色,将 MenuTextBrush 分配为菜单的前景色。请注意,您在分配画笔时使用的是资源键。

    下面的示例使用的是 Trigger 元素,通过这些元素,可以改变 MenuItem 的外观来响应发生在 Menu 上的事件。当您将鼠标移到 Menu 上时,菜单项的前景色和字体特征会发生变化。

    1. WPF控件库之Lable

    Label 控件通常在用户界面 (UI) 中提供信息。一直以来,Label 只包含文本,但由于 Windows Presentation Foundation (WPF) 附带的 Label 是一个 ContentControl,所以它可以包含文本或 UIElement。

    Label 为快捷键提供功能性和可视化支持。它常用于实现对控件(如 TextBox)的快速键盘访问。若要为 Control 指定 Label,请将 Label..::.Target 属性设置为当用户按下快捷键时应获得焦点的控件。

    下图演示了一个目标为 ComboBox 的 Label“主题”。当用户按下 Alt+T 时,ComboBox 将获得焦点。

    示例

    下面的示例演示如何创建一个 Label,该控件使用 AccessText 并且绑定到目标 TextBox。

     

     

    示例
    为标签添加文本换行

    Label 控件不支持文本换行。如果您需要一个多次换行的标签,可以嵌套一个支持文本换行的元素,并将该元素放在标签内。下面的示例演示如何使用 TextBlock 创建一个进行多次文本换行的标签。

     

     

    为标签添加访问键和文本换行

    如果您需要一个具有访问键(助记键)的 Label,则可以使用 Label 中的 AccessText 元素。

    Label、Button、RadioButton、CheckBox、MenuItem、TabItem、Expander 和 GroupBox 等控件具有默认的控件模板。这些模板包含一个 ContentPresenter。您可以为 ContentPresenter 设置的属性之一是 RecognizesAccessKey="true",您可以使用该属性为控件指定访问键。

    下面的示例演示如何创建一个具有访问键并支持文本换行的 Label。为了实现文本换行,本示例设置了 TextWrapping 属性并使用下划线字符指定访问键。(紧跟下划线字符后面的字符就是访问键。)

    1. WPF控件库之Toolbar

    ToolBar 控件是一组通常在功能上相关的命令或控件的容器。

    下面的插图显示垂直和水平 ToolBar 控件。

    水平 Toolbar

    垂直 Toolbar

    ToolBar 控件
    ToolBar 控件因其按钮或其他控件像条形栏一样排列成一行或一列而得名。WPF ToolBar 控件提供一种溢出机制,将不能自然适合于有大小限制的 ToolBar 的任意项放入一个特殊的溢出区域。另外,WPF ToolBar 控件通常还与相关的 ToolBarTray 控件一起使用,后者提供特殊的布局行为,并支持用户启动的工具栏大小调整和排列。

    在 ToolBarTray 中指定工具栏的位置
    使用 Band 和 BandIndex 属性可以在 ToolBarTray 中定位 ToolBar。Band 指示 ToolBar 在其父 ToolBarTray 中的位置。BandIndex 指示 ToolBar 放入其 Band 中的顺序。下面的示例演示如何使用此属性在 ToolBarTray 内放置 ToolBar 控件。

      

    具有溢出项的工具栏 
    通常,ToolBar 控件包含的项多于工具栏大小可以容纳的项数。出现此情况时,ToolBar 会显示一个溢出按钮。要查看溢出项,用户可以单击溢出按钮,这些项即会显示在 ToolBar 下方的弹出窗口中。下图显示了一个带溢出项的 ToolBar。

    具有溢出项的工具栏


    通过将 ToolBar..::.OverflowMode 附加属性设置为 OverflowMode..::.Always、OverflowMode..::.Never 或 OverflowMode..::.AsNeeded,您可以指定工具栏上的项何时会放置在溢出面板上。下面的示例指定工具栏上的最后四个按钮应总是显示在溢出面板上。

    ToolBar 在其 ControlTemplate 中使用 ToolBarPanel 和 ToolBarOverflowPanel。 ToolBarPanel 负责工具栏上的项的布局。 ToolBarOverflowPanel 负责 ToolBar 上容不下的项的布局。

    1. WPF控件开发之自定义控件

    Windows Presentation Foundation (WPF) 控件模型的扩展性极大减少了创建新控件的需要。但在某些情况下,仍可能需要创建自定义控件。本主题讨论可最大限度减少在 Windows Presentation Foundation (WPF) 中创建自定义控件以及其他控件创作模型的需要的功能。本主题还演示如何创建新控件。

    编写新控件的替代方法
    以前,如果要通过现有控件获取自定义体验,您只能更改控件的标准属性,例如背景色、边框宽度和字号。如果希望在这些预定义参数的基础之上扩展控件的外观或行为,则需要创建新的控件,通常的方法是继承现有控件并重写负责绘制该控件的方法。虽然这仍是一种可选方法,但也可以利用 WPF 丰富内容模型、样式、模板和触发器来自定义现有的控件。下面的列表提供了一些示例,演示如何在不创建新控件的情况下使用这些功能来实现统一的自定义体验。

    丰富内容。 很多标准 WPF 控件支持丰富内容。例如,Button 的内容属性为 Object 类型,因此从理论上讲,任何内容都可以显示在 Button 上。若要让按钮显示图像和文本,可以将图像和 TextBlock 添加到 StackPanel 中,然后将 StackPanel 分配给 Content 属性。由于这些控件可以显示 WPF 可视化元素和任意数据,因此,减少了创建新控件或修改现有控件来支持复杂可视化效果的需要。

    样式。 Style 是表示控件属性的值的集合。使用样式可创建所需控件外观和行为的可重用表示形式,而无需编写新控件。例如,假设希望所有 TextBlock 控件都呈现字号为 14 的红色 Airal 字体。您可以创建一个样式作为资源,然后相应地设置适当的属性。这样,添加到应用程序中的每个 TextBlock 都将具有相同的外观。

    数据模板。 DataTemplate 可用于自定义数据在控件上的显示方式。例如,DataTemplate 可用于指定数据在 ListBox 中的显示方式。有关这种情况的示例,请参见数据模板概述。除了自定义数据外观之外,DataTemplate 还可以包含 UI 元素,这样大大增加了自定义 UI 的灵活性。例如,使用 DataTemplate 可以创建一个 ComboBox,其中每一项都包含一个复选框。

    控件模板 WPF 中的很多控件都使用 ControlTemplate 来定义控件的结构和外观,这样可将控件外观和控件功能分离开。通过重新定义控件的 ControlTemplate,可以彻底更改控件的外观。例如,假设您希望控件看起来像一个交通信号灯。此控件具有简单的用户界面和功能。该控件有三个圆形,一次只能点亮其中的一个。经过考虑之后,您可能意识到 RadioButton 提供了一次只选中一项的功能,但是 RadioButton 的默认外观完全不像交通信号灯上的灯。由于 RadioButton 使用控件模板来定义其外观,因此很容易重新定义 ControlTemplate 以符合该控件的要求,从而使用单选按钮来制作交通信号灯。

    说明:
    尽管 RadioButton 可以使用 DataTemplate,但在本例中,只使用 DataTemplate 还不够。DataTemplate 定义控件内容的外观。对于 RadioButton,指示 RadioButton 是否选中的那个圆形右侧显示出来的全部都是该控件的内容。在交通信号灯的示例中,单选按钮只需要成为可“点亮”的圆形。由于交通信号灯的外观要求与 RadioButton 的默认外观存在很大差异,因此,有必要重新定义 ControlTemplate。一般而言,DataTemplate 用于定义控件的内容(或数据),ControlTemplate 用于定义控件的构成方式。

    触发器。 Trigger 用于在不创建新控件的情况下动态更改控件的外观和行为。例如,假设应用程序中有多个 ListBox 控件,而您希望每个 ListBox 中的项在选中时都显示为红色粗体。您首先想到的可能是创建一个从 ListBox 继承的类,然后重写 OnSelectionChanged 方法,以更改选中项的外观,不过,更好的方法是向 ListBoxItem 的样式添加一个更改选中项外观的触发器。触发器用于更改属性值或根据属性值执行操作。EventTrigger 用于在发生事件时执行操作。

    一般而言,如果控件完全复制现有控件的功能,但您希望该控件具有不同的外观,则应先考虑是否可以使用本节中讨论的某些方法来更改现有控件的外观。

    控件创作模型
    通过丰富内容模型、样式、模板和触发器,最大程度地减少了创建新控件的需要。但是,如果确实需要创建新控件,那么理解 WPF 中的不同控件创作模型就显得非常重要。WPF 提供三个用于创建控件的一般模型,每个模型都提供不同的功能集和灵活度。这三个模型的基类分别为 UserControl、Control 和 FrameworkElement。

    从 UserControl 派生
    在 WPF 中创建控件的最简单方法是从 UserControl 派生。如果生成继承自 UserControl 的控件,需要将现有组件添加到 UserControl,命名这些组件,然后在 可扩展应用程序标记语言 (XAML) 中引用事件处理程序。执行这些操作之后,即可在代码中引用这些命名元素和定义事件处理程序。此开发模型非常类似于用于 WPF 应用程序开发的模型。

    如果生成正确,UserControl 可以利用丰富内容、样式和触发器的优点。但是,如果控件继承自 UserControl,则使用该控件的用户将无法使用 DataTemplate 或 ControlTemplate 来自定义其外观。因此,有必要从 Control 类或其派生类(UserControl 除外)进行派生,以便创建支持模板的自定义控件。

    从 UserControl 派生的优点
    如果符合以下所有情况,请考虑从 UserControl 派生:

    希望以类似于生成应用程序的方式生成控件。

    控件仅由现有组件组成。

    不需要支持复杂自定义项。

    从 Control 派生
    从 Control 类派生是大多数现有 WPF 控件使用的模型。在创建继承自 Control 类的控件时,可使用模板定义其外观。通过这种方式,可以将运算逻辑从可视化表示形式中分离出来。通过命令和绑定(而不是事件),也可确保分离 UI 和逻辑,尽可能避免引用 ControlTemplate 中的元素。如果控件的 UI 和逻辑正确分离,该控件的用户即可重新定义其 ControlTemplate,从而自定义其外观。尽管生成自定义 Control 不像生成 UserControl 那样简单,自定义 Control 还是提供了最大的灵活性。

    从 Control 派生的优点
    如果符合以下任一情况,请考虑从 Control 派生,而不要使用 UserControl 类:

    希望控件外观能通过 ControlTemplate 进行自定义。

    希望控件支持不同的主题。

    从 FrameworkElement 派生
    从 UserControl 或 Control 派生的控件依赖于组合现有元素。很多情况下,这是一种可接受的解决方案,因为从 FrameworkElement 继承的任何对象都可以位于 ControlTemplate 中。但是,某些时候,简单的元素组合不能满足控件的外观需要。对于这些情况,使组件基于 FrameworkElement 才是正确的选择。

    生成基于 FrameworkElement 的组件有两种标准方法:直接呈现和自定义元素组合。直接呈现涉及的操作包括:重写 FrameworkElement 的 OnRender 方法,并提供显式定义组件视觉效果的 DrawingContext 操作。此方法由 Image 和 Border 使用。自定义元素组合涉及的操作包括使用 Visual 类型的对象组合组件的外观。有关示例,请参见使用 DrawingVisual 对象。Track 是 WPF 中使用自定义元素组合的控件示例。在同一控件中,也可以混合使用直接呈现和自定义元素组合。

    从 FrameworkElement 派生的优点
    如果符合以下任一情况,请考虑从 FrameworkElement 派生:

    希望对控件的外观进行精确控制,而不仅仅是简单的元素组合提供的效果。

    想要通过定义自己的呈现逻辑来定义控件的外观。

    想要以一种 UserControl 和 Control 之外的新颖方式组合现有元素。

     控件创作基础知识
    如前所述,WPF 最强大的功能之一是,无需创建自定义控件即可实现远比设置控件的基本属性来更改其外观和行为更强的功能。WPF 属性系统和 WPF 事件系统使样式、数据绑定和触发器功能成为可能。如果在控件中实现依赖项属性和路由事件,则无论使用什么模型创建自定义控件,自定义控件的用户都可以像对 WPF 随附的控件那样使用这些功能。

    使用依赖项属性
    当属性为依赖项属性时,可以进行下面的操作:

    在样式中设置该属性。

    将该属性绑定到数据源。

    使用动态资源作为该属性的值。

    动画处理该属性。

    如果希望控件的属性支持以上任一功能,则应将该属性实现为依赖项属性。下面的示例定义一个依赖项属性。

    这段代码执行下面的操作:

    将一个名为 ValueProperty 的 DependencyProperty 标识符定义为 publicstaticreadonly 字段。

    通过调用 DependencyProperty..::.Register 向属性系统注册该属性名,以指定以下内容:

    属性的名称。

    属性的类型。

    拥有该属性的类型。

    属性的元数据。元数据包含该属性的默认值、CoerceValueCallback 和 PropertyChangedCallback。

    通过实现该属性的 get 和 set 访问器,定义一个名为 Value 的 CLR“包装”属性,这个名称也就是用来注册该依赖项属性的名称。请注意,get 和 set 访问器只是分别调用 GetValue 和 SetValue。建议依赖项属性的访问器不要包含其他逻辑,这是因为客户端和 WPF 可绕过这两个访问器直接调用 GetValue 和 SetValue。例如,如果属性绑定到数据源,则不会调用该属性的 set 访问器。不要向 get 和 set 访问器添加其他逻辑,而应使用 ValidateValueCallback、CoerceValueCallback 和 PropertyChangedCallback 委托在值更改时进行响应或检查该值。

    为 CoerceValueCallback 定义一个名为 CoerceValue 的方法。CoerceValue 确保 Value 大于或等于 MinValue 且小于或等于 MaxValue。

    为 PropertyChangedCallback 定义一个名为 OnValueChanged 的方法。OnValueChanged 创建一个 RoutedPropertyChangedEventArgs<(Of <(T>)>) 对象,并准备引发 ValueChanged 路由事件。路由事件在下一节中讨论。

    使用 RoutedEvent
    就像依赖项属性以附加功能扩展 CLR 属性的用途一样,路由事件扩展了标准 CLR 事件的用途。在创建新的 WPF 控件时,将事件实现为路由事件也是一种好方法,这是因为路由事件支持以下行为:

    事件可以在多个控件的父级上进行处理。如果事件是冒泡事件,则元素树中的单个父级可预订该事件。然后,应用程序作者可以使用一个处理程序来响应多个控件的该事件。例如,如果控件属于 ListBox 中的每个项(因为它包含在 DataTemplate 中),则应用程序开发人员可以为该控件的 ListBox 事件定义相应的事件处理程序。每当这些控件中的任何控件发生该事件时,都会调用该事件处理程序。

    路由事件可在 EventSetter 中使用,应用程序开发人员通过 EventSetter 可以在样式内指定事件的处理程序。

    路由事件可在 EventTrigger 中使用,这对于使用 XAML 对属性进行动画处理很有用。

    下面的示例定义了一个路由事件。

    这段代码执行下面的操作:

    将一个名为 ValueChangedEvent 的 RoutedEvent 标识符定义为 publicstaticreadonly 字段。

    通过调用 EventManager..::.RegisterRoutedEvent 方法注册路由事件。该示例在调用 RegisterRoutedEvent 时指定以下信息:

    事件的名称为 ValueChanged。

    路由策略为 Bubble,这意味着首先调用源(引发事件的对象)上的事件处理程序,然后从最近的父元素上的事件处理程序开始,相继调用源的各个父元素上的事件处理程序。

    该事件处理程序的类型为 RoutedPropertyChangedEventHandler<(Of <(T>)>),是用 Decimal 类型构造的。

    该事件的所属类型为 NumericUpDown。

    声明一个名为 ValueChanged 的公共事件,并包含事件访问器声明。该示例调用 add 访问器声明中的 AddHandler 和 remove 访问器声明中的 RemoveHandler 来使用 WPF 事件服务。

    创建一个名为 OnValueChanged 的受保护的虚方法,该方法引发 ValueChanged 事件。

    使用绑定
    若要将控件 UI 与其逻辑分离,请考虑使用数据绑定。如果是使用 ControlTemplate 定义控件的外观,这一点尤其重要。使用数据绑定时,可能能够避免引用 UI 的特定部分。

    下面的示例更新 NumericUpDown 控件的 TextBlock,向它分配一个名称,然后在代码中按名称引用该文本框。

    下面的示例使用绑定来达到相同的目的。

    针对设计器设计
    若要在 Visual Studio Windows Presentation Foundation (WPF) 设计器中获得对自定义 WPF 控件的支持(例如,使用“属性”窗口编辑属性),请遵循以下准则。

    依赖项属性
    确保实现 CLRget 访问器和 set 访问器,如前面“使用依赖项属性”中所述。设计器可以使用包装来检测某个依赖项属性是否存在,但与 WPF 和控件客户端一样,在获取或设置属性时不需要使用设计器来调用访问器。

    附加属性
    应按照以下原则在自定义控件上实现附加属性:

    具有一个使用 RegisterAttached 方法创建的 publicstaticreadonlyDependencyProperty,其形式为“属性名称Property”。传递到 RegisterAttached 的属性名称必须与属性名称 匹配。

    实现一对名为 Set属性名称 和 Get属性名称 的 public static CLR 方法。这两种方法都应接受从 DependencyProperty 派生的类作为其第一个参数。Set属性名称 方法还接受其类型与属性的注册数据类型匹配的参数。Get属性名称 方法应返回相同类型的值。如果缺少 Set属性名称 方法,则该属性标记为只读。

    Set 属性名称 和 Get属性名称 必须分别直接路由到目标依赖项对象的 GetValue 和 SetValue 方法。通过调用方法包装或直接调用目标依赖项对象,设计器可以访问附加属性。

    创建 UserControl
    如前所述,在 WPF 中创建控件的最简单方法是从 UserControl 派生。下面的示例演示用于定义 NumericUpDownUserControl 的 用户界面 (UI) 的 XAML:

    下面的示例演示此 UserControl 的逻辑。

    如此示例所示,自定义 UserControl 的开发模型非常类似于用于应用程序开发的模型。

    创建自定义控件
    生成支持模板的控件
    UserControl 提供了一种简单方法在 WPF 中生成可重用的功能,但要利用模板化和支持不同主题,则要使用的模型为 Control。本节将上一节中的 UserControl 示例转换为自定义 Control。

    更改基类
    首先,将 UserControl 基类替换为 Control。

    移动到模板
    一旦更新了基类,则需要将控件的内容移动到模板。模板在可位于应用程序中的很多位置的样式中定义。对于此示例,样式位于应用程序资源中。

    在 UserControl 示例中,TextBlock 和 RepeatButton 实例已指定了名称。RepeatButton 实例还引用了代码中定义的事件处理程序。可以在此自定义 Control 中移除这两个实例,因为将通过更松散耦合的方式,改为使用绑定和命令来获取相同的行为。

    处理输入
    在 UserControl 示例中,RepeatButton 实例直接引用了代码中定义的事件处理程序。对于自定义 Control,命令是实现相同行为的一种更灵活的方式。控件可以定义命令,如下面的示例所示。

    然后,模板中的元素可以引用这些命令,如下面的示例所示。

    通过定义模板以及使用绑定和命令,您已将 NumericUpDown 控件从具有固定可视化效果的静态 UserControl 更改为可灵活自定义的自定义 Control。

    外部控件库
    最后一步是将 NumericUpDown 控件打包到其自己的程序集中,以使重用更容易。

    创建主题文件
    将 NumericUpDown 类移动到库程序集之后,需要移动样式定义。首先,需要创建一个“themes”文件夹用于存储所有主题文件。接下来,创建一个名为 generic.xaml 的文件。此文件将用作此程序集的所有资源查找的回退。

    在 generic.xaml 中定义一个 ResourceDictionary,并在 ResourceDictionary 中放置 NumericUpDown 控件的样式。

    定义 ThemeInfo 属性
    若要找到 generic.xaml 中的 Style,宿主应用程序需要知道该程序集包含控件特定的资源。可以通过向类中添加程序集属性来实现该任务。由于程序集仅包含一般资源,因此将 GenericDictionaryLocation 属性设置为 SourceAssembly。下面的示例显示了 AssemblyInfo.cs 文件的内容。

    为 Windows 主题提供支持
    一个控件在大量不同 WPF主题中可能有不同的外观。若要支持多个主题,必须使用控件所需的正确样式、模板和其他资源来定义主题文件。还需要将 ThemeInfoAttribute 属性 (Attribute) 的 ThemeDictionaryLocation 属性 (Property) 设置为引用源程序集,如下面的示例所示。

    本文档概述在设计可方便地样式化和模板化的控件时需要考虑的一组最佳做法。在为内置的 WPF 控件集处理主题控件样式时,我们通过大量试验和错误总结出了这组最佳做法。我们已经认识到,成功的样式化不只是设计完善的对象模型的功能,也是样式本身的功能。本文档面向控件作者,而不是样式作者。

    术语
    “样式化和模板化”是一组技术,控件作者可以通过该组技术来将控件的可视化特性延迟到控件的样式和模板。这组技术包括:

    样式(包括属性 setter、触发器和演示图板)。

    资源。

    控件模板。

    数据模板。

    准备工作:了解您的控件
    在开始阅读本文档中的准则之前,一定要了解并定义了控件的一般用法,这一点很重要。样式化公开一组通常不受约束的可能性。旨在由许多开发人员在许多应用程序中广泛使用的控件面临着如下挑战:可以使用样式化来对控件的可视化外观进行广泛的更改。实际上,带有样式的控件甚至可能并非控件作者的本意。由于样式化在本质上可以提供无限的灵活性,因此您可以使用“一般用法”这一概念来帮助您限制自己的决定。

    为了了解控件的一般用法,最好考虑控件的价值主张。您的控件能够提供哪些无法由其他控件提供的内容? 一般用法并不表示任何特定的可视化外观,而是表示控件的基本原理和一组有关其用法的合理预期。了解到这一点,就可以对控件在一般情况下的撰写模型和样式定义行为进行一些假设。例如,对于 ComboBox,如果您了解其一般用法,并不意味着您非常清楚特定的 ComboBox 是否有圆角,但是您将由此清楚 ComboBox 可能需要一个弹出窗口和某种用来切换其开关状态的方法。

    通用准则
    不严格实施模板约定。 控件的模板约定可以包含元素、命令、绑定和触发器,甚至还可以包含必需的或为了使控件正常工作而应当使用的属性设置。

    尽可能减少约定。

    围绕如下预期来设计:在设计时(即,在使用设计工具时),控件模板通常处于不完整状态。WPF 不提供“正在撰写”状态的基础结构,因此,控件必须围绕这样的状态可能有效这一预期来生成。

    在没有遵循模板约定的任何方面时,不引发异常。按照这一原则,当面板的子级太多或太少时,面板不应当引发异常。

    将外围功能分解成模板帮助器元素。 每个控件都应当将重点放在其核心功能和真正的价值主张上,而且每个控件都应当由控件的一般用法来定义。为此,请使用模板中的撰写和帮助器元素来实现外围行为和可视化,即,那些不构成控件核心功能的行为和可视化。帮助器元素可分为三类:

    独立帮助器类型是以“匿名方式”用在模板中的可重用的公共控件或基元,这意味着帮助器元素和带有样式的控件无法互相识别。在技术上,任何元素都可以是匿名类型,但是在这里,术语“独立”用于描述那些封装专用功能以实现目标方案的类型。

    基于类型的帮助器元素是封装专用功能的新类型。通常,这些元素在设计上比公用控件或基元的功能范围要窄。与独立帮助器元素不同的是,基于类型的帮助器元素能够识别它们的使用上下文,而且通常必须与包含它们所属模板的控件共享数据。

    命名的帮助器元素是控件应当能够在其模板中根据名称找到的公用控件或基元。这些元素在模板中具有一个已知的名称,这使得控件能够找到这些元素并以编程方式与之交互。在任何模板中,都只能有一个具有给定名称的元素。

    下表显示的是由目前的控件样式使用的部分帮助器元素列表:

    尽可能减少帮助器元素所必需的、特定于用户的绑定或属性设置。通常,帮助器元素需要某些绑定或属性设置才能在控件模板中正确工作。帮助器元素和模板化控件应当尽可能多地生成这些设置。在设置属性或者建立绑定时,注意不要重写由用户设置的值。具体的最佳做法如下所示:

    命名的帮助器元素应当由父级标识,而且父级应当针对帮助器元素建立任何必需的设置。

    对于基于类型的帮助器元素,应当直接针对它们建立任何必需的设置。这样做可能需要帮助器元素查找它在使用时的信息上下文,包括其 TemplatedParent(它在使用时的模板的控件类型)。例如,在用于 ContentControl 派生类型时,ContentPresenter 会自动将它的 TemplatedParent 的 Content 属性绑定到它的 Content 属性。

    独立帮助器元素不能按这种方式进行优化,这是因为按照定义,帮助器元素和父级不能相互识别。

    使用 Name 属性来标记模板中的元素。如果控件需要在样式中查找某个元素才能以编程方式访问它,则该控件应当使用 Name 属性和 FindName 范例来进行查找。控件不应在未找到所需元素时引发异常,而是应在不提示的情况下禁用需要该元素的功能。

    使用最佳做法来表示样式中的控件状态和行为。 下面按顺序列出了用来表示样式中的控件状态更改和行为的最佳做法。您应当使用列表上的第一项来实现您的方案。

    属性绑定。示例:在 ComboBox..::.IsDropDownOpen 和 ToggleButton..::.IsChecked 之间绑定。

    触发的属性更改或属性动画。示例:Button 的悬停状态。

    命令。示例:ScrollBar 中的 LineUpCommand / LineDownCommand。

    独立帮助器元素。示例:TabControl 中的 TabPanel。

    基于类型的帮助器类型。示例:Button 中的 ContentPresenter 和 Slider 中的 TickBar。

    命名的帮助器元素。示例:ComboBox 中的 TextBox。

    命名帮助器类型中的冒泡事件。如果您侦听样式元素中的冒泡事件,则应当要求生成该事件的元素能够进行唯一标识。示例:ToolBar 中的 Thumb。

    自定义 OnRender 行为。示例:Button 中的 ButtonChrome。

    慎用样式触发器(与模板触发器相对)。会影响到模板中元素属性的触发器必须在模板中进行声明。会影响到控件属性(没有 TargetName)的触发器可以在样式中声明,除非您知道更改模板还可能会损坏触发器。

    与现有的样式模式一致。 一个问题常常有多种解决办法。注意尽可能与现有的控件样式模式保持一致。这对于派生自同一基类型(例如,ContentControl、ItemsControl、RangeBase 等)的控件尤其重要。

    在不重新模板化的情况下公开属性来启用常见自定义项方案。WPF 不支持可插入/可自定义的部件,因此控件用户只能使用两种自定义方法:直接设置属性或者使用样式设置属性。请记住,比较合适的做法是,设置数量有限的属性,使其面向极其常见的高优先级自定义项方案,否则的话,这些方案需要重新模板化。下面是有关何时以及如何启用自定义项方案的最佳方法:

    极其常见的自定义项应当作为属性在控件上公开并由模板使用。

    不太常见(尽管并非极少见)的自定义项应当作为附加属性公开并由模板使用。

    需要对已知但是极少见的自定义项重新模板化,这一点也是可以接受的。

    主题注意事项
    主题样式应当尝试在所有的主题之间具有一致的属性语义,但不保证能够实现这一点。作为控件文档的一部分,控件应当具有一个描述其属性语义(即控件属性的“含义”)的文档。例如,ComboBox 控件应当定义 Background 属性在 ComboBox 中的含义。控件的默认样式应当尝试遵循在其文档中的所有主题中定义的语义。另一方面,控件用户应当注意属性语义可能因主题而异。在某些情况下,给定的属性在由特定主题所需的可视化约束下可能无法表示 (例如,对于许多控件来说,传统主题没有可以向其应用 Thickness 的边框)。

    主题样式不需要在所有的主题之间具有一致的触发器语义。由控件样式通过触发器或动画公开的行为可能因主题而异。控件用户应当注意到,控件将不必使用同一个机制在所有的主题中实现一个特定的行为。例如,一个主题可以使用动画来表示悬停行为,而另一个主题则可以使用触发器。这可能会导致自定义控件上的行为保留出现不一致现象 (例如,如果控件的悬停状态是使用触发器来表示的,那么,更改背景属性可能不会影响该状态)。但是,如果悬停状态是使用动画来实现的,那么,更改背景属性可能会不可挽回地中断动画,从而中断状态过渡)。

    主题样式不需要在所有的主题之间具有一致的“布局”语义。例如,默认的样式不需要保证控件将在所有的主题中占用同样的大小,也不需要保证控件将在所有的主题中具有同样的内容边距/空白。

    1. WPF控件开发之装饰器

    装饰器是一种特殊类型的 FrameworkElement,用于向用户提供可视化提示。对于其他用户,装饰器可用于将功能控点添加到元素中或提供有关控件的状态信息。

     关于装饰器
    Adorner 是绑定到 UIElement 的自定义 FrameworkElement。装饰器在 AdornerLayer 中呈现,这是一个始终位于装饰元素或装饰元素集合上方的呈现图面。装饰器的呈现独立于该装饰器所绑定到的 UIElement 的呈现。装饰器通常使用位于装饰元素左上部的标准 2-D 坐标原点,相对于其绑定到的元素进行定位。

    装饰器的常见应用包括:

    向 UIElement 添加功能控点,使用这些控点,用户可以通过某种方式(调整大小、旋转、重新定位等等)操作元素。

    提供可视反馈以指示各种状态,或响应各种事件。

    在 UIElement 上叠加视觉效果。

    从视觉上遮盖或重写 UIElement 的一部分或全部。

    Windows Presentation Foundation (WPF) 为装饰视觉元素提供一个基本框架。下表列出了装饰对象时使用的主要类型及其用途。下面是几个用法示例。

    Adorner
    一个抽象基类,所有具体装饰器的实现都从该类继承。

    AdornerLayer
    一个类,表示一个或多个装饰元素的装饰器的呈现层。

    AdornerDecorator
    一个类,使装饰器层与元素集合相关联。

     实现自定义装饰器
    Windows Presentation Foundation (WPF) 提供的装饰器框架主要用于支持创建自定义装饰器。您可以通过实现一个从抽象 Adorner 类继承的类来创建自定义装饰器。

    说明:
    Adorner 的父级为 AdornerLayer,它呈现的是 Adorner,而不是正在装饰的元素。

    下面的示例演示一个实现简单装饰器的类。该示例装饰器使用圆简单地装饰 UIElement 的各角。

    下面的图像演示了应用于 TextBox 的 SimpleCircleAdorner。

     装饰器的呈现行为
    注意:装饰器不包括任何继承呈现行为,确保装饰器呈现是装饰器实施者的责任。 实现呈现行为的常见方式是重写 OnRenderSizeChanged 方法,并使用一个或多个 DrawingContext 对象来按需呈现装饰器的视觉效果(如上述示例所示)。

    说明:
    放置在装饰器层中的所有内容均呈现在您已设置的任何其余样式的顶部。也就是说,装饰器始终以可见的方式位于顶部,无法使用 z 顺序重写。

    事件和命中测试
    装饰器就像任何其他 FrameworkElement 一样接收输入事件。 因为装饰器的 z 顺序总是高于它所装饰的元素,所以该装饰器接收可能适用于基础装饰元素的输入事件(例如 Drop 或 MouseMove)。 装饰器可以侦听某些输入事件,并通过重新引发这些事件将其传递给基础装饰元素。

    若要对装饰器下的元素启用传递命中测试,请在装饰器上将命中测试 IsHitTestVisible 的属性设置为 false。

    装饰单个 UIElement
    若要将装饰器绑定到特定的 UIElement,请按照以下步骤操作:

    调用静态方法 GetAdornerLayer,为要装饰的 UIElement 获取一个 AdornerLayer 对象。GetAdornerLayer 从指定的 UIElement 开始沿着可视树向上走,返回它所发现的第一个装饰器层。(如果未发现装饰器层,则该方法返回 Null。)

    调用 Add 方法将装饰器绑定到目标 UIElement。

    以下示例将 SimpleCircleAdorner(如上述所示)绑定到名为 myTextBox 的 TextBox。

    说明:
    目前不支持使用可扩展应用程序标记语言 (XAML) 将装饰器绑定到另一个元素。

    装饰面板的子级
    若要将装饰器绑定到 Panel 的子级,请按照下列步骤操作:

    调用 static 方法 GetAdornerLayer 为要绑定其子级的元素查找一个装饰器层。

    依次枚举父元素的子级并调用 Add 方法将装饰器绑定到每个子级元素。

    以下示例将 SimpleCircleAdorner(如上所示)绑定到名为 myStackPanel 的 StackPanel 的子级。

    WPF数据绑定

    1. 数据绑定概述

    Windows Presentation Foundation (WPF) 数据绑定为应用程序提供了一种简单而一致的方法来显示数据以及与数据交互。元素可以以公共语言运行库 (CLR) 对象和 XML 的形式绑定到各种数据源的数据。ContentControl(如 Button)和 ItemsControl(如 ListBox 和 ListView)具有内置功能,使单个数据项或数据项集合可以进行灵活的样式设置。可以在数据之上生成排序、筛选和分组视图。

    WPF 中的数据绑定功能与传统模型相比具有一些优势,包括本质上支持数据绑定的各种属性、灵活的数据 UI 表示形式,以及业务逻辑与 UI 的完全分离。

    本主题首先讨论 WPF 数据绑定的基本概念,然后详细介绍 Binding 类以及数据绑定的其他功能的用法。

    什么是数据绑定?
    数据绑定是在应用程序 UI 与业务逻辑之间建立连接的过程。如果绑定具有正确设置并且数据提供正确通知,则当数据更改其值时,绑定到数据的元素会自动反映更改。数据绑定可能还意味着如果元素中数据的外部表现形式发生更改,则基础数据可以自动更新以反映更改。例如,如果用户编辑 TextBox 元素中的值,则基础数据值会自动更新以反映该更改。

    数据绑定的一种典型用法是将服务器或本地配置数据放置到窗体或其他 UI 控件中。在 WPF 中,此概念得到扩展,包括了大量属性与各种数据源的绑定。在 WPF 中,元素的依赖项属性可以绑定到 CLR 对象(包括 ADO.NET 对象或与 Web 服务和 Web 属性相关联的对象)和 XML 数据。

    有关数据绑定的示例,请看一看来自数据绑定演示的以下应用程序 UI:

    上面是显示拍卖项列表的应用程序 UI。该应用程序演示数据绑定的以下功能:

    ListBox 的内容绑定到 AuctionItem 对象的集合。AuctionItem 对象具有一些属性,如 Description、StartPrice、StartDate、Category、SpecialFeatures。

    ListBox 中显示的数据(AuctionItem 对象)进行模板化,以便显示每个拍卖项的说明和当前价格。这是使用一个 DataTemplate 实现的。此外,每个项的外观取决于要显示的 AuctionItem 的 SpecialFeatures 值。如果 AuctionItem 的 SpecialFeatures 值为 Color,则该项具有蓝色边框。如果该值为 Highlight,则该项具有橙色边框和一个星号。

    用户可以使用提供的 CheckBox 对数据进行分组、筛选或排序。在上面的图像中,选中了“Group by category”(按类别分组)和“Sort by category and date”(按类别和日期排序)CheckBox。您可能已经注意到数据是根据产品类别分组的,而且类别名称按字母顺序排序。这些项在每个类别中也是按照起始日期排序的,虽然从该图像中很难注意到这一点。这是使用集合视图 实现的。

    当用户选中一个项时,ContentControl 会显示选定项的详细信息。这称为主从方案。

    StartDate 属性的类型为 DateTime,该类型返回一个日期,包括精确到毫秒的时间。在此应用程序中,使用了一个自定义转换器,以便显示较短的日期字符串。

    当用户单击“Add Product”(添加产品)按钮时,会出现下面的窗体:

    用户可以编辑窗体中的字段,使用简略预览和详细预览窗格来预览产品清单,然后单击“submit”(提交)以添加新的产品清单。任何现有的分组、筛选和排序功能都会应用于新项。在这种特殊情况下,在上面图像中输入的项会作为 Computer 类别中的第二项显示。

    “Start Date”(起始日期)TextBox 中提供的验证逻辑未在此图像中显示。如果用户输入一个无效日期(无效的格式或过去的日期),则会通过一个 ToolTip 和 TextBox 旁的一个红色感叹号来通知用户。数据验证一节讨论了如何创建验证逻辑。

    在详细介绍数据绑定的上述不同功能之前,我们会先在下一节中讨论一些对理解 WPF 数据绑定非常重要的基本概念。

    基本数据绑定概念

    不论要绑定什么元素,不论数据源的特性是什么,每个绑定都始终遵循下图所示的模型:


    如上图所示,数据绑定实质上是绑定目标与绑定源之间的桥梁。该图演示以下基本的 WPF 数据绑定概念:

    通常,每个绑定都具有四个组件:绑定目标对象、目标属性、绑定源,以及要使用的绑定源中的值的路径。例如,如果要将 TextBox 的内容绑定到 Employee 对象的 Name 属性,则目标对象是 TextBox,目标属性是 Text 属性,要使用的值是 Name,源对象是 Employee 对象。

    目标属性必须为依赖项属性。大多数 UIElement 属性都是依赖项属性,而大多数依赖项属性(除了只读属性)默认情况下都支持数据绑定。(只有 DependencyObject 类型可以定义依赖项属性,所有 UIElement 都派生自 DependencyObject。)

    尽管图中并未指出,但应该注意,绑定源对象并不限于自定义 CLR 对象。WPF 数据绑定支持 CLR 对象和 XML 形式的数据。举例来说,绑定源可以是 UIElement、任何列表对象、与 ADO.NET 数据或 Web 服务关联的 CLR 对象,或是包含 XML 数据的 XmlNode。

    在阅读其他软件开发工具包 (SDK) 主题时,请务必记住一点:当您建立绑定时,您是将绑定目标绑定到 绑定源。例如,如果您要使用数据绑定在一个 ListBox 中显示一些基础 XML 数据,就是将 ListBox 绑定到 XML 数据。

    若要建立绑定,请使用 Binding 对象。本主题的剩余部分会讨论与 Binding 对象相关的许多概念以及该对象的一些属性和用法。

    数据流的方向
    正如上文所述和上图中箭头所示,绑定的数据流可以从数据目标流向数据源(例如,当用户编辑 TextBox 的值时,源值会发生更改)和/或(如果绑定源提供正确的通知)从绑定源流向绑定目标(例如,TextBox 内容会随绑定源中的更改而进行更新)。

    您可能希望应用程序使用户可以更改数据并将数据传播回源对象。或者,您可能不希望允许用户更新源数据。您可以通过设置 Binding 对象的 Mode 属性来对此进行控制。下图演示不同类型的数据流:

    OneWay 绑定导致对源属性的更改会自动更新目标属性,但是对目标属性的更改不会传播回源属性。此绑定类型适用于绑定的控件为隐式只读控件的情况。例如,您可能绑定到如股票行情自动收录器这样的源,或许目标属性没有用于进行更改的控件接口(如表的数据绑定背景色)。如果无需监视目标属性的更改,则使用 OneWay 绑定模式可避免 TwoWay 绑定模式的系统开销。

    TwoWay 绑定导致对源属性的更改会自动更新目标属性,而对目标属性的更改也会自动更新源属性。此绑定类型适用于可编辑窗体或其他完全交互式 UI 方案。大多数属性都默认为 OneWay 绑定,但是一些依赖项属性(通常为用户可编辑的控件的属性,如 TextBox 的 Text 属性和 CheckBox 的 IsChecked 属性)默认为 TwoWay 绑定。确定依赖项属性在默认情况下是单向绑定还是双向绑定的一种编程方式是使用 GetMetadata 获取属性的属性元数据,然后检查 BindsTwoWayByDefault 属性的布尔值。

    OneWayToSource 与 OneWay 绑定相反;它在目标属性更改时更新源属性。一个示例方案是您只需要从 UI 重新计算源值的情况。

    OneTime 绑定未在图中显示,该绑定会导致源属性初始化目标属性,但不传播后续更改。这意味着,如果数据上下文发生了更改,或者数据上下文中的对象发生了更改,则更改会反映在目标属性中。如果您使用的数据的当前状态的快照适于使用,或者这些数据是真正静态的,则适合使用此绑定类型。如果要使用源属性中的某个值初始化目标属性,并且事先不知道数据上下文,则也可以使用此绑定类型。此绑定类型实质上是 OneWay 绑定的简化形式,在源值不更改的情况下可以提供更好的性能。

    请注意,若要检测源更改(适用于 OneWay 和 TwoWay 绑定),则源必须实现一种合适的属性更改通知机制(如 INotifyPropertyChanged)。

    触发源更新的原因
    TwoWay 或 OneWayToSource 绑定侦听目标属性的更改,并将这些更改传播回源。这称为更新源。例如,可以编辑文本框中的文本以更改基础源值。如上一节中所述,数据流的方向由绑定的 Mode 属性的值确定。

    但是,源值是在您编辑文本的同时进行更新,还是在您结束编辑文本并将鼠标指针从文本框移走后才进行更新呢? 绑定的 UpdateSourceTrigger 属性确定触发源更新的原因。下图中右箭头的点演示 UpdateSourceTrigger 属性的角色:


    如果 UpdateSourceTrigger 值为 PropertyChanged,则 TwoWay 或 OneWayToSource 绑定的右箭头指向的值会在目标属性更改时立刻进行更新。但是,如果 UpdateSourceTrigger 值为 LostFocus,则仅当目标属性失去焦点时,该值才会使用新值进行更新。

    与 Mode 属性类似,不同的依赖项属性具有不同的默认 UpdateSourceTrigger 值。大多数依赖项属性的默认值都为 PropertyChanged,而 Text 属性的默认值为 LostFocus。这意味着,只要目标属性更改,源更新通常都会发生,这对于 CheckBox 和其他简单控件很有用。但对于文本字段,每次键击之后都进行更新会降低性能,用户也没有机会在提交新值之前使用退格键修改键入错误。这就是为什么 Text 属性的默认值是 LostFocus 而不是 PropertyChanged 的原因。

    下表使用 TextBox 作为示例提供每个 UpdateSourceTrigger 值的示例方案:

     

    创建绑定

    前面几节中讨论的一些概念可以概括为:使用 Binding 对象建立绑定,每个绑定通常都具有四个组件:绑定目标、目标属性、绑定源、要使用的源值的路径。本节讨论如何设置绑定。

    请看下面的示例,其中的绑定源对象是一个名为 MyData 的类,该类在 SDKSample 命名空间中定义。出于演示的目的,MyData 类具有一个名为 ColorName 的字符串属性,该属性的值设置为“Red”。因此,此示例生成一个具有红色背景的按钮。

    如果将此示例应用于基本关系图,则生成的图如下所示。这是一个 OneWay 绑定,因为 Background 属性在默认情况下支持 OneWay 绑定。

     

    您可能会奇怪为什么会这样,即使 ColorName 属性为类型字符串,而 Background 属性为 Brush 类型。这是由于进行了默认类型转换,此类型转换在数据转换一节中进行了讨论。

    指定绑定源
    请注意,在上一个示例中,绑定源是通过设置 DockPanel 元素上的 DataContext 属性来指定的。Button 随后从 DockPanel(这是其父元素)继承 DataContext 值。在这里重复一下,绑定源对象是绑定的四个必需组件之一。因此,如果未指定绑定源对象,则绑定将没有任何作用。

    可通过多种方法指定绑定源对象。在将多个属性绑定到相同源时,可以使用父元素上的 DataContext 属性。但是,在各个绑定声明上指定绑定源有时可能更为合适。对于上一个示例,可以不使用 DataContext 属性,而是通过在按钮的绑定声明上直接设置 Source 属性来指定绑定源,如下面的示例中所示:

    除了在元素上直接设置 DataContext 属性、从上级继承 DataContext 值(如第一个示例中的按钮)、通过设置 Binding 上的 Source 属性来显式指定绑定源(如最后一个示例中的按钮),还可以使用 ElementName 属性或 RelativeSource 属性指定绑定源。当绑定到应用程序中的其他元素时(例如在使用滑块调整按钮的宽度时),ElementName 属性是很有用的。当在 ControlTemplate 或 Style 中指定绑定时,RelativeSource 属性是很有用的。

    指定值的路径
    如果绑定源是一个对象,则可使用 Path 属性指定要用于绑定的值。如果要绑定到 XML 数据,则可使用 XPath 属性指定该值。在某些情况下,可以使用 Path 属性,即使在数据为 XML 时。例如,如果要访问返回的 XmlNode(作为 XPath 查询的结果)的 Name 属性,则应使用 Path 属性和 XPath 属性。

    请注意,虽然我们已强调要使用的值的 Path 是绑定的四个必需组件之一,但在要绑定到整个对象的情况下,要使用的值会与绑定源对象相同。在这些情况下,不指定 Path 比较合适。请看下面的示例:

    上面的示例使用空绑定语法:{Binding}。在此情况下,ListBox 从父 DockPanel 元素继承 DataContext(此示例中未演示)。当未指定路径时,默认为绑定到整个对象。换句话说,在此示例中路径已被省略,因为要将 ItemsSource 属性绑定到整个对象。

    除了绑定到集合以外,在希望绑定到整个对象,而不是仅绑定到对象的单个属性时,也可以使用此方案。例如,在源对象为类型字符串,并且您仅仅希望绑定到该字符串本身时。另一种常见情况是您希望将一个元素绑定到具有多个属性的一个对象。

    请注意,您可能需要应用自定义逻辑,使数据对您的绑定目标属性有意义。自定义逻辑的形式可以是自定义转换器(如果默认类型转换不存在)。

    Binding 和 BindingExpression
    在详细介绍数据绑定的其他功能和用法之前,介绍 BindingExpression 类会十分有用。如前面几节所述,Binding 类是用于绑定声明的高级别类;Binding 类提供了很多属性,您可以利用这些类来指定绑定的特征。相关类 BindingExpression 是维持源与目标之间的连接的基础对象。一个绑定包含可以在多个绑定表达式之间共享的所有信息。BindingExpression 是无法共享的实例表达式,其中包含有关 Binding 的所有实例信息。

    例如,请看下面的示例,其中 myDataObject 是 MyData 类的实例,myBinding 是源 Binding 对象,MyData 类是包含一个名为 MyDataProperty 的字符串属性的已定义类。此示例将 mytext(TextBlock 的实例)的文本内容绑定到 MyDataProperty。

    您可以使用相同的 myBinding 对象来创建其他绑定。例如,可以使用 myBinding 对象将复选框的文本内容绑定到 MyDataProperty。在这种情况下,将有两个 BindingExpression 实例共享 myBinding 对象。

    可以通过对数据绑定对象调用 GetBindingExpression 的返回值来获取 BindingExpression 对象。以下主题演示 BindingExpression 类的一些用法:

    如何:从绑定目标属性获取绑定对象

    如何:控制文本框文本更新源的时间

    数据转换
    在上面的示例中,按钮是红色的,因为其 Background 属性绑定到一个值为“Red”的字符串属性。可以这样做的原因是 Brush 类型上提供了一个类型转换器,可以将字符串值转换为 Brush。

    要将此信息添加到创建绑定一节的图中,关系图如下所示:

     

    但是,如果绑定源对象不是具有类型字符串的属性,而是具有类型 Color 的 Color 属性,该怎么办? 在这种情况下,为了使绑定正常工作,您需要先将 Color 属性值转换为 Background 属性所接受的值。您需要通过实现 IValueConverter 接口来创建一个自定义转换器,如下面的示例中所示:

    现在使用自定义转换器而不是默认转换,关系图如下所示:

    在这里重复一下,由于要绑定到的类型中提供了类型转换器,因而可以使用默认转换。此行为取决于目标中可用的类型转换器。如果无法确定,请创建您自己的转换器。

    下面提供了一些典型方案,在这些方案中,实现数据转换器是非常有意义的:

    数据应根据区域性以不同方式显示。例如,可能需要根据在特定区域性中使用的值或标准,来实现货币转换器或日历日期/时间转换器。

    使用的数据不一定会更改属性的文本值,但会更改其他某个值(如图像的源,或显示文本的颜色或样式)。在这种情况下,可以通过转换可能不合适的属性绑定(如将文本字段绑定到表单元格的 Background 属性)来使用转换器。

    将多个控件或控件的多个属性绑定到相同数据。在这种情况下,主绑定可能仅显示文本,而其他绑定则处理特定的显示问题,但仍使用同一绑定作为源信息。

    到目前为止,我们尚未讨论 MultiBinding(其目标属性具有绑定集合)。对于 MultiBinding,可以使用自定义 IMultiValueConverter 从绑定的值生成最终值。例如,可以从红色、蓝色和绿色的值来计算颜色,这些值可以是来自于相同或不同绑定源对象的值。

    绑定到集合

    绑定源对象可以视为其属性包含数据的单个对象,也可以视为通常组合在一起的多态对象的数据集合(如查询数据库的结果)。到目前为止,我们仅讨论了绑定到单个对象,但是绑定到数据集合是一个常见方案。例如,一个常见方案是使用 ItemsControl(如 ListBox、ListView 或 TreeView)来显示数据集合,如什么是数据绑定?一节中的应用程序所示。

    幸运的是,基本关系图仍然适用。如果要将 ItemsControl 绑定到集合,则关系图如下所示:

    正如此图中所示,若要将 ItemsControl 绑定到集合对象,应使用 ItemsSource 属性。可以将 ItemsSource 属性视为 ItemsControl 的内容。请注意,绑定是 OneWay,因为 ItemsSource 属性默认情况下支持 OneWay 绑定。

    如何实现集合
    可以枚举实现 IEnumerable 接口的任何集合。但是,若要设置动态绑定,以使集合中的插入或移除操作可以自动更新 UI,则该集合必须实现 INotifyCollectionChanged 接口。此接口公开一个事件,只要基础集合发生更改,都应该引发该事件。

    WPF 提供 ObservableCollection<(Of <(T>)>) 类,它是公开 INotifyCollectionChanged 接口的数据集合的内置实现。请注意,为了完全支持将数据值从源对象传送到目标,支持可绑定属性的集合中的每个对象还必须实现 INotifyPropertyChanged 接口。

    在实现自己的集合之前,请先考虑使用 ObservableCollection<(Of <(T>)>) 或一个现有的集合类,如 List<(Of <(T>)>)、Collection<(Of <(T>)>) 和 BindingList<(Of <(T>)>) 等。如果您有高级方案并且希望实现自己的集合,请考虑使用 IList,它提供可以按索引逐个访问的对象的非泛型集合,因而可提供最佳性能。

    集合视图
    一旦 ItemsControl 绑定到数据集合,您可能希望对数据进行排序、筛选或分组。若要执行此操作,可以使用集合视图,这是实现 ICollectionView 接口的类。

    什么是集合视图?
    可以将集合视图视为位于绑定源集合顶部的层,您可以通过它使用排序、筛选和分组查询来导航和显示源集合,所有这些操作都无需操作基础源集合本身。如果源集合实现了 INotifyCollectionChanged 接口,则 CollectionChanged 事件引发的更改将传播到视图。

    由于视图不会更改基础源集合,因此每个源集合可以有多个关联的视图。例如,您可以有 Task 对象的集合。通过使用视图,可以通过多种不同的方式来显示相同数据。例如,您可能希望在页面左侧显示按优先级排序的任务,而在页面右侧显示按区域分组的任务。

    如何创建视图
    创建和使用视图的一种方式是直接实例化视图对象并将其用作绑定源。例如,请考虑在什么是数据绑定?一节中演示的数据绑定演示应用程序。 该应用程序的实现方式是将 ListBox 绑定到数据集合上的视图,而不是直接绑定到数据集合。下面的示例摘自数据绑定演示应用程序。CollectionViewSource 类是 CollectionView 的可扩展应用程序标记语言 (XAML) 代理。在此特定示例中,视图的 Source 绑定到当前应用程序对象的 AuctionItems 集合(类型为 ObservableCollection<(Of <(T>)>))。

    资源 listingDataView 随后用作应用程序中元素(如 ListBox)的绑定源:

    若要为同一集合创建另一个视图,可以创建另一个 CollectionViewSource 实例并为其提供另一个 x:Key 名称。

    将视图用作绑定源并不是创建和使用集合视图的唯一方式。所有集合都具有一个默认集合视图。例如,对于所有实现 IEnumerable 的集合,CollectionView 都是默认视图对象。ListCollectionView 是实现 IList 的集合的默认视图对象,而 BindingListCollectionView 是用于那些实现 IBindingList 的集合的集合视图类。若要获取默认视图,请使用 GetDefaultView 方法。

    排序
    如前所述,视图可以将排序顺序应用于集合。如同在基础集合中一样,数据可能具有或不具有相关的固有顺序。通过集合上的视图,您可以根据您提供的比较条件来应用顺序或更改默认顺序。由于这是基于客户端的数据视图,因此一种常见情况是用户可能希望根据列对应的值,对多列表格数据进行排序。通过使用视图,可以应用这种用户驱动的排序,而无需对基础集合进行任何更改,甚至不必再次查询集合内容。

    下面的示例演示什么是数据绑定?一节中的应用程序 UI 的“Sort by category and date”(按类别和日期排序)CheckBox 的排序逻辑:

    筛选
    视图还可以将筛选器应用于集合。这意味着即使集合中可能存在一个项,此特定视图也仅用于显示整个集合的某个子集。可以根据条件在数据中进行筛选。例如,正如在什么是数据绑定? 一节中的应用程序的工作方式那样,“Show only bargains”(仅显示成交商品)CheckBox 包含了筛选出成交价为 25 美元或更高的项的逻辑。执行下面的代码可以在选中该 CheckBox 时将 ShowOnlyBargainsFilter 设置为 Filter 事件处理程序:

    ShowOnlyBargainsFilter 事件处理程序具有以下实现:

    如果直接使用一个 CollectionView 类而不是 CollectionViewSource,则应使用 Filter 属性来指定回调。

    分组
    视图支持分组功能,此功能使用户能够将集合视图中的集合分区为逻辑组。这些组可以是显式的,其中的用户提供组列表,也可以是隐式的,其中的组依据数据动态生成。

    下面的示例演示“Group by category”(按类别分组)CheckBox 的逻辑:

    当前记录指针
    视图还支持当前项的概念。可以在集合视图中的对象之间导航。在导航时,您是在移动记录指针,该指针可用于检索存在于集合中的特定位置的对象。

    请注意,当前记录指针的移动与应用于该集合的任何排序或筛选会相互产生某些影响。排序将当前记录指针保留在所选的最后一条记录上,但集合视图现在是围绕此指针重构的。(或许所选记录以前曾位于列表的开头,但现在所选记录可能在中间的某个位置。) 如果所选内容在筛选之后保留在视图中,则筛选操作会保留所选记录。否则,当前记录指针会设置到经过筛选的集合视图的第一条记录。

    主-从绑定方案
    当前项的概念不仅用于集合中项的导航,而且用于主-从绑定方案。再考虑一下什么是数据绑定?中的应用程序 UI。 在该应用程序中,ListBox 的所选内容决定了在 ContentControl 中显示的内容。换句话说,当选中一个 ListBox 项时,ContentControl 会显示选定项的详细信息。

    通过将两个或更多控件绑定到同一视图可以轻松地实现主-从方案。下面这个摘自数据绑定演示的示例演示什么是数据绑定?中的应用程序 UI 上看到的 ListBox 和 ContentControl 的标记:

    请注意,这两个控件都绑定到同一个源,即 listingDataView 静态资源(请参见如何创建视图一节中的此资源的定义)。可以这样做的原因是当单一实例对象(此示例中为 ContentControl)绑定到一个集合视图时,该对象会自动绑定到该视图的 CurrentItem。请注意,CollectionViewSource 对象会自动同步货币与所选内容。如果列表控件没有像示例中那样绑定到 CollectionViewSource 对象,则您需要将其 IsSynchronizedWithCurrentItem 属性设置为 true 以达到此目的。

    您可能已经注意到上面的示例使用了一个模板。实际上,如果不使用模板(由 ContentControl 显式使用的模板以及由 ListBox 隐式使用的模板),则数据不会按照我们希望的方式显示。现在,我们开始介绍下一节中的数据模板化。

    数据模板化
    如果不使用数据模板,则什么是数据绑定?一节中的应用程序 UI 将如下所示:

    如上一节中的示例所示,ListBox 控件和 ContentControl 都绑定到 AuctionItem 的整个集合对象(更具体地说,是绑定到集合对象上的视图)。如果没有关于如何显示数据集合的特定说明,则 ListBox 会显示基础集合中的每个对象的字符串表示形式,而 ContentControl 会显示绑定到的对象的字符串表示形式。

    若要解决该问题,应用程序应定义 DataTemplate。如上一节中的示例所示,ContentControl 显式使用 detailsProductListingTemplate DataTemplate。在显示集合中的 AuctionItem 对象时,ListBox 控件隐式使用下面的 DataTemplate:

      

    使用这两个 DataTemplate,生成的 UI 如什么是数据绑定?所示。 如屏幕快照所示,除了可以在控件中放置数据以外,使用 DataTemplate 还可以为数据定义引人注目的视觉效果。例如,上面的 DataTemplate 中使用了 DataTrigger,因而 SpecialFeatures 值为 HighLight 的 AuctionItem 会显示为带有橙色边框和一个星号。

    数据验证

    接受用户输入的大多数应用程序都需要具有验证逻辑,以确保用户输入了需要的信息。验证检查可以基于类型、范围、格式或其他应用程序特定的要求。本节讨论了数据验证在 WPF 中的工作方式。

    将验证规则与绑定关联
    使用 WPF 数据绑定模型可以将 ValidationRules 与 Binding 对象相关联。例如,下面是什么是数据绑定?一节中的 Add Product Listing(添加产品清单)“Start Price”(起始价格)TextBox 的 XAML:

    ValidationRules 属性采用 ValidationRule 对象的集合。ExceptionValidationRule 是内置的 ValidationRule,用于检查在绑定源属性的更新过程中引发的异常。在此特定示例中,绑定源属性为 StartPrice(属于整数类型),而目标属性为 TextBox.Text。当用户输入的值无法转换为整数时,将引发异常,这会导致将绑定标记为无效。

    也可以通过从 ValidationRule 类派生和实现 Validate 方法来创建自己的验证规则。下面的示例演示什么是数据绑定?一节中的 Add Product Listing(添加产品清单)“Start Date”(起始日期)TextBox 使用的规则:

    StartDateEntryForm TextBox 使用此 FutureDateRule,如下面的示例中所示:

    请注意,由于 UpdateSourceTrigger 值为 PropertyChanged,因此在每次键击时,绑定引擎都会更新源值,这意味着该引擎还会在每次键击时检查 ValidationRules 集合中的每条规则。我们会在“验证过程”一节中进行更深入的讨论。

    提供视觉反馈
    如果用户输入的值无效,则您可能希望在应用程序 UI 上提供一些有关错误的反馈。提供这种反馈的一种方式是将 Validation..::.ErrorTemplate 附加属性设置为自定义 ControlTemplate。如上一小节中所示,StartDateEntryForm TextBox 使用一个名为 validationTemplate 的 ErrorTemplate。下面的示例显示 validationTemplate 的定义:

    AdornedElementPlaceholder 元素指定要装饰的控件应放置的位置。

    此外,您可能还要使用 ToolTip 显示错误消息。StartDateEntryForm 和 StartPriceEntryForm TextBox 都使用样式 textStyleTextBox,该样式创建一个显示错误消息的 ToolTip。下面的示例显示 textStyleTextBox 的定义。当绑定元素的属性的一个或多个绑定发生错误时,附加属性 Validation.HasError 为 true。

    使用自定义 ErrorTemplate 和 ToolTip,StartDateEntryForm TextBox 在发生验证错误时会如下所示:

    如果您的 Binding 具有关联验证规则,但是您未在绑定控件上指定 ErrorTemplate,则在出现验证错误时会使用默认 ErrorTemplate 来通知用户。默认 ErrorTemplate 是一个在装饰器层中定义红色边框的控件模板。使用默认 ErrorTemplate 和 ToolTip,StartPriceEntryForm TextBox 的 UI 在发生验证错误时会如下所示:

     

    有关如何提供逻辑以验证对话框中的所有控件的示例,请参见对话框概述中的“自定义对话框”一节。

    验证过程
    由于数据验证与从目标到源的更新有关,因此它仅应用于 TwoWay 和 OneWayToSource 绑定。每次将输入值传输到绑定源属性时会发生验证。在这里重复一下,导致源更新的原因取决于 UpdateSourceTrigger 属性的值,如触发源更新的原因一节中所述。

    下面的插图提供了在数据绑定过程中适合进行验证的位置的可视表示形式:

    如上图所示,验证在调用转换器之前从目标到源的值传输过程中发生。下面将介绍验证 过程,如上面的关系图中所标记的那样:

    在将值从目标属性传输到源属性时,数据绑定引擎首先移除可能已添加到绑定元素的 Validation.Errors 附加属性的任何 ValidationError。然后,数据绑定引擎检查是否为该 Binding 定义了自定义 ValidationRule;如果已经定义,则它将调用每个 ValidationRule 上的 Validate 方法,直到其中一个规则出错或者全部规则都通过为止。

    如果某个自定义规则未通过,则绑定引擎会创建一个 ValidationError 对象,并将该对象添加到绑定元素的 Validation.Errors 集合。如果 Validation.Errors 不为空,则元素的 Validation.HasError 附加属性会设置为 true。此外,如果 Binding 的 NotifyOnValidationError 属性设置为 true,则绑定引擎将引发该元素上的 Validation.Error 附加事件。

    如果所有规则都通过,则绑定引擎会调用转换器(如果存在)。

    如果转换器通过,则绑定引擎会调用源属性的 setter。

    如果绑定具有与其关联的 ExceptionValidationRule,并且在步骤 4 中引发异常,则绑定引擎将检查是否存在 UpdateSourceExceptionFilter。您可以选择使用 UpdateSourceExceptionFilter 回调来提供用于处理异常的自定义处理程序。如果未对 Binding 指定 UpdateSourceExceptionFilter,则绑定引擎将对异常创建 ValidationError 并将其添加到绑定元素的 Validation.Errors 集合。

    还应注意,任何方向(目标到源或源到目标)的有效值传输操作都将清除 Validation.Errors 附加属性。

    调试机制
    可以在绑定相关对象上设置附加属性 PresentationTraceSources..::.TraceLevel 以接收有关特定绑定的状态的信息。

    1. WPF数据绑定之绑定源

    在数据绑定中,绑定源(源)对象指的是您从其获取数据的对象。本主题讨论可以用作源的对象类型。

    Windows Presentation Foundation (WPF) 数据绑定支持下列类型的绑定源:

      

    将 CLR 类用作绑定源对象
    本节讨论您在实现 CLR 类以用作源对象时需要了解的内容。

    提供更改通知
    如果您要使用 OneWay 或 TwoWay 绑定(您希望 UI 在源属性发生动态变化时进行更新),则必须实现一个适当的“属性已更改”通知机制。建议的机制是使 CLR 类实现 INotifyPropertyChanged 接口。

    如果您不实现 INotifyPropertyChanged,则必须安排您自己的通知系统,确保绑定中使用的数据是最新的。您可以通过支持需要更改通知的每个属性的 PropertyChanged 模式来提供更改通知。若要支持此模式,请定义每个属性的属性名称 Changed 事件,其中属性名称 是属性的名称。每次更改属性时都会引发此事件。

    如果源对象实现了正确的通知机制,则会自动进行目标更新。如果源对象由于某种原因未提供正确的属性更改通知,那么您可以选择使用 UpdateTarget 方法来显式更新目标属性。

    其他特征
    下表提供了需要注意的其他几个要点:

    如果您要在 XAML 中创建对象,则类必须使用默认构造函数。在某些 .NET 语言(例如 C#)中,可能已为您创建默认构造函数。

    用作绑定源属性的属性必须是类的公共属性。不能出于绑定目的来访问显式定义的接口属性,也不能访问没有基实现的受保护的私有或虚拟属性。

    您不能绑定到 CLR 类的公共字段。

    在您的类中声明的属性类型是传递到绑定的类型。但是,绑定最终使用的类型取决于绑定目标属性的类型,而不是源属性的类型。如果类型不同,您可能需要编写一个转换器来处理最初将您的自定义属性传递到绑定的方式。

    将整个对象用作绑定源
    可以将整个对象用作绑定源。要实现此目的,可以使用 Source 或 DataContext 属性将对象指定为绑定源,然后提供一个不含路径的空白绑定声明:{Binding}。这种方法可发挥较大作用的情况包括:要绑定到属于类型字符串的对象;要绑定到具有您关注的多个属性的对象;要绑定到集合对象。

    请注意,您可能需要应用自定义逻辑,使数据对您的绑定目标属性有意义。自定义逻辑的形式可以是自定义转换器(如果默认类型转换不存在)或者 DataTemplate。

    将集合对象用作绑定源
    通常,您要用作源的对象是包含多个自定义对象的集合,其中的每个自定义对象都代表一个数据对象,该数据对象用作重复绑定的一个实例的源。例如,您有一个 CustomerOrders 集合,它由 CustomerOrder 对象组成,您的应用程序可以循环访问该集合以确定存在多少订单以及每个订单包含的数据。

    可以枚举实现 IEnumerable 接口的任何集合。但是,若要设置动态绑定,以便集合中的插入或删除操作可以自动更新 UI,则该集合必须实现 INotifyCollectionChanged 接口。此接口公开一个事件,只要基础集合发生更改,就必须引发该事件。

    WPF 提供 ObservableCollection<(Of <(T>)>) 类,它是公开 INotifyCollectionChanged 接口的数据集合的内置实现。该集合中的各个数据对象必须满足前面各节中描述的要求。有关示例,请参见如何:创建和绑定到 ObservableCollection。在实现您自己的集合之前,请考虑使用 ObservableCollection<(Of <(T>)>) 或使用现有集合类之一,如 List<(Of <(T>)>)、Collection<(Of <(T>)>) 和 BindingList<(Of <(T>)>) 等。

    如果您有高级方案并且希望实现自己的集合,请考虑使用 IList,它提供可以按索引逐个访问的对象的非泛型集合,因而可提供最佳性能。

    权限要求
    下表总结了在完全信任或部分信任级别下执行的应用程序中可以绑定到的属性类型:

     
    下表描述了数据绑定中有关权限要求的要点:

    对于 CLR 属性,只要绑定引擎能够使用反射访问源属性,数据绑定就能进行。否则,绑定引擎会发出找不到属性的警告,并使用回退值或默认值(如果可用)。

    始终可以绑定到依赖项属性。

    XML 绑定的权限要求与此类似:在部分信任沙盒中,如果 XmlDataProvider 无权访问给定的数据,则它将失败。

    1. WPF数据绑定之数据模板

    WPF 数据模板模型为定义数据的表示提供了很大的灵活性。WPF 控件具有支持自定义数据表示的内置功能。本主题首先演示如何定义 DataTemplate,然后介绍其他数据模板功能,例如根据自定义逻辑选择模板和支持显示分层数据。

    先决条件
    本主题重点介绍数据模板功能,不介绍数据绑定概念。有关基本数据绑定概念的信息,请参见数据绑定概述。

    DataTemplate 用于数据表示,是 WPF 样式和模板模型提供的许多功能中的一种。

    另外,了解Resources也很重要,它实际上是有关使对象(例如,Style 和 DataTemplate)成为可重用对象的内容。

    数据模板基础

    为了说明 DataTemplate 为什么这么重要,让我们演示一个数据绑定的示例。在本示例中,有一个绑定到 Task 对象列表的 ListBox。每个 Task 对象都有 TaskName (string)、Description (string)、Priority (int) 和类型 TaskType 的属性,它是一个 Enum,其值为 Home 和 Work。

    没有 DataTemplate
    如果没有 DataTemplate,我们的 ListBox 当前具有如下外观:

     
    由于没有任何特定说明,ListBox 在尝试显示集合中的对象时,默认情况下会调用 ToString。因此,如果 Task 对象重写 ToString 方法,则 ListBox 显示基础集合中每个源对象的字符串表示形式。

    例如,如果 Task 类以这种方式重写 ToString 方法,其中 name 是 TaskName 属性的字段:

    则 ListBox 如下所示:

     
    但是,这是受到限制的,并不灵活。另外,如果您要绑定到 XML 数据,您不能重写 ToString。

    定义简单 DataTemplate
    该解决方案定义 DataTemplate。这样做的一种方式是将 ListBox 的 ItemTemplate 属性设置为 DataTemplate。DataTemplate 中指定的内容变成数据对象的可视结构。以下 DataTemplate 相当简单。我们要给出的说明是:每项显示为 StackPanel 中的三个 TextBlock 元素。每个 TextBlock 元素绑定到 Task 类的一个属性。

    本主题中的示例的基础数据是 CLR 对象的一个集合。如果您要绑定到 XML 数据,基本概念相同,但是语法稍微不同。例如,不是让 Path=TaskName,而是将 XPath 设置为 @TaskName(如果 TaskName 是 XML 节点的属性)。

    现在 ListBox 如下所示:

    将 DataTemplate 创建为资源
    在上面的示例中,我们定义了 DataTemplate 内联。更为常见的是在资源部分中定义它,以使其成为一个可重用的对象,如下面的示例所示:

    现在,您可以将 myTaskTemplate 用作资源,如下面的示例所示:

    因为 myTaskTemplate 是资源,所以您现在可以在具有采用 DataTemplate 类型的属性的其他控件中使用它。如上所述,对于 ItemsControl 对象(例如 ListBox),它是 ItemTemplate 属性。对于 ContentControl 对象,它是 ContentTemplate 属性。

    DataType 属性
    DataTemplate 类具有 DataType 属性,该属性非常类似于 Style 类的 TargetType 属性。因此,在上述示例中不需要为 DataTemplate 指定 x:Key,您可以执行以下操作:

    此 DataTemplate 自动应用于所有 Task 对象。请注意,在这种情况下,x:Key 是隐式设置的。因此,如果要为此 DataTemplate 分配一个 x:Key 值,则要重写隐式 x:Key 并且不会自动应用 DataTemplate。

    如果要将 ContentControl 绑定到 Task 对象的集合,则 ContentControl 不会自动使用以上 DataTemplate。这是因为在 ContentControl 上绑定需要更多的信息来区分是要绑定到整个集合还是要绑定到单个对象。如果 ContentControl 要跟踪对 ItemsControl 类型的选择,您可以将 ContentControl 绑定的 Path 属性设置为“/”以表示您对当前项感兴趣。有关示例,请参见如何:绑定到集合并基于选择显示信息。否则,需要通过设置 ContentTemplate 属性显式指定 DataTemplate。

    DataType 属性在您拥有不同类型数据对象的 CompositeCollection 时尤其有用。
    向 DataTemplate 添加更多信息
    当前,数据显示了必要的信息,但是还可以显示更多信息。让我们通过添加 Border、Grid 和一些用于描述要显示的数据的 TextBlock 来显示更多信息。

    下面的屏幕快照使用此已修改的 DataTemplate 来显示 ListBox。

    我们可以在 ListBox 上将 HorizontalContentAlignment 设置为 Stretch 来确保项的宽度占据整个空间:

    HorizontalContentAlignment 属性设置为 Stretch 后,ListBox 现在如下所示:

    将 DataTemplate 创建为资源
    在上面的示例中,我们定义了 DataTemplate 内联。更为常见的是在资源部分中定义它,以使其成为一个可重用的对象,如下面的示例所示:

    现在,您可以将 myTaskTemplate 用作资源,如下面的示例所示:

    因为 myTaskTemplate 是资源,所以您现在可以在具有采用 DataTemplate 类型的属性的其他控件中使用它。如上所述,对于 ItemsControl 对象(例如 ListBox),它是 ItemTemplate 属性。对于 ContentControl 对象,它是 ContentTemplate 属性。

    DataType 属性
    DataTemplate 类具有 DataType 属性,该属性非常类似于 Style 类的 TargetType 属性。因此,在上述示例中不需要为 DataTemplate 指定 x:Key,您可以执行以下操作:

    此 DataTemplate 自动应用于所有 Task 对象。请注意,在这种情况下,x:Key 是隐式设置的。因此,如果要为此 DataTemplate 分配一个 x:Key 值,则要重写隐式 x:Key 并且不会自动应用 DataTemplate。

    如果要将 ContentControl 绑定到 Task 对象的集合,则 ContentControl 不会自动使用以上 DataTemplate。这是因为在 ContentControl 上绑定需要更多的信息来区分是要绑定到整个集合还是要绑定到单个对象。如果 ContentControl 要跟踪对 ItemsControl 类型的选择,您可以将 ContentControl 绑定的 Path 属性设置为“/”以表示您对当前项感兴趣。有关示例,请参见如何:绑定到集合并基于选择显示信息。否则,需要通过设置 ContentTemplate 属性显式指定 DataTemplate。

    DataType 属性在您拥有不同类型数据对象的 CompositeCollection 时尤其有用。
    向 DataTemplate 添加更多信息
    当前,数据显示了必要的信息,但是还可以显示更多信息。让我们通过添加 Border、Grid 和一些用于描述要显示的数据的 TextBlock 来显示更多信息。

    下面的屏幕快照使用此已修改的 DataTemplate 来显示 ListBox。

    我们可以在 ListBox 上将 HorizontalContentAlignment 设置为 Stretch 来确保项的宽度占据整个空间:

    HorizontalContentAlignment 属性设置为 Stretch 后,ListBox 现在如下所示:

    DataTemplate 中有哪些内容?
    在前面的示例中,我们使用 DataTemplate.Triggers 属性将触发器放入 DataTemplate 中。触发器的 Setter 设置 DataTemplate 中元素(Border 元素)的属性值。但是,如果您的 Setters 相关属性不是当前 DataTemplate 中元素的属性,则使用 Style(用于 ListBoxItem 类)设置属性更合适(如果您要绑定的控件是 ListBox)。例如,如果您想要在鼠标指向某一项时让您的 Trigger 对该项的 Opacity 值进行动画处理,则需要在 ListBoxItem 样式中定义触发器。

    通常需要注意:DataTemplate 会应用于每个生成的 ListBoxItem(有关它实际应用的方式和场合的更多信息,请参见 ItemTemplate 页)。DataTemplate 仅与数据对象的表示和外观有关。在大多数情况下,所有其他表示方面(例如,项在被选中时的外观或 ListBox 排列项的方式)不在 DataTemplate 定义范围内。

    根据数据对象的属性选择 DataTemplate
    在 DataType 属性一节中,我们讨论了您可以针对不同的数据对象定义不同的数据模板。这在您拥有不同类型的 CompositeCollection 或不同类型的项集合时尤其有用。在使用 DataTrigger 来应用属性值一节中,我们演示了如果您拥有相同类型的数据对象集合,您可以创建 DataTemplate,然后根据每个数据对象的属性值使用触发器来应用更改。虽然触发器允许您应用属性值或启动动画,但是它们无法让您灵活重构数据对象的结构。在某些情况下,可能需要您为类型相同但属性不同的数据对象创建其他 DataTemplate。

    例如,当 Task 对象的 Priority 值为 1 时,您可能需要为它指定完全不同的外观,以给予您自己一个提醒。在这种情况下,您需要创建 DataTemplate 来显示高优先级的 Task 对象。让我们将以下 DataTemplate 添加到资源部分:

    请注意,此示例使用 DataTemplate.Resources 属性。DataTemplate 中的元素共享该部分中定义的资源。

    若要提供逻辑以根据数据对象的 Priority 值选择要使用的 DataTemplate,需要创建 DataTemplateSelector 的子类并重写 SelectTemplate 方法。在下面的示例中,SelectTemplate 方法提供逻辑以根据 Priority 属性的值返回适当的模板。可以在封装 Window 元素的资源中找到要返回的模板。

    然后,我们可以将 TaskListDataTemplateSelector 声明为资源:

    若要使用模板选择器资源,请将其分配到 ListBox 的 ItemTemplateSelector 属性。ListBox 为基础集合中的每一项调用 TaskListDataTemplateSelector 的 SelectTemplate 方法。该调用会将数据对象作为项参数来传递。然后,将由该方法返回的 DataTemplate 应用于该数据对象。

    使用模板选择器后,ListBox 现在如下所示:

     

    这正是此示例要得到的结果。

     对 ItemsControl 进行样式和模板处理
    即使 ItemsControl 不是 DataTemplate 所用于的唯一控件类型,将 ItemsControl 绑定到集合仍然很常见。在 DataTemplate 中有哪些内容一节中,我们讨论了您的 DataTemplate 定义应当仅与数据表示相关。为了明确何时不适合使用 DataTemplate,有必要了解 ItemsControl 提供的不同样式和模板属性。下面的示例旨在演示这些属性中每一个属性的功能。本示例中的 ItemsControl 绑定到与前面示例中的 Tasks 集合。为便于演示,本示例中的样式和模板都进行了内联声明。

    下面是该示例在呈现时的屏幕快照:

     
    请注意,您可以使用 ItemTemplateSelector,而不是 ItemTemplate。请参考上一节的示例。同样,可以选择使用 ItemContainerStyleSelector,而不是 ItemContainerStyle。

    这里未显示 ItemsControl 的其他两个与样式相关的属性,它们是 GroupStyle 和 GroupStyleSelector。

    对分层数据的支持
    到目前为止,我们仅讨论如何绑定和显示单个集合。某些时候,您要绑定的集合包含其他集合。HierarchicalDataTemplate 类专用于 HeaderedItemsControl 类型以显示这样的数据。在下面的示例中,ListLeagueList 是 League 对象的列表。每个 League 对象都有一个 Name 和 Division 对象的集合。每个 Division 都有一个 Name 和 Team 对象的集合,并且每个 Team 对象都有一个 Name。

    该示例演示通过使用 HierarchicalDataTemplate,您可以轻松地显示包含其他列表的列表数据。下面是该示例的一个屏幕快照。

    1. WPF数据绑定之绑定声明

    本主题讨论声明绑定的不同方法。

    先决条件
    在阅读本主题之前,应当先熟悉标记扩展的概念和使用,这一点非常重要。

    本主题不介绍数据绑定的概念。

    在 XAML 中声明绑定
    本节讨论如何在可扩展应用程序标记语言 (XAML) 中声明绑定。

    标记扩展使用
    Binding 是标记扩展。当您使用绑定扩展来声明绑定时,声明包含一系列子句,这些子句跟在 Binding 关键字后面,并由逗号 (,) 分隔。绑定声明中的子句可以按任意顺序排列,因此有许多可能的组合。子句是名称=值 对,其中名称 是 Binding 属性,值 是您要为该属性设置的值。

    当在标记中创建绑定声明字符串时,必须将它们附加到目标对象的特定依赖项属性。下面的示例演示如何通过使用绑定扩展并指定 Source、Path 和 UpdateSourceTrigger 属性来绑定 TextBox.Text 属性。

    您可以通过这种方法来指定 Binding 类的大部分属性。

    对象元素语法
    对象元素语法是创建绑定声明的另一种方法。在大多数情况下,使用标记扩展或对象元素语法没有特定的优势。但是,在标记扩展不支持您的方案的情况下,例如,当您的属性值是不存在类型转换的非字符串类型时,您将需要使用对象元素语法。

    下面是对象元素语法和标记扩展使用的一个示例:

    此示例通过使用扩展语法声明绑定来绑定 Foreground 属性。Text 属性的绑定声明使用对象元素语法。

    MultiBinding 和 PriorityBinding
    MultiBinding 和 PriorityBinding 不支持 XAML 扩展语法。因此,如果您要在 XAML 中声明 MultiBinding 或 PriorityBinding,则必须使用对象元素语法。

    在代码中创建绑定
    指定绑定的另一种方法是在代码中直接为 Binding 对象设置属性。下面的示例演示如何在代码中创建 Binding 对象并指定属性:

    如果您要绑定的对象是 FrameworkElement 或 FrameworkContentElement,则您可以直接对该对象调用 SetBinding 方法,而不是使用 BindingOperations..::.SetBinding。

    绑定路径语法
    使用 Path 属性可以指定您要绑定到的源值:

    在最简单的情况下,Path 属性值是要用于绑定的源对象的属性名,如 Path=PropertyName。

    在 C# 中可以通过类似语法指定属性的子属性。例如,子句 Path=ShoppingCart.Order 设置与对象或属性 ShoppingCart 的 Order 子属性的绑定。

    若要绑定到附加属性,应在附加属性周围放置圆括号。例如,若要绑定到附加属性 DockPanel..::.Dock,则语法是 Path=(DockPanel.Dock)。

    可以在要应用索引器的属性名后面的方括号内指定属性的索引器。例如,子句 Path=ShoppingCart[0] 将绑定设置为与属性的内部索引处理文本字符串“0”的方式对应的索引。还支持嵌套的索引器。

    可以在 Path 子句中混合索引器和子属性;例如,Path=ShoppingCart.ShippingInfo[MailingAddress,Street].

    在索引器内部,您可以有多个由逗号 (,) 分隔的索引器参数。可以使用圆括号指定每个参数的类型。例如,您可以有 Path="[(sys:Int32)42,(sys:Int32)24]",其中 sys 映射到 System 命名空间。

    转义机制
    在索引器 ([ ]) 内部,插入符号 (^) 用于对下一个字符进行转义。

    如果您在 XAML 中设置 Path,则还需要使用 XML 实体对 XML 分析程序专用的某些字符进行转义:

    使用 & 对字符“&”进行转义。

    使用 > 对结束标记“>”进行转义。

    此外,如果您使用标记扩展语法描述属性中的整个绑定,则需要使用反斜杠 \ 对 WPF 标记扩展分析程序专用的字符进行转义:

    反斜杠 \ 本身是转义字符。

    等号 (=) 将属性名与属性值隔开。

    逗号 (,) 用于分隔属性。

    右大括号 (}) 是标记扩展的结尾。

    默认行为
    如果未在声明中指定默认行为,则默认行为如下。

    创建一个尝试在绑定源值与绑定目标值之间执行类型转换的默认转换器。如果无法进行转换,则默认转换器会返回 null。

    如果您不设置 ConverterCulture,则绑定引擎会使用绑定目标对象的 Language 属性。在 XAML 中,此属性默认为“en-US”,或者从页面的根元素(或任何元素)继承值(如果已显式设置了一个值)。

    只要绑定已有数据上下文(例如,来自父元素的继承数据上下文),并且该上下文所返回的项或集合适合于绑定,而不需要进一步的路径修改,则绑定声明可以不必有任何子句:{Binding}。在绑定作用于集合的情况下,这通常是为数据样式指定绑定的方式。

    默认 Mode 可能是单向,也可能是双向,具体取决于所绑定的依赖项属性。您始终可以显式声明绑定模式,以确保绑定具有所需的行为。通常,用户可编辑的控件属性(如 TextBox..::.Text 和 RangeBase..::.Value)默认为双向绑定,而其他大多数属性默认为单向绑定。

    默认 UpdateSourceTrigger 值可能是 PropertyChanged,也可能是 LostFocus,具体也取决于所绑定的依赖项属性。多数依赖项属性的默认值都为 PropertyChanged,而 TextBox..::.Text 属性的默认值为 LostFocus。

    1. 实例一:绑定到ADO.NET数据源

    本示例演示如何将 Windows Presentation Foundation (WPF) ListBox 控件绑定到 ADO.NET DataSet。

    示例
    在本示例中,OleDbConnection 对象用于连接到数据源,该数据源是在连接字符串中指定的 Access MDB 文件。建立连接后,会创建一个 OleDbDataAdpater 对象。OleDbDataAdpater 对象执行一个 select 结构化查询语言 (SQL) 语句,以便从数据库中检索记录集。通过调用 OleDbDataAdapter 的 Fill 方法,此 SQL 命令的结果存储在 DataSet 的 DataTable 中。本示例中,DataTable 命名为 BookTable。然后,此示例将 ListBox 的 DataContext 属性设置为 DataSet 对象。

    然后,我们将 ListBox 的 ItemsSource 属性绑定到 DataSet 的 BookTable:

    BookItemTemplate 是定义数据显示形式的 DataTemplate:

    IntColorConverter 将 int 转换为颜色。利用此转换器,如果 NumPages 的值小于 350,则第三个 TextBlock 的 Background 颜色为绿色,否则为红色。此处未显示此转换器的实现。

    1. 实例二:绑定到LINQ查询的结果

    本示例演示如何运行 LINQ 查询然后绑定到查询结果。

    示例
    下面的示例创建两个列表框。第一个列表框包含三个列表项。

    在第一个列表框中选择一项会激发下面的事件处理程序。在本示例中,Tasks 是 Task 对象的集合。Task 类具有名为 Priority 的属性。此事件处理程序运行一个 LINQ 查询,该查询返回具有选定优先级值的 Task 对象的集合,然后将其设置为 DataContext:

    第二个列表框绑定到该集合,因为该列表框的 ItemsSource 值设置为 {Binding}。因此,该列表框显示返回的集合(基于 myTaskTemplate DataTemplate)。

    WPF图形和多媒体开发

    1. WPF 图形动画和媒体概述

    本主题介绍 Windows Presentation Foundation (WPF) 的图形、动画和媒体功能,使用这些功能,可以向应用程序添加图形、过渡效果、声音和视频。

    WPF 提供高级绘图和动画功能,以前只能从专用库(具体来说,就是 Microsoft Windows 图形设备接口 (GDI) 和 Microsoft Windows GDI+)使用这些功能。现在,WPF 提供对多媒体、向量图形、动画和内容撰写的集成支持,使得开发人员可以轻松地生成悦目的用户界面和内容。使用 Microsoft Visual Studio .NET 或者甚至文本编辑器(如 Microsoft 记事本),可以创建矢量图形或复杂的动画并将媒体集成到应用程序中。

    WPF 在图形和多媒体方面的新增功能
    WPF 向 Windows 开发人员引进了新的图形功能,这些功能具有如下优点:

    与分辨率和设备无关的图形。WPF 图形系统使用与设备无关的单元来支持分辨率和设备独立性。每个与设备无关的像素都会随系统上的每英寸点数设置自动缩放。

    更高的精度。WPF 坐标系使用双精度值,而不使用浮点值。转换值和不透明度值也是使用双精度值来表示。WPF 还支持更广泛的颜色域 (scRGB),并为管理来自不同颜色空间的输入提供了集成的支持。

    高级图形和动画支持。WPF 通过为您管理场景图简化了图形编程;您无需再担心场景处理、呈现循环和双线性内插算法。WPF 提供了命中测试支持、集成的动画系统和全面的字母合成支持。

    硬件加速。WPF 图形系统旨在利用图形硬件来最小化 CPU 使用率。

    二维形状
    WPF 提供了一个库,包含用矢量绘制的常用 二维 形状,如下图中演示的矩形和椭圆。


    这些内部的 WPF 形状不仅仅是形状:它们是可编程的元素,能够实现可通过最常见的控件(包括键盘输入和鼠标输入)实现的许多功能。

    下面的插图显示前面 XAML 标记和代码隐藏的输出。

    二维几何图形
    当 WPF 提供的 二维 形状不足时,可以使用 WPF 中的几何图形和路径支持来创建自己的形状。下面的插图显示如何使用几何图形来创建形状、如何将几何图形用作绘图画笔以及如何使用几何图形来剪裁其他 WPF 元素。

     

    二维效果
    WPF 提供了一个包含 二维 类的库,可用来创建各种效果。使用 WPF 的 二维 呈现功能,可以绘制具有渐变、位图、绘图和视频的 UI 元素,并借助于旋转、缩放和扭曲功能来操作这些元素。下图举例说明通过 WPF 画笔可获得的多种效果。

    三维呈现
    WPF 提供了一组三维呈现功能,这些功能可与 WPF 中的二维图形支持功能集成,以便您创建更加令人惊喜的布局、UI 和数据可视化效果。在色谱的一端,WPF 允许您将 二维 图像呈现到 三维 形状的一个图面上,如下图中所示。

    动画
    使用动画,可以使控件和元素变大、晃动、旋转和淡化,还可以产生有趣的页面过渡和更多效果。由于 WPF 允许您对大多数属性进行动画处理,因此,您不但可以对大多数 WPF 对象进行动画处理,而且还可以使用 WPF 来对您创建的自定义对象进行动画处理。

    媒体
    图像、视频和音频是用来传达信息和用户体验的富媒体方法。

    图像
    图像(包括图标、背景甚至动画的一部分)是大多数应用程序的核心部分。由于您经常需要使用图像,因此 WPF 提供了以各种方式处理图像的功能。下图只说明了其中的一种方法。

    视频和音频
    WPF 的一个核心图形功能就是为处理多媒体(包括视频和音频)提供本机支持。下面的示例说明如何在应用程序中插入媒体播放器。

    既能够播放视频又能够播放音频,而且具有很好的可扩展性,可以用来方便地创建自定义的 UI。

    1. WPF的图形呈现

    本主题概述 WPF 可视化层。本主题重点讲述 WPF 模型中呈现支持的 Visual 类的角色。

    Visual 对象的角色
    Visual 类是每个 FrameworkElement 对象所派生自的基本抽象。该类还充当在 WPF 中编写新控件的入口点。在 Win32 应用程序模型中,该类在许多方面可以被视为窗口句柄 (HWND)。

    Visual 对象是一个核心 WPF 对象,它的主要角色是提供呈现支持。用户界面控件(如 Button 和 TextBox)派生自 Visual 类,并使用该类来保持它们所呈现的数据。Visual 对象为下列功能提供支持:

    输出显示:呈现 Visual 对象的持久的序列化绘图内容。

    转换:针对 Visual 对象执行转换。

    剪辑:为 Visual 对象提供剪辑区域支持。

    命中测试:确定 Visual 对象的边界内是否包含坐标或几何形状。

    边界框计算:确定 Visual 对象的边框。

    但是,Visual 对象不包括对非呈现功能的支持,如:
    ◆事件处理

    ◆布局

    ◆样式

    ◆数据绑定

    ◆全球化

    Visual 作为子类必须派生自的公共抽象类进行公开。下图显示了 WPF 中所公开的可视化对象的层次结构。

    Visual 类的层次结构

     

    DrawingVisual
    DrawingVisual 是一个用于呈现形状、图像或文本的轻量绘图类。此类之所以被视为轻量,是因为它不提供布局或事件处理功能,从而能够改善运行时性能。因此,绘图最适于背景和剪贴画。DrawingVisual 可用于创建自定义可视化对象。

    Viewport3DVisual
    Viewport3DVisual 在二维 Visual 和 Visual3D 对象之间起到桥梁作用。Visual3D 类是所有三维可视化元素的基类。Viewport3DVisual 要求您定义一个 Camera 值和一个 Viewport 值。可以借助照相机来查看场景。投影映射到二维图面的区域称作视区。

    ContainerVisual
    ContainerVisual 类用作 Visual 对象集的容器。DrawingVisual 类派生自 ContainerVisual 类,这允许它包含可视化对象的集合。

    可视化对象中的绘图内容
    Visual 对象将它的呈现数据另存为向量图形指令列表。指令列表中的每一项都以序列化格式表示一组低级别的图形数据及其相关资源。共有四种不同类型的呈现数据可以包含绘图内容。

    通过 DrawingContext,您可用可视化内容填充 Visual。当您使用 DrawingContext 对象的绘图命令时,实际上是存储一组日后将由图形系统使用的呈现数据,而不是实时绘制到屏幕上。

    当您创建 WPF 控件(如 Button)时,该控件会为绘图对象本身隐式生成呈现数据。例如,设置 Button 的 Content 属性会导致该控件存储标志符号的呈现表示。

    Visual 将其内容描述为一个或多个包含在 DrawingGroup 中的 Drawing 对象。DrawingGroup 还描述不透明蒙板、转换、位图效果和应用于其内容的其他操作。呈现内容时,DrawingGroup 操作按如下顺序应用:OpacityMask、Opacity、BitmapEffect、ClipGeometry、GuidelineSet 和 Transform。

    下图显示了在呈现过程中 DrawingGroup 操作的应用顺序。

    在可视化层绘制内容
    绝不能直接实例化 DrawingContext;但可以通过某些方法(例如 DrawingGroup..::.Open 和 DrawingVisual..::.RenderOpen)获取绘图上下文。下面的示例从 DrawingVisual 中检索 DrawingContext 并将其用于绘制矩形。

    在可视化层枚举绘图内容
    此外,Drawing 对象还可提供用来枚举 Visual 内容的对象模型。

    说明:
    您在枚举可视化层的内容时,就是相当于在检索 Drawing 对象,而不是以向量图形指令列表形式检索呈现数据的基础表示。

    下面的示例使用 GetDrawing 方法来检索 Visual 的 DrawingGroup 值并枚举该值。

    如何使用可视化对象来生成控件
    WPF 中的许多对象都由其他可视化对象组成,这意味着它们可以包含子代对象的各种层次结构。WPF 中的许多用户界面元素(如控件)都由多个表示不同类型呈现元素的可视化对象组成。例如,Button 控件可以包含许多其他对象,其中包括 ClassicBorderDecorator、ContentPresenter 和 TextBlock。

    下面的代码显示的是在标记中定义的 Button 控件。

    如果您要枚举包含默认 Button 控件的可视化对象,则将发现如下所示的可视化对象层次结构:

    可视化树层次结构的关系图


    Button 控件包含一个 ClassicBorderDecorator 元素,该元素又包含一个 ContentPresenter 元素。ClassicBorderDecorator 元素负责为 Button 绘制边框和背景。ContentPresenter 元素负责显示 Button 的内容。在本例中,由于您要显示文本,因此 ContentPresenter 元素中包含一个 TextBlock 元素。Button 控件使用 ContentPresenter,这意味着该控件的内容可以由其他元素(如 Image)或几何形状(如 EllipseGeometry)来表示。

    控件模板
    将控件扩展为控件层次结构的关键在于 ControlTemplate。控件模板为控件指定默认的可视化层次结构。当您显式引用某个控件时,会隐式引用它的可视化层次结构。您可以重写控件模板的默认值,以便为控件创建自定义的可视化外观。例如,您可以修改 Button 控件的背景颜色值,以便它使用线性渐变颜色值,而不使用纯色值。

    用户界面元素(如 Button 控件)包含几个向量图形指令列表,这些列表描述控件的全部呈现定义。下面的代码显示的是在标记中定义的 Button 控件。

    如果您要枚举包含 Button 控件的可视化对象和向量图形指令列表,则将发现如下所示的可视化对象层次结构:

    可视化树和呈现数据的关系图


    Button 控件包含一个 ClassicBorderDecorator 元素,该元素又包含一个 ContentPresenter 元素。ClassicBorderDecorator 元素负责绘制所有构成按钮边框和背景的离散图形元素。ContentPresenter 元素负责显示 Button 的内容。在本例中,由于您要显示图像,因此 ContentPresenter 元素中包含一个 Image 元素。

    对于可视化对象和向量图形指令列表的层次结构,需要注意多个事项:

    该层次结构中的排序表示绘图信息的呈现顺序。从可视化元素的根,按照从左到右、从上到下的顺序遍历子元素。如果某个元素有可视化子元素,则会先遍历该元素的子元素,然后再遍历该元素的同级。

    层次结构中的非叶节点元素(如 ContentPresenter)用于包含子元素,它们并不包含指令列表。

    如果可视化元素既包含向量图形指令列表又包含可视化子级,则会先呈现父级可视化元素中的指令列表,然后再呈现任何可视化子对象中的绘图。

    向量图形指令列表中的项按照从左到右的顺序呈现。

    可视化树
    可视化树中包含某个应用程序的用户界面所使用的所有可视化元素。由于可视化元素中包含持久的绘图信息,因此您可以将可视化树视为场景图,其中包含将输出写入显示设备所必需的全部呈现信息。该树汇集了由该应用程序在代码或标记中直接创建的所有可视化元素。该可视化树还包含由元素(如控件和数据对象)的模板扩展功能创建的所有可视化元素。

    下面的代码显示的是在标记中定义的 StackPanel 元素。

    如果您要枚举包含标记示例中 StackPanel 元素的可视化对象,将发现如下所示可视化对象的层次结构:

    可视化树层次结构的关系图

     
     
    呈现顺序
    通过可视化树,可以确定 WPF 可视化对象和绘图对象的呈现顺序。将从位于可视化树中最顶层节点中的可视化元素根开始遍历,然后将按照从左到右的顺序遍历可视化元素根的子级。如果某个可视化元素有子级,则将先遍历该可视化元素的子级,然后再遍历其同级。这意味着子可视化元素的内容先于该可视化元素本身的内容而呈现。

    可视化树呈现顺序的关系图

     
     
    可视化元素根
    可视化元素根是可视化树层次结构中最顶层的元素。在大多数应用程序中,可视化元素根的基类是 Window 或 NavigationWindow。但是,如果您在 Win32 应用程序中承载可视化对象,则可视化元素根将是在 Win32 窗口中承载的最顶层的可视化元素。

    与逻辑树的关系
    WPF 中的逻辑树表示应用程序在运行时的元素。尽管您不直接操作该树,但是该应用程序视图对于了解属性继承和事件路由非常有用。与可视化树不同,逻辑树可以表示非可视化数据对象(如 ListItem)。在许多情况下,逻辑树密切映射到应用程序的标记定义。下面的代码显示的是在标记中定义的 DockPanel 元素。

    如果您要枚举包含标记示例中 DockPanel 元素的逻辑对象,则将发现如下所示逻辑对象的层次结构:

    逻辑树的关系图


    可视化树和逻辑树与当前的应用程序元素集合同步,并反映对元素进行的任何添加、删除或修改。但是,这些树表示不同的应用程序视图。与可视化树不同,逻辑树不展开控件的 ContentPresenter 元素。这意味着同一组对象的逻辑树和可视化树之间没有直接的一对一对应关系。实际上,在将同一个元素用作参数的情况下,调用 LogicalTreeHelper 对象的 GetChildren 方法与调用 VisualTreeHelper 对象的 GetChild 方法会生成不同的结果。

    使用 XamlPad 查看可视化树
    WPF 工具 (XamlPad) 提供了一个用来查看和浏览可视化树的选项,该树与当前所定义的 XAML 内容相对应。单击菜单栏上的“显示可视化树”[Show Visual Tree]按钮可显示相应的可视化树。下面将说明如何在 XamlPad 的“可视化树资源管理器”[Visual Tree Explorer]面板中将 XAML 内容扩展为可视化树节点:

    XamlPad 中的“可视化树资源管理器”[Visual Tree Explorer]面板

     
    请注意,Label、TextBox 和 Button 控件在 XamlPad 的“可视化树资源管理器”[Visual Tree Explorer]面板中各自显示一个可视化对象层次结构。这是由于 WPF 控件具有一个包含其可视化树的 ControlTemplate。当您显式引用某个控件时,会隐式引用它的可视化层次结构。

    分析可视化性能
    WPF 提供了一套性能分析工具,来帮助您分析应用程序的运行时行为,并确定可以应用的性能优化的类型。可视化探查器工具通过直接映射到应用程序的可视化树来为性能数据提供一个丰富的图形视图。在此屏幕快照中,可视化探查器的“CPU Usage”[CPU 使用率]部分使您可以清楚地了解对象对 WPF 服务(如呈现和布局)的使用情况。

    可视化探查器显示输出

    可视化对象的呈现行为
    WPF 引进了几个影响可视化对象呈现行为的功能:保留的模式图形、矢量图形和与设备无关的图形。

    保留的模式图形
    了解即时模式和保留模式图形系统之间的区别是了解 Visual 对象角色的要点之一。基于 GDI 或 GDI+ 的标准 Win32 应用程序使用即时模式图形系统。这意味着应用程序负责重新绘制工作区中由于某项操作(如调整窗口大小)或者对象的可视化外观发生变化而失效的部分。

    Win32 呈现顺序的关系图


    与之相比,WPF 使用保留模式系统。这意味着具有可视化外观的应用程序对象定义一组序列化绘图数据。在定义了绘图数据之后,系统会响应所有的重新绘制请求来呈现应用程序对象。甚至在运行时,您也可以修改或创建应用程序对象,并仍旧依赖系统响应绘制请求。保留模式图形系统中有一个强大功能,那就是绘图信息总是由应用程序保持为序列化状态,但是呈现功能仍由系统负责。下面的关系图演示应用程序如何依赖 WPF 来响应绘制请求。

    WPF 呈现顺序的关系图

     
    智能重绘
    使用保留模型图形的最大好处之一就是,WPF 可以高效率地优化需要在应用程序中重绘的内容。即使您有一个具有各种不透明度的复杂场景,通常也不必编写特殊用途的代码来优化重绘功能。请将智能重绘功能与 Win32 编程进行比较,在后者中,可以通过最小化更新区域中的重绘量来尽力优化应用程序。

    向量图形
    WPF 使用向量图形作为其呈现数据的格式。向量图形(包括可缩放的向量图形 (SVG)、Windows 元文件 (.wmf) 和 TrueType 字体)存储呈现数据,并以指令列表的形式传输该呈现数据,这些指令描述如何使用图形基元来重新创建图像。例如,TrueType 字体是描述一组直线、曲线和命令(而不是像素数组)的矢量字。矢量图形的主要好处之一就是能够伸缩到任何大小和分辨率。

    与矢量图形不同,位图图形以图像的逐像素表示形式来存储呈现数据,而且在特定的分辨率下预先呈现。位图图形格式和矢量图形格式的主要区别之一就是对原始图像的保真度。例如,当某个源图像的大小发生变化时,位图图形系统会拉伸该图像,而向量图形系统会伸缩该图像,从而保持图像的保真度。

    下图显示了源图像在放大到 3 倍时的情况。请注意,当源图像作为位图图形拉伸时会发生失真,而当源图像作为矢量图形伸缩时,则不会发生失真。

    光栅图形和矢量图形之间的区别


    下面的标记显示所定义的两个 Path 元素。第二个元素使用 ScaleTransform 将第一个元素的绘图指令放大到 3 倍。请注意 Path 元素中的绘图指令保持不变。

    关于分辨率和与设备无关的图形
    可通过以下两个系统因子来确定屏幕上的文本大小和图形大小:分辨率和 DPI。分辨率描述出现在屏幕上的像素数量。由于分辨率变大,因此像素会变小,从而导致所显示的图形和文本会变小。在将显示器的分辨率从 1024 x 768 更改为 1600 x 1200 时,显示器上所显示的图形会小得多。

    另一个系统设置 (DPI) 以像素数来描述屏幕英寸的大小。大多数 Windows 系统的 DPI 都为 96,这意味着一屏幕英寸等于 96 个像素。增加 DPI 设置会使屏幕英寸变大,减小 DPI 会使屏幕英寸变小。这意味着屏幕英寸与实际的英寸不相等;在多数系统上,二者很有可能不相等。当您增加 DPI 时,屏幕英寸会变大,因此支持 DPI 的图形和文本也会变大。增加 DPI 可能会增强文本的可读性,在高分辨率下尤其如此。

    并非所有的应用程序都支持 DPI:一些应用程序将硬件像素作为其主要计量单位;更改系统 DPI 不会对这些应用程序产生任何影响。许多其他应用程序都使用支持 DPI 的单位来描述字号,使用像素来描述任何其他内容。DPI 太小或太大都可能会导致这些应用程序出现布局问题,因为应用程序的文本会随着系统的 DPI 设置而伸缩,而应用程序的 UI 却不会出现此类问题。对于使用 WPF 开发的应用程序,此问题已经消除。

    WPF 支持通过将与设备无关的像素(而不是硬件像素)用作其主要计量单位来自动伸缩;图像和文本会适当伸缩,而无需应用程序开发人员执行任何额外的工作。下图显示了 WPF 文本和图形在不同 DPI 设置下的显示方式的示例。

    不同 DPI 设置下的图形和文本

     
    VisualTreeHelper
    VisualTreeHelper 类是一个静态帮助器类,它提供了一个要在可视化对象级别编程的低级功能,该类在非常特殊的方案(如开发高性能自定义控件)中非常有用。在大多数情况下,更高级的 WPF 框架对象(如 Canvas 和 TextBlock)提供更大的灵活性且更易于使用。

    命中测试
    VisualTreeHelper 类提供了当默认的命中测试支持无法满足您的需要时,针对可视化对象的命中测试方法。可以在 VisualTreeHelper 类中使用 HitTest 方法来确定几何形状或点坐标值是否位于给定对象(如控件或图形元素)的边界内。例如,您可以使用命中测试来确定鼠标在对象边框中的单击点是否落在圆形几何形状内部。您还可以选择重写对命中测试的默认实现来执行自己的自定义命中测试计算。

    枚举可视化树
    VisualTreeHelper 类提供了用来枚举可视化树成员的功能。若要检索父级,请调用 GetParent 方法。若要检索可视化对象的子级或直接子代,请调用 GetChild 方法。此方法返回父级在指定索引处的子 Visual。

    下面的示例演示如何枚举一个可视化对象的所有子代,如果您对序列化可视化对象层次结构的所有呈现信息感兴趣,则可能希望使用该技术。

    在大多数情况下,逻辑树更能表示 WPF 应用程序中的元素。尽管您不直接修改逻辑树,但是该应用程序视图对于了解属性继承和事件路由非常有用。与可视化树不同,逻辑树可以表示非可视化数据对象(如 ListItem)。
    VisualTreeHelper 类提供用来返回可视化对象边框的方法。可以通过调用 GetContentBounds 来返回可视化对象的边框。可以通过调用 GetDescendantBounds 来返回可视化对象及其所有子代的边框。下面的代码演示如何计算可视化对象及其所有子代的边框。

    1. WPF的图像处理

    本主题介绍 WPF 图像处理。通过 Microsoft Windows Presentation Foundation 图像处理组件,开发人员可以显示、转换和格式化图像。

    WPF 图像处理组件
    WPF 图像处理使得 Microsoft Windows 内的图像处理功能得到了极大改进。以前,图像处理功能(如显示位图或在常见控件上使用图像)依赖于 Microsoft Windows 图形设备接口 (GDI) 或 Microsoft Windows GDI+ 库。这些 API 提供有基本图像处理功能,但缺少诸如支持编解码器扩展性和高保真图像支持等功能。WPF 图像处理旨在克服 GDI 和 GDI+ 的缺点,并提供一组新的 API,用以在应用程序内显示和使用图像。

    有两种方式可以访问 WPF 图像处理 API:托管组件和非托管组件。非托管组件提供以下功能。

    适用于新的或专用图像格式的扩展性模型。

    对包括位图 (BMP)、联合图像专家组 (JPEG)、可移植网络图形 (PNG)、标记图像文件格式 (TIFF)、Microsoft Windows Media 照片、图形交换格式 (GIF) 和图标 (.ico) 在内的本机图像格式增强了性能和安全性。

    高位深图像数据的保留最多 32 位/通道。

    非破坏性图像缩放、裁切和旋转。

    简化的颜色管理

    支持文件内的专用元数据。

    托管组件利用非托管基础结构提供图像与其他 WPF 功能(如用户界面 (UI)、动画和图形)的无缝集成。托管组件还可以从 Windows Presentation Foundation (WPF) 图像处理编解码器扩展性模型获益,利用该模型可以实现自动识别 WPF 中的新图像格式。

    大部分托管的 WPF 图像处理 API 驻留在 System.Windows.Media.Imaging 命名空间中,不过,几个重要的类型(如 ImageBrush 和 ImageDrawing)都驻留在 System.Windows.Media 命名空间,Image 驻留在 System.Windows.Controls 命名空间。

    本主题提供有关托管组件的其他信息。

    WPF 图像格式
    编解码器用于对特定媒体格式解码或编码。WPF 图像处理包括一个适用于 BMP、JPEG、PNG、TIFF、Windows Media 照片、GIF 和 ICON 图像格式的编解码器。利用上述每个编解码器,应用程序可以对其各自的图像格式进行解码(ICON 除外)和编码。

    BitmapSource 是一个重要的类,用于对图像进行解码和编码。它是 WPF 图像处理管线的基本构造块,表示具有特定大小和分辨率的单个不变的像素集。BitmapSource 可以是多个帧图像中的单个帧,或者可以是在 BitmapSource 上执行转换的结果。它是 WPF 图像处理中使用的许多主要类(如 BitmapFrame)的父级。

    BitmapFrame 用于存储图像格式的实际位图数据。许多图像格式仅支持单一 BitmapFrame,不过 GIF 和 TIFF 等格式的图像支持每个图像有多个帧。帧由解码器用作输入数据,并传递到编码器以创建图像文件。

    下面的示例演示如何从 BitmapSource 创建一个 BitmapFrame 并将其添加到 TIFF 图像。

    图像格式解码
    图像解码是指将某种图像格式转换为可以由系统使用的图像数据。然后,此图像数据可以用于显示、处理或编码为其他格式。解码器的选择是基于图像格式做出的。编解码器的选择是自动做出的,除非指定了特定的解码器。在 WPF 中显示图像小节中的示例演示了自动解码。使用非托管 WPF 图像处理界面开发并向系统注册的自定义格式解码器会自动加入到解码器选择队列。这将使得自定义格式可以自动显示在 WPF 应用程序中。

    下面的示例演示使用位图解码器对 BMP 格式的图像进行解码。

    图像格式编码
    图像编码是指将图像数据转换为特定图像格式的过程。然后,已编码的图像数据可以用于创建新图像文件。WPF 图像处理为上面介绍的每种图像格式提供编码器。

    下面的示例演示使用编码器保存一个新创建的位图图像。

    在 WPF 中显示图像
    可以通过多种方式在 Windows Presentation Foundation (WPF) 应用程序中显示图像。可以使用 Image 控件显示图像、使用 ImageBrush 在可视图面上绘制图像或使用 ImageDrawing 绘制图像。

    使用 Image 控件
    Image 是一个框架元素,是在应用程序中显示图像的主要方式。在 XAML 中,有两种方式可以使用 Image:属性 (attribute) 语法或属性 (property) 语法。下面的示例演示如何使用属性 (attribute) 语法或属性 (property) 标记语法来呈现一个宽 200 像素的图像。

    许多示例使用 BitmapImage 对象引用图像文件。BitmapImage 是一个专用的 BitmapSource,已经优化为用于可扩展应用程序标记语言 (XAML) 加载,是一种将图像显示为 Image 控件的 Source 的简便方式。

    下面的示例演示如何使用代码呈现宽 200 像素的图像。

    说明:
    BitmapImage 实现 ISupportInitialize 接口,以对多个属性的初始化进行优化。只能在对象初始化过程中进行属性更改。调用 BeginInit 以表示初始化开始;调用 EndInit 以表示初始化结束。初始化一旦开始之后,将忽略所做的属性更改。

    旋转、转换和裁切图像
    通过 WPF,用户可以使用 BitmapImage 的属性或使用其他 BitmapSource 对象(如 CroppedBitmap 或 FormatConvertedBitmap)来转换图像。上述图像转换可以缩放或旋转图像、更改图像的像素格式或裁切图像。

    可以使用 BitmapImage 的 Rotation 属性来执行图像旋转。旋转只能以 90 度的增量来进行。在下面的示例中,图像旋转了 90 度。

    可以使用 FormatConvertedBitmap 将图像转换为不同的像素格式,如灰度。在下面的示例中,图像转换为 Gray4。

    旋转、转换和裁切图像
    通过 WPF,用户可以使用 BitmapImage 的属性或使用其他 BitmapSource 对象(如 CroppedBitmap 或 FormatConvertedBitmap)来转换图像。上述图像转换可以缩放或旋转图像、更改图像的像素格式或裁切图像。

    可以使用 BitmapImage 的 Rotation 属性来执行图像旋转。旋转只能以 90 度的增量来进行。在下面的示例中,图像旋转了 90 度。

    可以使用 FormatConvertedBitmap 将图像转换为不同的像素格式,如灰度。在下面的示例中,图像转换为 Gray4。

    若要裁切图像,可以使用 Image 或 CroppedBitmap 的 Clip 属性。通常情况下,如果您只想调整图像的一部分,则应使用 Clip。如果需要编码和保存裁切过的图像,应使用 CroppedBitmap。下面的示例使用 EllipseGeometry 和 Clip 属性来裁切图像。

    拉伸图像
    Stretch 属性控制如何拉伸一个图像以使其填充容器。Stretch 属性接受以下值(是由 Stretch 枚举定义的):

    None:不会拉伸图像以填充输出区域。如果图像比输出区域大,则图像将绘制到输出区域,而无法容纳的内容将被剪裁掉。

    Fill:会拉伸图像以适应输出区域。由于图像的高度和宽度是独立进行缩放的,因此图像的原始长宽比可能不会保留。也就是说,为了完全填充输出容器,图像可能会扭曲。

    Uniform:图像进行缩放,以便其完全适应输出区域。图像的长宽比会保留。

    UniformToFill:图像会进行缩放,以便在保留图像原始长宽比的同时完全填充输出区域。

    下面的示例将每个可用的 Stretch 枚举应用于 Image。

    下面的图像显示示例的输出,演示了不同的 Stretch 设置在应用到图像时的效果。

    不同的拉伸设置

    绘制图像
    您还可以利用 Brush 进行绘制,从而在应用程序中显示图像。利用画笔,您可以利用任意内容(从简单的纯色到复杂的图案和图像集)绘制 UI 对象。若要绘制图像,请使用 ImageBrush。ImageBrush 是一种 TileBrush 类型,用于将其内容定义为位图图像。ImageBrush 显示由其 ImageSource 属性指定的单个图像。您可以控制图像的拉伸、对齐和平铺方式,从而可以防止失真并生成图案和其他效果。下图显示了利用 ImageBrush 可以达到的几种效果。

    图像的画笔可以填充形状、控件、文本等


    下面的示例演示如何使用 ImageBrush 绘制带图像的按钮的背景。

    图像元数据
    某些图像文件包含用于描述文件的内容或特征的元数据。例如,大多数数码相机创建的图像中包含用于捕获该图像的照相机的品牌和型号的元数据。每个图像格式处理元数据的方式不同,但 WPF 图像处理提供一种统一的方式来为每个支持的图像格式存储和检索元数据。

    对元数据的访问是通过 BitmapSource 对象的 Metadata 属性来进行的。Metadata 返回一个 BitmapMetadata 对象,其中包含该图像所包含的所有元数据。此数据可以位于一个元数据架构中或位于不同方案的组合中。WPF 图像处理 支持以下图像元数据架构:可交换图像文件 (Exif)、tEXt(PNG 文本数据)、图像文件目录 (IFD)、国际新闻通信委员会 (IPTC) 和可扩展元数据平台 (XMP)。

    为了简化读取元数据的过程,BitmapMetadata 提供易于访问的若干命名属性(如 Author、Title 和 CameraModel)。许多命名属性还可以用于编写元数据。元数据查询读取器提供对读取元数据的其他支持。GetQuery 方法用于检索元数据查询读取器,这是通过提供字符串查询(如 "/app1/exif/")来实现的。在下面的示例中,GetQuery 用于获取存储在 "/Text/Description" 位置中的文本。

    若要编写元数据,会使用到元数据查询编写器。SetQuery 获取查询编写器并设置需要的值。在下面的示例中,SetQuery 用于编写存储在 "/Text/Description" 位置中的文本。

    编解码器扩展性
    WPF 图像处理的核心功能是用于新图像编解码器的扩展性模型。通过这些非托管的接口,编解码器开发人员可以将编解码器与 WPF 集成,这样 WPF 应用程序就可以自动使用新的图像格式。

    说明:
    编解码器必须进行数字签名,以便于系统识别。

    1. WPF的三维图形应用

    本主题概述 Windows Presentation Foundation (WPF) 图形系统中的三维功能。通过 WPF 三维实现,开发人员可使用与该平台所提供给二维图形的相同的功能,对标记和过程代码中的三维图形进行绘制、转换和动画处理。 开发人员可以合并二维和三维图形来创建丰富的控件,提供复杂的数据图解,或者增强用户对应用程序界面的体验。WPF 支持三维的设计宗旨不是提供功能齐全的游戏开发平台。

    二维容器中的三维
    WPF 中的三维图形内容封装在 Viewport3D 元素中,该元素可以参与二维元素结构。与 WPF 中的许多其他内容一样,图形系统将 Viewport3D 视为二维可视化元素。Viewport3D 充当三维场景中的一个窗口(视区)。更准确地说,它是三维场景所投影到的图面。

    在传统的二维应用程序中,当您需要使用 Grid 或 Canvas 之类的另一个容器元素时,可以使用 Viewport3D。 尽管您可以将 Viewport3D 与同一个场景图中的其他二维绘图对象结合使用,但是您不能在 Viewport3D 内部渗透二维和三维对象。 本主题重点讲述如何在 Viewport3D 内部绘制三维图形。

    三维坐标空间
    二维图形的 WPF 坐标系将原点定位在呈现区域(通常是屏幕)的左上角。在二维系统中,x 轴上的正值朝右,y 轴上的正值朝下。 但是,在三维坐标系中,原点位于呈现区域的中心,x 轴上的正值朝右,但是 y 轴上的正值朝上,z 轴上的正值从原点向外朝向观察者。

    传统的二维和三维坐标系表示形式

      
    由这些轴定义的空间是三维对象在 WPF 中的固定参考框架。当您在该空间中生成模型并创建光源和照相机以查看这些模型时,一定要在向每个模型应用变换时,将固定参考框架或“全局空间”与您为该模型创建的局部参考框架区分开。另请记住,根据光源和照相机设置,全局空间中的对象可能会看上去完全不同或者根本不可见,但是照相机的位置不会改变对象在全局空间中的位置。

    照相机和投影
    处理二维对象的开发人员习惯于将绘图基元置于二维屏幕上。当您创建三维场景时,一定要记住您实际上是要创建三维对象的二维表示形式。由于三维场景的外观会因观察者的观察位置不同而异,因此您必须指定观察位置。可以使用 Camera 类来为三维场景指定观察位置。

    了解三维场景如何在二维图面上表示的另一种方法就是将场景描述为到观察表面上的投影。使用 ProjectionCamera,可以指定不同的投影及其属性以更改观察者查看三维模型的方式。PerspectiveCamera 指定用来对场景进行透视收缩的投影。 换言之,PerspectiveCamera 提供消失点透视。 您可以指定照相机在场景坐标系中的位置、照相机的方向和视野以及用来定义场景中“向上”方向的向量。下图阐释 PerspectiveCamera 的投影。

    ProjectionCamera 的 NearPlaneDistance 和 FarPlaneDistance 属性限制照相机的投影范围。由于照相机可以位于场景中的任何位置,因此照相机实际上可能会位于模型内部或者紧靠模型,这使得很难正确区分对象。使用 NearPlaneDistance,可以指定一个距离照相机的最小距离,即,在超过该距离后将不绘制对象。 相反,使用 FarPlaneDistance,可以指定一个距离照相机的距离(即,在超过该距离后将不绘制对象),从而确保因距离太远而无法识别的对象将不包括在场景中。

    照相机位置

     
    OrthographicCamera 指定三维模型到二维可视化图面上的正投影。与其他照相机一样,它指定位置、观察方向和“向上”方向。但是,与 PerspectiveCamera 不同的是,OrthographicCamera 描述了不包括透视收缩的投影。换言之,OrthographicCamera 描述了一个侧面平行的取景框,而不是侧面汇集在场景中一点的取景框。下图演示使用 PerspectiveCamera 和 OrthographicCamera 查看同一模型时的情况。

    透视投影和正投影

     
    下面的代码演示一些典型的照相机设置。

    模型和网格基元
    Model3D 是表示泛型三维对象的抽象基类。 若要生成三维场景,需要一些要查看的对象,而且构成场景图的对象必须派生自 Model3D。 目前,WPF 支持用 GeometryModel3D 对几何形状进行建模。此模型的 Geometry 属性采用网格基元。

    若要生成模型,请首先生成一个基元或网格。 三维基元是一系列构成单个三维实体的顶点。 大多数三维系统都提供在最简单的闭合图(由三个顶点定义的三角形)上建模的基元。 由于三角形的三个点在一个平面上,因此您可以继续添加三角形,以便对网格这样较为复杂的形状建模。

    WPF 三维系统目前提供 MeshGeometry3D 类,使用该类,可以指定任何几何形状;它目前不支持预定义的三维基元(如球体和立方体)。首先通过将三角形顶点的列表指定为它的 Positions 属性来创建 MeshGeometry3D。每个顶点都指定为 Point3D。 (在可扩展应用程序标记语言 (XAML) 中,将该属性指定为三个一组的数字列表,每组中的三个数字表示每个顶点的坐标)。 根据网格的几何形状,网格可能会由多个三角形组成,其中的一些三角形共用相同的角(顶点)。 若要正确地绘制网格,WPF 需要有关哪些顶点由哪些三角形共用的信息。 可以通过指定具有 TriangleIndices 属性的三角形索引列表来提供此信息。此列表指定在 Positions 列表中指定的点将按哪种顺序确定三角形。

    在上面的示例中,Positions 列表指定用八个顶点来定义立方体形状的网格。 TriangleIndices 属性指定了一个包含十二个组的列表,每组由三个索引组成。 列表中的每个数字都指向与 Positions 列表的偏移量。 例如,由 Positions 列表指定的第一组(三个顶点)是 (1,1,0)、(0,1,0) 和 (0,0,0)。由 TriangleIndices 列表指定的第一组(三个索引)是 0、2 和 1,这与 Positions 列表中的第一个、第三个和第二个点相对应。 因此,构成立方体模型的第一个三角形将按照从 (1,1,0) 到 (0,1,0) 再到 (0,0,0) 的顺序组合而成,其余的十一个三角形将按照类似方式确定。

    您可以通过为 Normals 和 TextureCoordinates 属性指定值来继续定义模型。 为了呈现模型的图面,图形系统需要有关曲面在任何给定三角形上的朝向信息。图形系统使用此信息来针对该模型进行照明计算:直接朝向光源的图面比偏离光源的图面显得更亮。尽管 WPF 可以使用位置坐标来确定默认的法向量,但是您还可以指定不同的法向量来近似计算曲面的外观。

    TextureCoordinates 属性指定 Point 集合,该集合可通知图形系统如何将用来确定纹理绘制方式的坐标映射到网格的顶点。TextureCoordinates 可指定为 0 和 1(包括 0 和 1)之间的值。 如同 Normals 属性一样,图形系统可以计算默认纹理坐标,但是您可以选择设置不同的纹理坐标来控制对包括重复图案一部分的纹理的映射。

    下面的示例演示如何在过程代码中创建立方体模型的一面。请注意,您可以将整个立方体绘制为单个 GeometryModel3D;此示例将该立方体的各个面绘制为一个不同的模型,以便在以后向每个面应用不同的纹理。

    向模型应用 Material
    为了使网格看上去像三维对象,必须向其应用纹理,以便覆盖由顶点和三角形定义的图面,从而使其可以由照相机照明和投影。在二维中,可以使用 Brush 类来向屏幕中的区域应用颜色、图案、渐变或其他可视化内容。 但是,三维对象的外观是照明模型的功能,而不只是应用于它们的颜色或图案。实际对象的图面质量不同,它们反射光的方式也会有所不同:光亮的图面与粗糙或不光滑的图面看上去不同,某些对象似乎可以吸收光,而某些对象似乎能够发光。您可以向三维对象应用与应用于二维对象的完全相同的画笔,但是您不能直接应用它们。

    WPF 使用 Material 抽象类来定义模型图面的特征。Material 的具体子类用来确定模型图面的某些外观特征,每个子类还提供一个可以向其传递 SolidColorBrush、TileBrush 或 VisualBrush 的 Brush 属性。

    DiffuseMaterial 指定将向模型应用画笔,就好像模型是使用漫射光来照亮的一样。使用 DiffuseMaterial 与直接针对二维模型使用画笔非常相似;模型表面不反射光,就好像是自发光一样。

    SpecularMaterial 指定将向模型应用画笔,就好像模型的表面坚硬或者光亮,能够反射光线一样。可以通过为 SpecularPower 属性指定一个值来设置系统将为纹理的反射特质(或“发光”)建议的度数。

    使用 EmissiveMaterial 可以指定将应用纹理,就好像模型所发出的光与画笔的颜色相同。这不会使模型成为光源;但是,它参与阴影设置的方式将不同于用 DiffuseMaterial 或 SpecularMaterial 设置纹理时的情况。

    为进一步提高性能,可以从场景中精选 GeometryModel3D 的背面(由于它们相对于照相机位于模型的背面,因此您将看不到这些面)。 若要指定要应用于模型(如飞机)背面的 Material,请设置模型的 BackMaterial 属性。

    为了实现某些图面质量(如发光或发射效果),您可能希望向模型连续应用几个不同的画笔。您可以使用 MaterialGroup 类来应用和重用多个 Material。MaterialGroup 的子级在多个呈现过程中按照从头到尾的顺序来应用。

    下面的代码示例演示如何将纯色和绘图以画笔形式应用于三维模型。

    照亮场景
    与实际的光一样,三维图形中的光能够使图面可见。更确切地说,光确定了场景的哪个部分将包括在投影中。WPF 中的光对象创建了各种光和阴影效果,而且是按照各种实际光的行为建模的。您必须至少在场景中包括一个光,否则模型将不可见。

    下面的光派生自基类 Light:

    AmbientLight:它所提供的环境光以一致的方式照亮所有的对象,而与对象的位置或方向无关。

    DirectionalLight:像远处的光源那样照亮。 将方向光的 Direction 指定为 Vector3D,但是没有为方向光指定位置。

    PointLight:像近处的光源那样照亮。PointLight 具有一个位置并从该位置投射光。场景中的对象是根据对象相对于光源的位置和距离而照亮的。PointLightBase 公开了一个 Range 属性,该属性确定一个距离,在超过该距离后模型将无法由光源照亮。PointLight 还公开了多个衰减属性,这些属性确定光源的亮度如何随距离的增加而减小。您可以为光源的衰减指定恒定、线性或二次内插算法。

    SpotLight:继承自 PointLight。Spotlight 的照亮方式与 PointLight 类似,但是它既具有位置又具有方向。它们在 InnerConeAngle 和 OuterConeAngle 属性所设置的锥形区域(以度为单位指定)中投射光。

    光源是 Model3D 对象,因此您可以转换光源对象并对光源属性(包括位置、颜色、方向和范围)进行动画处理。

    变换模型
    当您创建模型时,它们在场景中具有特定的位置。为了在场景中移动、旋转这些模型或者更改这些模型的大小而更改用来定义模型本身的顶点是不切实际的。 相反,正如在二维中一样,您可以向模型应用转换。

    每个模型对象都有一个可用来对模型进行移动、重定向或调整大小的 Transform 属性。 当您应用转换时,实际上是按照由转换功能指定的向量或值(以适用者为准)来有效地偏移模型的所有点。换言之,您已经变换了在其中定义模型的坐标空间(“模型空间”),但是,您尚未更改在整个场景的坐标系(“全局空间”)中构成模型几何形状的值。

    对模型进行动画处理
    WPF 三维实现与二维图形参与同一个计时和动画系统。换言之,若要对三维场景进行动画处理,需要对其模型的属性进行动画处理。 可以直接对基元的属性进行动画处理,但是通常很容易对用来更改模型位置或外观的变换进行动画处理。 由于可以向 Model3DGroup 对象及其各个模型应用转换,因此可以向 Model3DGroup 的子级应用一组动画,向一组子对象应用另一组动画。 还可以通过对场景的照明属性进行动画处理来实现各种可视化效果。 最后,您可以选择通过对照相机的位置或视野进行动画处理来对投影本身进行动画处理。

    若要对 WPF 中的对象进行动画处理,可以创建时间线、定义动画(实际上是随着时间的推移而更改某个属性值)并指定要向其应用动画的属性。 由于三维场景中的所有对象都是 Viewport3D 的子级,因此要应用于场景的任何动画所面向的属性都是 Viewport3D 的属性。

    假设您希望实现模型看上去是在原地摇摆的效果, 您可以选择向模型应用 RotateTransform3D,并对模型从一个向量旋转到另一个向量时所围绕的轴进行动画处理。下面的代码示例演示如何将 Vector3DAnimation 应用于该转换的 Rotation3D 的 Axis 属性,并假设 RotateTransform3D 是应用于具有 TransformGroup 的模型的几个转换之一。

    向窗口中添加三维内容
    若要呈现场景,请向 Model3DGroup 中添加模型和光源,然后将 Model3DGroup 设置为 ModelVisual3D 的 Content。将 ModelVisual3D 添加到 Viewport3D 的 Children 集合中。 通过设置 Viewport3D 的 Camera 属性来向其添加照相机。

    最后,请向该窗口中添加 Viewport3D。在将 Viewport3D 作为布局元素(如 Canvas)的内容来包含时,可以通过设置 Viewport3D 的 Height 和 Width 属性(继承自 FrameworkElement)来指定 Viewport3D 的大小。

    1. WPF的三维变换应用

    本主题描述如何向 Windows Presentation Foundation (WPF) 图形系统中的三维模型应用变换。开发人员可以借助于变换功能来对模型进行重定位、调整大小和重定向,而无需更改用来定义模型的基值。

    三维坐标空间
    Windows Presentation Foundation (WPF) 中的三维图形内容封装在 Viewport3D 元素中,该元素可以参与二维元素结构。该图形系统将 Viewport3D 视为一个像 Windows Presentation Foundation (WPF) 中的许多其他元素一样的二维可视化元素。Viewport3D 充当三维场景中的一个窗口(视区)。更准确地说,它是三维场景所投影到的图面。 尽管可以将 Viewport3D 与其他二维绘图对象用在同一个场景关系图中,但是不能在 Viewport3D 中渗透二维和三维对象。以下讨论中所描述的坐标空间包含在 Viewport3D 元素中。

    二维图形的 Windows Presentation Foundation (WPF) 坐标系将原点定位在呈现图面(通常是屏幕)的左上角。在二维系统中,x 轴上的正值朝右增加,y 轴上的正值朝下增加。但是,在三维坐标系中,原点位于屏幕的中心,x 轴上的正值朝右增加,但是 y 轴上的正值朝上增加,z 轴上的正值从原点向外朝向观察者增加。

    坐标系比较


    由这些轴定义的空间是三维对象在 Windows Presentation Foundation (WPF) 中的固定参考框架。当您在该空间中生成模型并创建光源和照相机以查看这些模型时,一定要在向每个模型应用变换时,将固定参考框架或“全局空间”与您为该模型创建的局部参考框架区分开。另请记住,根据光源和照相机设置,全局空间中的对象可能会看上去完全不同或者根本不可见,但是照相机的位置不会改变对象在全局空间中的位置。

    变换模型
    当您创建模型时,它们在场景中具有特定的位置。为了在场景中移动、旋转这些模型或者更改这些模型的大小而更改用来定义模型本身的顶点是不切实际的。相反,正如在二维对象中一样,您可以向模型应用变换。

    每个模型对象都有一个可用来对模型进行移动、重定向或调整大小的 Transform 属性。当您应用变换时,实际上是按照由变换功能指定的向量或值(以适用者为准)来偏移模型的所有点。换言之,您已经变换了在其中定义模型的坐标空间(“模型空间”),但是,您尚未更改在整个场景的坐标系(“全局空间”)中构成模型几何形状的值。

    平移变换
    三维变换继承自抽象基类 Transform3D;这些变换包括仿射变换类 TranslateTransform3D、ScaleTransform3D 和 RotateTransform3D。Windows Presentation Foundation (WPF) 三维系统还提供一个 MatrixTransform3D 类,使用该类,可以用更简明的矩阵操作来指定同样的变换。

    TranslateTransform3D 沿着您用 OffsetX、OffsetY 和 OffsetZ 属性指定的偏移向量所指示的方向来移动 Model3D 中的所有点。例如,假设立方体的一个顶点位于 (2,2,2),则偏移向量 (0,1.6,1) 会将该顶点 (2,2,2) 移到 (2,3.6,3)。该立方体的顶点在模型空间中仍位于 (2,2,2),但是现在,该模型空间与世界空间的关系已经发生改变,因此,模型空间中的 (2,2,2) 是世界空间中的 (2,3.6,3)。

    带有偏移的平移


    下面的代码示例演示如何应用平移。

    缩放变换
    ScaleTransform3D 沿着指定的缩放向量,相对于中心点来更改模型的比例。可以指定等比缩放,即在 X、Y 和 Z 轴上将模型缩放同样的值来按比例更改模型的大小。例如,将该变换的 ScaleX、ScaleY 和 ScaleZ 属性设置为 0.5 会将此模型二等分;将这些属性设置为 2 会将此模型在所有这三个轴上的值放大一倍。

    ScaleVector 示例


    通过指定非等比变换(X、Y 和 Z 值不相等的缩放变换),可以使模型在一个或两个维度上拉伸或收缩,而不会影响其他维度。例如,如果将 ScaleX、ScaleY 和 ScaleZ 分别设置为 1、2 和 1,则将导致变换模型的高度增加一倍,但是 X 和 Z 轴上的值仍保持不变。

    默认情况下,ScaleTransform3D 会导致顶点围绕原点 (0,0,0) 拉伸或收缩。但是,如果要变换的模型不是从原点绘制的,那么,在从原点缩放模型时,模型将不会“就地”缩放。相反,当模型的顶点与缩放向量相乘时,缩放操作对模型的平移和缩放都会产生影响。

    缩放中心示例

    若要“就地”缩放模型,请通过设置 ScaleTransform3D 的 CenterX、CenterY 和 CenterZ 属性来指定模型的中心。这可确保图形系统缩放模型空间,然后平移该空间,使其在指定的 Point3D 上居中。相反,如果模型是围绕原点生成的,而且您指定了一个不同的中心点,则将看到模型会背离原点平移。

    旋转变换
    可以通过几种不同的方法来旋转三维模型。典型的旋转变换指定一个轴以及围绕该轴的旋转角度。使用 RotateTransform3D 类,可以用 Rotation 属性来定义 Rotation3D。然后可以在 Rotation3D(在本例中为 AxisAngleRotation3D)上指定 Axis 和 Angle 属性来定义变换。下面的几个示例围绕 Y 轴将模型旋转 60 度。

    注意:Windows Presentation Foundation (WPF) 三维是一个右手系统,这意味着,如果旋转角度为正数,则将围绕该轴逆时针旋转。

    对于指定了轴和角度的旋转来说,如果没有为 RotateTransform3D 上的 CenterX、CenterY 和 CenterZ 属性指定值,则假设围绕原点旋转。与缩放时一样,一定要记住旋转时会变换模型的整个坐标空间。如果模型不是围绕原点创建的,或者它以前经过平移,则旋转可能会围绕原点“转动”,而不是就地旋转。

    在指定了新中心的情况下旋转


    若要“就地”旋转模型,请将模型的实际中心指定为旋转中心。由于几何形状通常是围绕原点建模的,因此,在依次执行下列操作时通常将获取一组预期的变换结果:调整模型大小(缩放该模型),然后设置模型方向(旋转该模型),最后将模型移到所需的位置(平移该模型)。

    旋转示例


    指定了轴和角度的旋转非常适于静态变换和某些动画。但是,请考虑围绕 X 轴将立方体模型旋转 60 度,然后围绕 Z 轴将其旋转 45 度。您可以将这种变换描述为两个离散的仿射变换,也可以将其描述为一个矩阵。但是,对于按照这种方式定义的旋转,可能很难平滑地进行动画处理。尽管按照这两种方法计算的模型起始位置和结束位置相同,但是,从计算的角度来看,该模型经过的中间位置是不确定的。四元数提供了一种用来在旋转的起始位置和结束位置之间计算内插值的替代方法。

    四元数表示三维空间中的一个轴以及围绕该轴的旋转。例如,四元数可以表示 (1,1,2) 轴以及 50 度的旋转角度。四元数在定义旋转方面的价值在于可以针对它们执行的两个运算:合成和内插。应用于一个几何形状的两个四元数的合成是指“围绕 axis2 将几何形状旋转 rotation2 度,然后围绕 axis1 将其旋转 rotation1 度”。通过使用合成运算,可以将应用于几何形状的两个旋转合成在一起,以便获得一个代表合成结果的四元数。由于四元数内插可以计算出从一个轴和方向到另一个轴和方向的平滑而又合理的路径,因此您可以从原始位置到合成的四元数之间进行内插,以便实现从一个位置到另一个位置的平滑过渡,从而可以对该变换进行动画处理。对于要进行动画处理的模型,可以通过针对 Rotation 属性使用 QuaternionRotation3D 来为旋转指定目标 Quaternion。

    使用变换集合
    在生成场景时,通常会向模型应用多个变换。向 Transform3DGroup 类的 Children 集合中添加变换,从而方便地将多个变换组合在一起以应用于场景中的各种模型。通常,可以很方便地在几个不同的组中重用某个变换,这与通过向每个实例应用一组不同的变换来重用模型大体相同。请注意,将变换添加到该集合中的顺序至关重要:集合中的变换是按照从第一个到最后一个的顺序应用的。

    对变换进行动画处理
    Windows Presentation Foundation (WPF) 三维实现与二维图形参与同一个计时和动画系统。换言之,若要对三维场景进行动画处理,需要对其模型的属性进行动画处理。可以直接对基元的属性进行动画处理,但是通常很容易对用来更改模型位置或外观的变换进行动画处理。由于可以向 Model3DGroup 对象及其各个模型应用变换,因此可以向 Model3DGroup 的子级应用一组动画,向一组对象应用另一组动画。

    若要对 Windows Presentation Foundation (WPF) 中的对象进行动画处理,可以创建时间线、定义动画(实际上是随着时间的推移而更改某个属性值)并指定要向其应用动画的属性。此属性必须是 FrameworkElement 的属性。由于三维场景中的所有对象都是 Viewport3D 的子级,因此要应用于场景的任何动画所面向的属性都是 Viewport3D 属性的属性。一定要仔细设计动画的属性路径,因为语法可能会极为冗长。

    假设您希望就地旋转某个对象,而且还希望应用摆动动作以公开要查看的对象的更多内容。您可以选择向模型应用 RotateTransform3D,并对模型从一个向量旋转到另一个向量时所围绕的轴进行动画处理。下面的代码示例演示如何将 Vector3DAnimation 应用于该变换的 Rotation3D 的 Axis 属性,并假设 RotateTransform3D 是应用于具有 TransformGroup 的模型的几个变换之一。

    使用类似的语法可以将其他变换属性作为目标来移动或缩放该对象。 例如,可以在进行缩放变换时将 Point3DAnimation 应用于 ScaleCenter 属性,以便使模型的形状平滑地扭曲。

    尽管上面的几个示例对 GeometryModel3D 的属性进行变换,但是还可以对场景中其他模型的属性进行变换。 例如,可以通过对应用于 Light 对象的平移进行动画处理来创建移动灯光和阴影效果,这些效果可以显著改变模型的外观。

    由于照相机也是模型,因此也可以对照相机的属性进行变换。 尽管您确实可以通过改变照相机的位置或平面距离来改变场景的外观(实际上是变换整个场景的投影),但应注意,对于观察者来说,以这种方法实现的许多效果不如将变换应用于场景中模型的地点或位置更有“视觉意义”。

    1. WPF的动画开发

    Windows Presentation Foundation (WPF) 提供了一组强大的图形和布局功能,通过应用这些功能,可以创建漂亮的用户界面和吸引人的文档。动画不仅可以使漂亮的用户界面更加引人注目,还可以使其更加便于使用。只需对背景色进行动画处理或应用动画 Transform,就可以创造出生动的屏幕过渡效果或提供有帮助的视觉提示。

    本概述介绍了 WPF 动画和计时系统,并使用演示图板重点讨论 WPF 对象的动画。

    本主题包括以下部分。

    动画简介
    动画是快速播放一系列图像(其中每个图像与下一个图像略微不同)给人造成的一种幻觉。大脑感觉这组图像是一个变化的场景。在电影中,摄像机每秒钟拍摄许多照片(帧),便可使人形成这种幻觉。用投影仪播放这些帧时,观众便可以看电影了。

    计算机上的动画与此类似。例如,使一个矩形逐渐从视野中消失的程序可能按以下方式工作。

    程序创建一个计时器。

    程序按照设置的时间间隔检查计时器以查看经历了多长时间。

    程序每次检查计时器时,它将根据经历的时间来计算矩形的当前不透明度值。

    然后程序用新值更新矩形并重画此矩形。

    在 WPF 出现之前,Microsoft Windows 开发人员必须创建和管理自己的计时系统或使用特殊的自定义库。WPF 包括一个通过托管代码和 可扩展应用程序标记语言 (XAML) 公开的高效计时系统,该系统紧密地集成到 WPF 框架中。通过使用 WPF 动画,可以轻松地对控件和其他图形对象进行动画处理。

    WPF 可以高效地处理管理计时系统和重绘屏幕的所有后台事务。它提供了计时类,使用这些类,可以重点关注要创造的效果,而非实现这些效果的方法。另外,WPF 通过公开动画基类(您的类可以继承自这些类)让您可以轻松创建自己的动画,这样便可以制作自定义动画。这些自定义的动画获得了标准动画类的许多性能优点。

    WPF 属性动画系统
    如果了解关于计时系统的一些重要概念,则在使用 WPF 时可能会更加轻松一些。最重要的是,在 WPF 中,通过对对象的个别属性应用动画,可以对对象进行动画处理。例如,若要使框架元素增大,请对其 Width 和 Height 属性进行动画处理。若要使对象逐渐从视野中消失,可以对其 Opacity 属性进行动画处理。

    要使属性具有动画功能,属性必须满足以下三个要求:

    它必须是依赖项属性。

    它必须属于继承自 DependencyObject 并实现 IAnimatable 接口的类。

    必须存在可用的兼容动画类型.

    WPF 包含许多具有动画属性的对象。诸如 Button、TabControl 和 Panel 控件以及 Shape 对象都继承自 DependencyObject。它们的大多数属性都是依赖项属性。

    您几乎可以在任何地方使用动画,包括在样式和控件模板中使用。动画未必可见;对于不属于用户界面的对象,只要它们满足本节中所述的条件,便可以对其进行动画处理。

    示例:使元素逐渐进入视野并从视野中逐渐消失
    此示例演示如何使用 WPF 动画对依赖项属性的值进行动画处理。本示例使用 DoubleAnimation(一种生成 Double 值的动画类型)对 Rectangle 的 Opacity 属性进行动画处理。因此,Rectangle 将逐渐进入视野并逐渐从视野中消失。

    示例的第一部分创建一个 Rectangle 元素,并将其显示在 Page 中。下面的步骤表明如何创建动画并将其应用于矩形的 Opacity 属性。

    第 1 部分:创建 DoubleAnimation
    使元素逐渐进入视野并逐渐从视野中消失的一种方法是对其 Opacity 属性进行动画处理。由于 Opacity 属性的类型是 Double,因此需要一个产生双精度值的动画。DoubleAnimation 就是这样的一个动画。DoubleAnimation 创建两个双精度值之间的过渡。若要指定其起始值,可设置其 From 属性。若要指定其终止值,可设置其 To 属性。

    不透明度值 1.0 使对象完全不透明,不透明度值 0.0 使对象完全不可见。若要使动画的不透明度值从 1.0 过渡为 0.0,可以将其 From 属性设置为 1.0,将其 To 属性设置为 0.0。

    [C#]

    然后,必须指定 Duration。动画的 Duration 指定了从其起始值过渡为目标值所需的时间。在下面的示例中,为动画指定的持续时间为五秒钟。

    上面的代码显示了不透明度值从 1.0 向 0.0 转换的动画,此转换使目标元素从完全不透明逐渐转变为完全不可见。若要使元素在消失后再逐渐回到视野中,请将动画的 AutoReverse 属性设置为 true。若要使动画无限期地重复,请将其 RepeatBehavior 属性设置为 Forever。

    第 2 部分:创建演示图板
    若要向对象应用动画,请创建 Storyboard 并使用 TargetName 和 TargetProperty 附加属性指定要进行动画处理的对象和属性。

    创建 Storyboard 并将动画添加为其子项。

    在代码中,将 Storyboard 声明为类成员。

    然后初始化 Storyboard 并将动画添加为其子项。

    必须知道要在哪里应用动画。使用 Storyboard..::.TargetName 附加属性指定要进行动画处理的对象。在下面的代码中,为 DoubleAnimation 指定了一个目标名称 MyRectangle,这是要进行动画处理的对象的名称。

      

    说明:
    在代码中创建演示图板时,必须执行两个附加步骤:创建名称范围以及注册要进行动画处理的对象的名称。本节开头的代码创建了页面和矩形,还声明了 NameScope 并注册了矩形的名称。如果还没有 NameScope,则可以使用 SetNameScope 方法创建一个。您可以使用 RegisterName 方法向为其创建了 NameScope 的元素注册目标对象的名称。否则,Storyboard 无法找到要进行动画处理的对象。

    使用 TargetProperty 附加属性指定要进行动画处理的属性。在下面的代码中,动画被配置为面向 Rectangle 的 Opacity 属性。

    第 3 部分 (XAML):将演示图板与触发器关联
    在 XAML 中应用和启动 Storyboard 的最简单的方法是使用事件触发器。

    创建一个 BeginStoryboard 对象并将演示图板与其关联。BeginStoryboard 是一种应用和启动 Storyboard 的 TriggerAction。

    创建 EventTrigger 并将 BeginStoryboard 添加至其 Actions 集合。将 EventTrigger 的 RoutedEvent 属性设置为启动 Storyboard 所需的路由事件。

    将 EventTrigger 添加至矩形的 Triggers 集合。

    第 3 部分(代码):将演示图板与事件处理程序关联
    在代码中应用和启动 Storyboard 的最简单的方法是使用事件处理程序。

    注册矩形的 Loaded 事件。

    [C#]

    声明事件处理程序。在事件处理程序中,使用 Begin 方法来应用演示图板。

    [C#]

    完整的示例

    下面的示例演示了一段完整代码,这段代码用于创建逐渐进入视野并从视野中逐渐消失的矩形。

    动画类型
    由于动画生成属性值,因此对于不同的属性类型,会有不同的动画类型。若要对采用 Double 的属性(例如元素的 Width 属性)进行动画处理,请使用生成 Double 值的动画。若要对采用 Point 的属性进行动画处理,请使用生成 Point 值的动画,依此类推。由于存在许多不同的属性类型,因此 System.Windows.Media.Animation 命名空间中存在一些动画类。幸好它们都遵循严格的命名约定,因此可以轻松地区分它们:

    <类型>Animation

    这些动画称为“From/To/By”或“基本”动画,它们在起始值和目标值之间进行动画处理,或者通过将偏移量值与其起始值相加来进行动画处理。

    若要指定起始值,请设置动画的“From”属性。

    若要指定终止值,请设置动画的“To”属性。

    若要指定偏移量值,请设置动画的“By”属性。

    此概述中的示例使用这些动画,因为这些动画使用起来最简单。From/To/By 动画概述中详细描述了“From/To/By”动画。

    <类型>AnimationUsingKeyFrames

    关键帧动画的功能比“From/To/By”动画的功能更强大,因为您可以指定任意多个目标值,甚至可以控制它们的插值方法。某些类型的内容只能用关键帧动画进行动画处理。关键帧动画概述中详细描述了关键帧动画。

    <类型>AnimationUsingPath

    路径动画使您能够使用几何路径来生成动画值。

    <类型>AnimationBase

    在实现时对 <类型> 值进行动画处理的抽象类。此类用作 <类型>Animation 和 <类型>AnimationUsingKeyFrames 类的基类。只有在想要创建自己的自定义动画时,才必须直接处理这些类。否则,请使用 <类型>Animation 或 KeyFrame<类型>Animation。

    在大多数情况下,您将需要使用 <类型>Animation 类,例如 DoubleAnimation 和 ColorAnimation。

    下表显示了一些常用动画类型以及与这些类型一起使用的一些属性。

    动画是时间线
    所有动画类型均继承自 Timeline 类,因此所有动画都是专用类型的时间线。Timeline 定义时间段。您可以指定时间线的以下计时行为:其 Duration 和重复次数,甚至可以为时间线指定时间走得多快。

    因为动画是 Timeline,所以它还表示一个时间段。在动画的指定时间段(即 Duration)内运行动画时,动画还会计算输出值。在运行或“播放”动画时,动画将更新与其关联的属性。

    Duration、AutoReverse 和 RepeatBehavior 是三个常用的计时属性。

    Duration 属性
    如前文所述,时间线代表一个时间段。该时间段的长度由时间线的 Duration(通常用 TimeSpan 值来指定)来决定。当时间线达到其持续时间的终点时,表示时间线完成了一次重复。

    动画使用其 Duration 属性来确定其当前值。如果没有为动画指定 Duration 值,它将使用默认值(1 秒)。

    下面的语法显示了 Duration 属性 (Property) 的 可扩展应用程序标记语言 (XAML) 属性 (Attribute) 语法的简化版本。

    下表显示了一些 Duration 设置及其结果值。

    在代码中指定 Duration 的一种方法是使用 FromSeconds 方法创建 TimeSpan,然后使用该 TimeSpan 声明新的 Duration 结构。

    AutoReverse
    AutoReverse 属性指定时间线在到达其 Duration 的终点后是否倒退。如果将此动画属性设置为 true,则动画在到达其 Duration 的终点后将倒退,即从其终止值向其起始值反向播放。默认情况下,该属性为 false。

    RepeatBehavior
    RepeatBehavior 属性指定时间线的播放次数。默认情况下,时间线的重复次数为 1.0,即播放一次时间线,根本不进行重复。

    对属性应用动画
    前面几节描述动画的不同类型及其计时属性。本节演示如何对要进行动画处理的属性应用动画。Storyboard 对象提供了对属性应用动画的一种方法。Storyboard 是一个为其所包含的动画提供目标信息的容器时间线。

    以对象和属性为目标
    Storyboard 类提供 TargetName 和 TargetProperty 附加属性。通过在动画上设置这些属性,您将告诉动画对哪些内容进行动画处理。但是,通常必须为对象提供一个名称,动画才能以该对象作为处理目标。

    为 FrameworkElement 分配名称不同于为 Freezable 对象分配名称。大多数控件和面板是框架元素;而大多数纯图形对象(如画笔、转换和几何图形)是可冻结的对象。

    若要使 FrameworkElement 成为动画目标,应通过设置其 Name 属性为其提供一个名称。在代码中,还必须使用 RegisterName 方法向元素名称所属的页面注册该元素名称。

    要使用 XAML 使 Freezable 对象成为目标,应使用 x:Name 属性为其分配一个名称。在代码中,只需使用 RegisterName 方法向对象所属的页面注册该对象。

    下面几节举例说明如何使用 XAML 和代码为元素命名。

    应用和启动演示图板
    若要使用 XAML 启动演示图板,应将其与 EventTrigger 关联。EventTrigger 是一个描述在发生指定事件时执行哪些操作的对象。这些操作中的一个操作可以是 BeginStoryboard 操作,您可以使用此操作启动演示图板。事件触发器与事件处理程序的概念类似,因为通过使用它们,您都可以指定应用程序如何响应特定事件。与事件处理程序不同的是,您完全可以使用 XAML 来描述事件触发器,而无需使用其他代码。

    若要使用代码启动 Storyboard,您可以使用 EventTrigger 或使用 Storyboard 类的 Begin 方法。

    以交互方式控制演示图板
    前面的示例演示如何在事件发生时启动 Storyboard。您也可以在 Storyboard 启动后以交互方式控制它:可以暂停、继续和停止它,并可以使其进入到其填充期,还可以查找和移除 Storyboard。

    动画结束后会发生什么情况?
    FillBehavior 属性指定时间线结束时的行为方式。默认情况下,时间线结束时将启动 Filling。处于 Filling 状态动画将保持其最终输出值。

    前面示例中的 DoubleAnimation 不会终止,因为它的 RepeatBehavior 属性设置为 Forever。下面的示例使用类似的动画对矩形进行动画处理。与前面的示例不同,此动画的 RepeatBehavior 和 AutoReverse 属性设为默认值。因此,动画运行五秒钟使其不透明度值从 1 转变成 0,然后停止。

    因为动画的 FillBehavior 未发生变化,仍然为其默认值 HoldEnd,所以当动画结束时动画将保持其最终值 0。因此,在动画结束后,矩形的 Opacity 仍然为 0。如果将矩形的 Opacity 设置为另一个值,则您的代码似乎无效,因为动画仍将影响 Opacity 属性。

    在代码中重新获得对动画属性的控制的一种方法是使用 BeginAnimation 方法,并将 AnimationTimeline 参数指定为 null。

    请注意,虽然设置一个具有 Active 或 Filling 动画的属性值好像不起作用,但属性值确实发生了变化。

    对动画进行数据绑定和动画处理
    您可以对大多数动画属性绑定数据或进行动画处理;例如,可以对 DoubleAnimation 的 Duration 属性进行动画处理。但是,由于计时系统工作方式的缘故,绑定有数据或进行过动画处理的动画的行为与其他绑定有数据或进行过动画处理的对象的行为不同。若要了解它们的行为,请了解对属性应用动画的意义,这将十分有用。

    加载前面示例中的矩形时,它的事件触发器将应用 Storyboard。计时系统会创建 Storyboard 及其动画的副本。系统将冻结这些副本(使它们成为只读副本),并且将根据它们来创建 Clock 对象。这些时钟将执行对目标属性进行动画处理的实际工作。

    计时系统为 DoubleAnimation 创建一个时钟,并将该时钟应用于 DoubleAnimation 的 TargetName 和 TargetProperty 所指定的对象和属性。在本例中,计时系统将时钟应用于名为“MyRectangle”的对象的 Opacity 属性。

    虽然也会为 Storyboard 创建一个时钟,但未对任何属性应用该时钟。该时钟的用途是控制其子时钟(为 DoubleAnimation 创建的时钟)。

    若要使动画反映数据绑定或动画更改,必须重新生成其时钟。系统不会为您自动生成时钟。若要使动画反映更改,请使用 BeginStoryboard 或 Begin 方法重新应用其演示图板。当使用其中的任何一种方法时,动画将重新启动。在代码中,可以使用 Seek 方法将演示图板移回到其从前的位置。

    其他动画处理方式
    此概述中的示例演示如何使用演示图板进行动画处理。如果使用代码,则可以采用一些其他方法进行动画处理。

    动画示例
    以下示例可以帮助您开始向应用程序添加动画。

    属性动画示例

    演示如何对框架元素和可冻结的对象应用动画。

    From/To/By 动画的目标值示例

    演示不同的 From/To/By 设置。

    动画计时行为的示例

    演示可控制动画计时行为的不同方法。此示例还演示如何绑定动画目标值数据。

    1. WPF的多媒体开发

    本主题将介绍 Windows Presentation Foundation (WPF) 的多媒体功能。使用多媒体功能可以将声音和视频集成到应用程序中,从而增强了用户体验。

    媒体 API
    MediaElement 和 MediaPlayer 用于播放音频、视频以及包含音频内容的视频。这两种类型都可以以交互方式或时钟驱动方式进行控制。这两种类型都至少依赖 Microsoft Windows Media Player 10 OCX 进行媒体播放。但这两种 API 的用法因具体情况而异。

    MediaElement 是一个 UIElement,它受 布局系统 支持并可用作许多控件的内容。它也可用在可扩展应用程序标记语言 (XAML) 以及代码中。另一方面,MediaPlayer 用于 Drawing 对象,因而缺少对布局的支持。只能使用 VideoDrawing 或通过直接与 DrawingContext 进行交互来呈现使用 MediaPlayer 加载的媒体。不能在 XAML 中使用 MediaPlayer。

    说明:
    如果将媒体与应用程序一起分发,则不能将媒体文件用作项目资源。在项目文件中,必须将媒体类型改设为 Content,并将 CopyToOutputDirectory 设置为 PreserveNewest 或 Always。

    媒体播放模式
    说明:
    MediaElement 和 MediaPlayer 具有类似的成员。本部分中的链接指的是 MediaElement 类成员。除非明确说明,否则链接到 MediaElement 类中的成员也可在 MediaPlayer 类中找到。

    若要了解 Windows Presentation Foundation (WPF) 中的媒体播放,需要先了解可播放媒体的不同模式。MediaElement 和 MediaPlayer 可以用于两种不同的媒体模式中:独立模式和时钟模式。媒体模式由 Clock 属性确定。如果 Clock 为 null,则媒体对象处于独立模式。如果 Clock 不为 null,则媒体对象处于时钟模式。默认情况下,媒体对象处于独立模式。

    独立模式
    在独立模式下,由媒体内容驱动媒体播放。独立模式实现了下列功能选项:

    可直接指定媒体的 Uri。

    可直接控制媒体播放。

    可修改媒体的 Position 和 SpeedRatio 属性。

    通过设置 MediaElement 对象的 Source 属性或者调用 MediaPlayer 对象的 Open 方法来加载媒体。

    若要在独立模式下控制媒体播放,可使用媒体对象的控制方法。提供了下列控制方法:Play、Pause、Close 和 Stop。对于 MediaElement,仅当将 LoadedBehavior 设置为 Manual 时,使用这些方法的交互式控件才可用。当媒体对象处于时钟模式时,这些方法将不可用。

    时钟模式
    在时钟模式下,由 MediaTimeline 驱动媒体播放。时钟模式具有下列特征:

    媒体的 Uri 是通过 MediaTimeline 间接设置的。

    可由时钟控制媒体播放。不能使用媒体对象的控制方法。

    可通过以下方法加载媒体:设置 MediaTimeline 对象的 Source 属性,从时间线创建时钟,并将时钟分配给媒体对象。当位于 Storyboard 中的 MediaTimeline 针对 MediaElement 时,也可用这种方法加载媒体。

    若要在时钟模式下控制媒体播放,必须使用 ClockController 控制方法。ClockController 是从 MediaClock 的 ClockController 属性获取的。如果尝试在时钟模式下使用 MediaElement 或 MediaPlayer 对象的控制方法,则会引发 InvalidOperationException。

    MediaElement
    向应用程序添加媒体的操作十分简单,只需向应用程序的用户界面 (UI) 添加 MediaElement 控件,并为要包含的媒体提供 Uri。Windows Presentation Foundation (WPF) 中支持 Microsoft Windows Media Player 10 所支持的所有媒体类型。下面的示例演示 MediaElement 在可扩展应用程序标记语言 (XAML) 中的简单用法。

    在此示例中,媒体在加载后即会自动播放。播放完后,就会关闭媒体,并且会释放所有媒体资源(包括视频内存)。此行为是 MediaElement 对象的默认行为,由 LoadedBehavior 和 UnloadedBehavior 属性控制。

    控制 MediaElement
    当 IsLoaded 为 true 或 false 时,可分别使用 LoadedBehavior 和 UnloadedBehavior 属性控制 MediaElement 的行为。设置 MediaState 属性的目的是影响媒体播放行为。例如,默认的 LoadedBehavior 为 Play,而默认的 UnloadedBehavior 为 Close。这意味着加载 MediaElement 并完成预播放后,即会开始播放媒体。播放完后,就会关闭媒体,并且会释放所有媒体资源。

    LoadedBehavior 和 UnloadedBehavior 属性不是控制媒体播放的唯一方法。在时钟模式下,时钟可以控制 MediaElement,并且这些交互式控制方法在 LoadedBehavior 为 Manual 时具有控制权。MediaElement 通过计算下列优先级来处理此控制权的竞争。

    UnloadedBehavior. 在卸载媒体时发生。这可确保默认情况下释放所有媒体资源,即使 MediaClock 与 MediaElement 关联也是如此。

    MediaClock. 在媒体具有 Clock 时发生。如果卸载媒体,则只要 UnloadedBehavior 为 Manual,MediaClock 就会生效。时钟模式始终重写 MediaElement 的加载行为。

    LoadedBehavior. 在加载媒体时发生。

    交互式控制方法。在 LoadedBehavior 为 Manual 时发生。提供了下列控制方法:Play、Pause、Close 和 Stop。

    显示 MediaElement
    若要显示 MediaElement,它必须具有要呈现的内容,并在加载内容之前将其 ActualWidth 和 ActualHeight 属性设置为零。对于仅包含音频的内容,这些属性将始终为零。对于视频内容,在 MediaOpened 事件引发 ActualWidth 和 ActualHeight 后,会报告已加载媒体的大小。这意味着在加载媒体之前,MediaElement 不会占用用户界面 (UI) 中的任何物理空间,除非设置了 Width 或 Height 属性。

    如果设置 Width 和 Height 属性,则会导致拉伸媒体来填充为 MediaElement 提供的区域。若要保持媒体的原始纵横比,应设置 Width 或 Height 属性,但不能同时设置这两者。如果同时设置 Width 和 Height 属性,则会使媒体以固定元素大小显示,可能无法达到预期效果。

    为避免元素大小固定,Windows Presentation Foundation (WPF) 可以预播放媒体。为此,需要将 LoadedBehavior 设置为 Play 或 Pause。在 Pause 状态下,媒体将预播放第一帧。在 Play 状态下,媒体将预播放,然后再开始播放。

     MediaPlayer
    当 MediaElement 类为框架元素时,MediaPlayer 类设计为在 Drawing 对象中使用。在可牺牲框架级功能来获得性能的提高或者需要 Freezable 功能时,可使用 Drawing 对象。通过 MediaPlayer,您可以在应用程序中提供媒体内容,同时使用这些功能。与 MediaElement 类似,MediaPlayer 可在独立模式或时钟模式下使用,但不具有 MediaElement 对象的卸载和加载状态。这会降低 MediaPlayer 的播放控制的复杂程度。

    控制 MediaPlayer
    由于 MediaPlayer 是无状态的,因此只能使用两种方法控制媒体播放。

    交互式控制方法。在处于独立模式(null Clock 属性)时采用。

    MediaClock. 在媒体具有 Clock 时采用。显示 MediaPlayer
    从技术角度来说,不能显示 MediaPlayer,因为它没有物理表示形式。但它可用于通过使用 VideoDrawing 类在 Drawing 中呈现媒体。下面的示例演示如何使用 VideoDrawing 显示媒体。

    展开全文
  • 为了帮助用户了解应用当前要做什么,也给用户的下一步行为做参考,以及了解操作后所产生的结果 ,当用户和系统需要交互时,使用不同的模式来反馈信息或结果。当设计者使用反馈或者自定义一些反馈时,请注意:为用户...
  • 图标的使用图标能给人带来最直观的印象,有的时候再多的解释也抵不过一个图标。安卓应用的图标分成两类, 应用图标:在主界面上代表着整个应用的那个启动图标,是给用户第一印象的图标。一个应用开发成功后会不会被...
  • 心理辅导平台设计

    千次阅读 2017-12-04 10:22:57
    心理咨询是以维护人的心理健康状态为目标和内容的一项工作,这项工作要求借助于一种特殊的人际关系,运用心理学的理论知识和方法,通过言语、文字及其他信息传递方式,就咨询对象的心理方面存在的问题,提供帮助、...
  • 反馈样式

    2019-05-08 10:00:49
    反馈样式 1.什么是反馈 反馈是用户在有目的性的前提下进行操作时,应用所给出的信息为结果信息,这样的形式称之为“反馈...反馈机制建立的必要性:帮助用户随时感知系统的状态,满足用户的控制感,消减不确定性给用...
  • 图标其实存在于界面中的许多地方,今天我就带大家来聊一聊标签栏中的关键元素——图标。但因为是主要分析标签栏,所以我会借标签栏中较主流的图标样式,总结一套标签栏图标设计规范。这些方法在图标制作过程中都是...
  • 解决Windows反馈中心高级诊断无法记录的问题以下是解决办法 去微软社区请教了社区的工作人员,根据他们的操作叫我使用Dism和sfc命令修复电脑,重启后再试,还是下图这个错误。最后是我自己突然想起了很早之前设置了...
  • 此网络应用程序首先简要介绍关于我们部分和服务,然后当您单击三个我们所做的图标时,每次单击时都会切换消息以进一步演示工作室的工作。它还创建了之前的投资组合区域项目,这是通过将光标悬停在每个图片上以显示每...
  • App Store应用图标下载工具

    千次阅读 2019-05-04 21:00:00
    自十年前 App Store 正式推出之时起,在每个程序包(.IPA)里都有应用图标文件,有各种规格用于适配 iPhone、iPad 以及高清屏等不同版本。除此之外在正式...
  • 如果帮助到你了,请帮忙点亮右上角小星星,这将是我持续更新的动力! 功能 批量生成Android、iOS、Mac、Windows的要求的图片 自定义生成位置 通过模板文件自定义批量指定图片 播放音效 下载 mac、window、linux ...
  • 现实生活一致:现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div> <div>在界面中一致:所有的元素和结构需保持一致,比如:设计样式、图标和文本、元素的位置等。</div&
  • 有关Windows10中诊断和反馈隐私设置

    千次阅读 2019-03-04 22:33:08
    当你使用 Windows 时,我们将收集诊断信息,为了确保能收到你(我们的客户)的反馈,我们为你提供了多种方式,以便你可以随时发送反馈,也可以在某个特定的时间(例如当 Windows 10 向你提出关于某项功能、服务等的...
  • 该工具可以帮助硬件工程师快速的选出电源输出反馈电阻的组合,节约时间,也可以帮助检视电路中电源反馈电阻的正确性 工具图标如下: 打开后界面如下: 该工具有两种功能: 1、根据电路上使用的反馈电阻...
  • 系统Windows10 故障现象:桌面程序logo被白块挡住了大部分 ... 你的反馈很重要!! 如果故障已解决,请在评论回复已解决; 如果故障未解决,请在评论里说明卡在哪个故障点,说出来这样CSDN社区成员方便互相帮助解决。
  • 先说一下我的遇到的情况:之前一直用WIN10 1903,后来被强行升级到最新的20H2版本,强行升级也就算了,但在使用中马上发现2个问题,一是win10网络图标变成地球加禁止符号但能上网,二是WIN10输入发管理器没有了,但...