ItemsControl 的来龙去脉

ItemsControl 的来龙去脉,第1张

概述转自:http://msdn.microsoft.com/zh-cn/magazine/ff714594.aspx ItemsControl 的来龙去脉 Charles Petzold 下载代码示例 如果有人问我,哪个类最能概括 Windows Presentation Foundation (WPF) 和 Silverlight 的强大功能和灵活性,我首先会说这是一个很愚蠢的问题,然后会毫不犹豫 转自:http://msdn.microsoft.com/zh-cn/magazine/ff714594.aspx ItemsControl 的来龙去脉 Charles Petzold

下载代码示例

如果有人问我,哪个类最能概括 windows Presentation Foundation (WPF) 和 Silverlight 的强大功能和灵活性,我首先会说这是一个很愚蠢的问题,然后会毫不犹豫地回答“DataTemplate”。

DataTemplate 基本上是元素和控件的可视树。程序员使用 DataTemplate 可向非可视数据对象提供可视外观。可视树中元素的属性通过绑定链接到数据对象的属性。尽管 DataTemplate 最常用于定义 ItemsControl 或 ListBox(从 ItemsControl 中派生的类之一)中的对象外观,但您也可以使用 DataTemplate 将对象集的外观定义为 ContentControl 或 ContentControl 派生物(如按钮)的 Content 属性。

创建 DataTemplate(或其他任何类型的 FrameworkTemplate 派生物,如 ControlTemplate 或 HIErarchicalDataTemplate)是无法通过代码完成的少数 Silverlight 编程任务之一。您需要使用 XAML。曾经可以使用 Framework-ElementFactory 完全在代码中创建 WPF 模板,但我认为我是实际发布了示例的唯一一人(在我的《Applications = Code + Markup》”[Microsoft Press,2006 年] 一书中的第 11、13 和 16 章中),不过现在已弃用了这种方法。

我在本文中要向您演示拖放的一种变体:用户只需在各个 ItemsControl 之间移动项。但我的主要目标是使实现的这一整个过程具有看似自然的完全流畅外观,其中不存在突然出现或消失的内容。当然,要获得“自然的外观”通常会颇费周折,努力实现流畅性的任何程序都需要避免暴露表面下隐藏的繁琐方法。

我将组合使用在上个月的专栏(“突破框架思考”)中介绍的各种技术,以及在两个 ItemsControl 和一个 ContentControl 之间共享的 DataTemplate — 这是此整个程序的基本概念。

程序布局

本文随附的可下载代码包含一个名为 ItemsControlTransitions 的 Silverlight 项目,您可以从我的网站 charlespetzold.com/silverlight/ItemsControlTransitions2 运行该项目。(我稍后将解释此 URL 结尾处的“2”的含义。)您可以在 WPF 程序中使用此 Silverlight 程序所展示的相同概念。

该程序显示包含在 ScrollVIEwers 中的两个 ItemsControl。您可以将左侧的 ItemsControl 想象为“市场”销售的农产品。右侧的 ItemsControl 是您的“篮子”。您可以使用鼠标从市场中选取农产品项并将其移到篮子中。图 1 显示正从市场过渡到篮子的 Corn 项。


图 1 ItemsControlTransitions 显示

虽然 Corn 项已移出市场,但请注意 ItemsControl 中的间隙,这仍可指示该项的来源。如果用户在将所拖动的项放入篮子 Items-Control 之前释放鼠标按钮,则程序将以动画效果演示该项返回到市场中的过程。仅当将项放入篮子时,该间隙才会关闭(也用动画效果显示)。根据项的放置位置,会打开一个动画间隙以接收该项,并且该项会以动画形式放置到位。

将某个项移出市场后,市场中便不再存在该项,但可以方便地更改这一程序细节。不存在可用于从篮子中删除项再将其移回市场的工具,但是也可以轻松地添加该功能或类似功能。

图 2 显示了负责基本布局的 XAML 文件的大部分内容。(缺少包含七个动画的两个情节提要,稍后我将介绍这些内容。)

图 2 负责基本布局的部分 XAML 文件

复制代码
<UserControl x:Class="ItemsControlTransitions.MainPage"     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  name="this">    <UserControl.Resources>      <DataTemplate x:Key="produceDataTemplate">        <border WIDth="144"          Height="144"          borderBrush="Black"          borderThickness="1"          Background="AliceBlue"          margin="6">          <GrID>            <GrID.RowDeFinitions>              <RowDeFinition Height="*" />              <RowDeFinition Height="auto" />            </GrID.RowDeFinitions>            <Image GrID.Row="0"              Source="{Binding Photo}" />            <TextBlock GrID.Row="1"              Text="{Binding name}"              HorizontalAlignment="Center" />          </GrID>        </border>      </DataTemplate>        ...            </UserControl.Resources>    <GrID x:name="LayoutRoot" Background="White">        <ScrollVIEwer HorizontalAlignment="left"          margin="48">            <ItemsControl name="market"              ItemTemplate="{StaticResource produceDataTemplate}"                WIDth="156"                MouseleftbuttonDown="OnMarketItemsControlMouseleftbuttonDown" />        </ScrollVIEwer>        <ScrollVIEwer HorizontalAlignment="Right"          margin="48">            <ItemsControl name="basket"              ItemTemplate="{StaticResource produceDataTemplate}"            WIDth="156" />        </ScrollVIEwer>        <Canvas name="dragCanvas">          <ContentControl name="dragControl"            ContentTemplate="{StaticResource produceDataTemplate}"            Visibility="Collapsed" />        </Canvas>    </GrID></UserControl>

Resources 节包含一个用于显示农产品项的 DataTemplate,对此资源的引用设置为两个 ItemsControl 的 Itemstemplate 属性。

此外,还有一个 Canvas 覆盖程序占用的整个区域。您可以回想一下在上个月的专栏中,我们如何使用 Canvas 来承载需要在 UI 的其余部分之上“浮动”的项。此 Canvas 的唯一子级是 ContentControl,其 ContentTemplate 也设置为该 DataTemplate。但 Visibility 属性设置为 Collapsed,因此此 ContentControl 最初不可见。

派生自 ContentControl 的控件在 WPF 和 Silverlight 应用程序中十分常见,但通常您看不到 ContentControl 本身。如果您只需使用 DataTemplate 显示对象,则会非常方便。从外观上看,它非常类似于 ItemsControl 中的单个项。

程序首先加载包含一些农产品的小型 XML 数据库(使用上个月专栏的 ItemsControlPopouts 项目中的相同文件),然后使用类型为 ProduceItem 的对象填充市场 ItemsControl。此类具有 name 和 Photo 属性,DataTemplate 会引用这些属性来显示每个项。

从 ItemsControl 中拉动项

市场的 ItemsControl 为 MouseleftbuttonDown 设置了处理程序。接收到此事件时,程序需要从 ItemsControl 的四面边界中移出一个项,并允许该项随鼠标移动。但不能实际从 ItemsControl 中移除该项,否则间隙会自动关闭。

正如我在上个月的专栏中所演示的,您可以通过访问 ItemsControl 的 ItemContainerGenerator 属性获取一个类,该类可将 ItemsControl 中的每个项与为显示该特定项而生成的可视树关联。此可视树有一个类型为 ContentPresenter 的根元素。

我的第一个灵感是将 Translatetransform 应用于 ContentPresenter 的 Rendertransform 属性,使其可以浮动在 ItemsControl 之外。但根据我的经验判断,这样做根本不起作用。问题不在于 ItemsControl 本身,而在于 ScrollVIEwer,它必须将其子级剪辑到其内部。(我稍后将详细介绍此剪辑的基本原理。)

程序改为将 ItemsControl 中单击的 ProduceItem 复制到 ContentControl,并将 ContentControl 准确置于所单击项的 ContentPresenter 之上。(程序可以使用总是十分方便的 transformToVisual 方法,来获取 ContentPresenter 相对于 Canvas 的位置。)您会回想起 XAML 文件将 ContentControl 的 Visibility 属性设置为 Collapsed,但程序现在将该属性切换为 Visible。

同时,会使 ItemsControl 中的 ContentPresenter 不可见。在 WPF 中,只需将 Visibility 属性设置为 HIDden 即可实现此目的,这会使项不可见,但在另一方面却可观察元素的大小,以进行布局。Silverlight 中的 Visibility 属性没有 HIDden 选项,如果将 ContentPresenter 的 Visibility 属性设置为 Collapsed,则会关闭间隙。只需将 Opacity 属性设置为零,即可模拟将 Visibility 设置为 HIDden。元素仍然存在,但是不可见。在试验该程序时,您会发现感觉不到从 ItemsControl 中的项到可拖放 ContentControl 的转换。

此时,ItemsControl 中的 ContentPresenter 除了一个空洞外,不显示任何内容,ContentControl 显示现在可以使用鼠标在屏幕中四处拖动的项。

项放置

当初我在撰写有关 Win16 和 Win32 API 的书籍时,曾用整章篇幅演示如何使用滚动条在窗口中显示超过窗口范围的文本。而现在 ScrollVIEwer 即可轻松搞定,它使大家都感到轻松 — 尤其是我。

除了在 WPF 和 Silverlight 布局中的基本角色外,有时还可以采用复杂一些的方法使用 ScrollVIEwer。它的一些独特性可能有点令人迷惑,此程序揭示了其中一个独特性。看看您是否可以预计到该问题。

我们让用户使用鼠标在屏幕中四处移动某个农产品项。如果用户将该农产品项放置在表示篮子的 ItemsControl 上的某个位置,则该项将会成为该集合的一部分。(我稍后将详细介绍此过程。)否则,程序会在 MainPage.xaml 的 returnToOriginStoryboard 中使用两个动画演示该项返回原始位置的过程。在动画结束时,ContentPresenter 的 Opacity 属性会设置为 1,ContentControl 的 Visibility 属性会设置为 Collapsed,拖动事件结束时所有内容都会恢复正常。

为了确定是否将农产品项放置到 ItemsControl 上,程序会计算一个表示所拖动 ContentControl 的位置和大小的 Rect 对象,以及另一个表示 ItemsControl 的位置和大小的 Rect 对象。对于这两个对象,程序均使用 transformToVisual 方法获取控件左上角(点 (0,0))相对于页面的位置,并使用 ActualWIDth 和 ActualHeight 属性获取控件大小。Rect 结构的 Intersect 方法随后会计算两个矩形的交集,如果存在某些重叠,则该交集为非空。

除了 ItemsControl 的项多于其允许的垂直空间中可以容纳的项时,这种方法都适用。ScrollVIEwer 随后开始运行,使其垂直滚动条可见,以便您可以滚动访问各个项。但是,ScrollVIEwer 内的 ItemsControl 实际上认为自己大于所显示的大小;事实上,ScrollVIEwer 仅在 ItemsControl 上提供一个可查看窗口(称为“视区”)。您为该 ItemsControl 获取的位置和大小信息始终指示完整大小(称为“范围”大小),而不是视区大小。 

因此,ScrollVIEwer 需要剪辑其子级。如果您已经使用了一段时间的 Silverlight,则可能会特别习惯与子级剪辑有关的某种灵活性。您几乎总是可以使用 Rendertransform 来规避父级的边界。但是,ScrollVIEwer 明确需要剪辑,否则无法正常工作。

这意味着,不能使用 ItemsControl 的外观尺寸来确定有效放置,因为在有些情况下,ItemsControl 会延伸到 ScrollVIEwer 之上或之下。因此,我的程序会基于 ItemsControl 的水平尺寸(因为它要排除滚动条占用的区域)而不是 ScrollVIEwer 的垂直尺寸来确定有效的放置矩形。

将 ContentControl 放置在 ItemsControl 上时,它可能与两个现有项或只与一个现有项(如果将其放置在项堆栈的顶部或底部)重叠,也可能不与任何项重叠。我要在最接近放置位置的地方插入新项,这需要枚举 ItemsControl 中的项(及其关联的 ContentPresenter 对象)并确定合适的索引来插入新项。(GetBasketDestinationIndex 方法负责确定此索引。)插入项后,与该新项关联的 ContentPresenter 的初始高度和不透明度都设置为零,因此该项最初不可见。

执行此插入后,程序会启动一个名为 transferToBasketStoryboard 的情节提要,其中包含五个动画:一个动画用于减小市场 ItemsControl 中的不可见 ContentPresenter 的高度;另一个动画用于增加在篮子 ItemsControl 中新创建的不可见 ContentPresenter 的高度;还有两个动画用于动态实现 Canvas.left 和 Canvas.top 附加属性,以将 ContentControl 滑动到位。(我稍后会讨论第五个动画。)图 3 显示间隙随着 ContentControl 不断接近其目标而逐渐加宽。


图 3 将新项移动到位的动画

在动画结束时,新 ContentPresenter 的不透明度设置为 1,ContentControl 的可见性设置为 Collapsed,现在我们只需回头处理 ScrollVIEwer 中的两个普通 ItemsControl。

顶部和底部问题

在本文前面部分,我为您提供了 URL charlespetzold.com/silverlight/ItemsControlTransitions2,以供试用程序。可从 charlespetzold.com/silverlight/ItemsControlTransitions(结尾没有“2”)运行早期版本的程序。使用此早期版本时,将若干个农产品项移动到篮子中(足以显示垂直滚动条)。现在拖动另一个项,并在跨越 ScrollVIEwer 的底部处放置该项。释放鼠标按钮时,ContentControl 会向下朝着 ItemsControl 的一个不可见区域移动,然后突然消失。该项已正确插入(可通过向下滚动来进行验证),但效果并不十分美观。

现在滚动 ScrollVIEwer 使顶部项仅部分可见。从篮子中移动另一个项并将其放置在插入顶部项之前的位置处。新项会滑入 ItemsControl,但并不完全可见。这不如 ItemsControl 底部问题那么糟糕,但仍需要一些帮助。

如何解决?需要通过某种方法以编程方式滚动 ScrollVIEwer。通过 VerticalOffset 属性提供 ScrollVIEwer 的当前有效的垂直滚动量。此数字是从整个 ItemsControl 的顶部到 ScrollVIEwer 顶部显示的控件位置的正偏移。

只以动画效果实现该 VerticalOffset 属性不是很好吗?不幸的是,只有 get 访问器是公共的。幸运的是,可以通过编程方式来滚动 ScrollVIEwer,但需要调用一个名为 ScrollToVerticalOffset 的方法。

为了通过 Silverlight 动画功能完成此小型滚动作业,我在 MainPage 本身中定义了一个名为 Scroll 的依赖属性。在 XAML 文件中,我将页面命名为“this”,并在 transferToBasketStoryboard 中定义了针对此属性的第五个动画:

复制代码
<DoubleAnimation x:name="scrollitemsControlAnima"                 Storyboard.Targetname="this"                 Storyboard.TargetProperty="Scroll" />

OnMouseleftbuttonUp 覆盖会计算此动画的 From 和 To 值。(可以通过注释掉以注释“Calculate ScrollVIEwer scrolling animation”开头的代码块,来比较此附加动画的效果。)在以动态效果实现此 Scroll 属性时,其属性更改处理程序会使用动画值来调用 ScrollVIEwer 的 ScrollToVerticalOffset 方法。

发展为流畅的 UI

许多年以前,计算机的速度比现在慢得多,屏幕中没有什么令人惊奇的内容。现在,程序实现的 UI 可在眨眼之间完全改变其外观。但这仍然不能令人满意。通常,我们甚至看不到所发生的情况,因此现在需要故意降低 UI 的速度,使过渡更为流畅和自然。Silverlight 4 引入了一些“流畅的 UI”功能(我十分渴望讨论这些功能),但即使是在 Silverlight 3 中,也可以朝着这个方向开始旅程。    

Charles Petzold 是《MSDN 杂志》的长期特约编辑。他当前正在撰写《Programming windows Phone 7 SerIEs》,该书将在 2010 年秋季作为可免费下载的电子书发布。现在,已通过其网站 charlespetzold.com 提供了预览版本。

总结

以上是内存溢出为你收集整理的ItemsControl 的来龙去脉全部内容,希望文章能够帮你解决ItemsControl 的来龙去脉所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/web/1027779.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存