WPF自定义集合控件概述与遇到的问题

WPF中涉及到控件,那么就不可能绕过Template。首先咱们来看一下WPF中基础集合控件ItemsControl涉及到的几个Template。

  1. ItemsControl自身的Template,类型为ControlTemplate,其内部声明了一个ItemsPresenter用以呈现下述第2个Template。当我们为集合控件赋予自定义模板时,ItemsPresenter不可丢弃,否则就失去了集合控件的意义;
  2. ItemsPanel,类型为ItemsPanelTemplate,其内容一般为可拥有多个子项的布局控件,如StackPanel、Grid等;
  3. ItemTemplate,类型为DataTemplate,呈现各数据项。

此时有人会问,数据项对应的类型是ControlTemplate的Template跑哪里去了?说的是呀,话说只要是继承自Control的控件都应该有这个Template属性,你看ItemsControl不就有吗。那么与各子项对应的这个属性应该怎么获取和设置呢。ItemsControl并没有提供给我们直接访问子项Template属性的方法,而是给了另一个属性ItemContainerStyle。ItemContainerStyle对应子项控件的Style,因此我们可以通过该Style设置子项控件的Template(上述第3条实际上对应子项控件Template内的ContentPresenter)。ItemContainerStyle对于ItemsControl是设置ContentPresenter的样式,不过ContentPresenter并没有Template属性,但是对于ListBox来说,它设置的是ListBoxItem的Style。

关于为什么wpf不给我们直接设置子项控件Template的方法,我没有特地研究过,不过我想微软的开发人员是这么想的:也许有人希望在保持原先默认的Template,对其它属性做一些掌控,比如可见性,那么何不如直接给你们设置所有样式的机会呢?(上帝说,要有光。于是就有了光。我等P民感恩戴德。)

WPF灵活地样式自定义给了开发人员极大的便利,同时也带来了可能破坏控件内在逻辑的问题。如何做到设计和逻辑分离,周大牛在WPF中自定义控件(3) CustomControl说的很详细,不过由于该博文“年代久远”,因此不排除有过时的内容。

原本我打算自定义一个名为UserAddDelItemsControl的集合控件,该控件继承自ItemsControl,为用户提供自增删子项的功能,但进行到一半的时候发现它的ItemsSource属性给我造成了相当大的麻烦。ItemsSource != null即有数据源绑定时,我要去判断可能的数据源类型,谢天谢地,大部分实体数据源都继承自IList,该接口有我期望的Insert、Remove等方法。但是假设我Insert一个新数据项(注意此处是数据项)的时候,我要不要去刷新界面,以及如何去刷新界面以反映后台数据列表的更改呢,所以我又要去判断数据源是否继承自INotifyCollectionChanged。假如它没有继承自INotifyCollectionChanged,那么我该怎么办?或许我该使用 ICollectionView.Refresh()方法?或许最终可以实现,但这样的控件并不能使我满意。我意识到要实现一个优良的可增删项的集合控件的真正重点和难点是需要重写ItemsSource属性,甚至要为其自定义一套数据源处理逻辑。水太深鸟!时间不多,我只能暂时放弃。

不过该项工作并没有白做,除自定义控件的知识外,重温并加深了关于Command、VisualState(4.0,可以理解为Trigger的升级版)的知识。

  1. RoutedCommand,可理解为事件event,或者更本质上,其实是一个信号。控件(如按钮)触发这个命令,有人(类或实例)收到这个命令,检查自己能否处理(拥有的CommandBindingCollection是否包含与这个命令相关的CommandBinding(检查其Command属性)),若找到对应的CommandBinding,那么就执行CommandBinding的Executed逻辑。
  2. 按第1条理解,RoutedCommand定义在哪里并不要紧,一般将之定义为自定义控件的静态字段。
  3. 事件和命令的区别,个人理解:事件的信号源是一个,关注者收到信号后做自己计划做的事,信号源并不care关注者做什么事;命令的信号源可以有多个,当然关注者也可以有多个,一般来说,所有信号源都期望关注者执行一个或一组逻辑,而关注者也遵循它们的意愿,这里的关注者可以理解为代理。
  4. 相同VisualStateGroup包含的所有VisualState互斥,使用VisualStateManager.GoToState来进行状态跳转。

转载请注明本文出处:http://www.cnblogs.com/newton/archive/2012/12/28/2836672.html

原文地址:https://www.cnblogs.com/newton/p/2836672.html