DotNet关键知识点——WPF篇(七)

1. 模板

相关类型继承关系:

DispatcherObject – FrameworkTemplate – ControlTemplate
                                     - DataTemplate

不同于DataTemplate,控件模板ControlTemplate一般设置到FrameworkElement.Template上。

作为资源的模板
类似其他资源内容,加x:Key标识,引用时用Template=”{StaticResource xxx}”

内容占位符
ContentPresenter(: FrameworkElement)标记用于占位Content属性内容。
ItemsPresenter(: FrameworkElement)标记用于占位Items属性内容。

为使上述内容占位符工作,ControlTemplate.TargetType必须设置(可从ControlTemplate的一个构造函数直接设入),另外ControlTemplate.TargetType和Style.TargetType不同,它不会使模板自动作用到所辖单元上,单元必须主动设置其Template属性才能引用模板

模板可视内容
模板可视内容是模板的主体,具体内容在其FrameworkElementFactory Framework.VisualTree上(树状结构的根),由于模板中涉及到的组件并不是组件的实例,所以它必须是一个抽象的对象,就是这里所谓的组件类厂FrameworkElementFactory。类厂的一个构造函数能接受一个Type类型,代入单元的类型(例如Grid, Rectangle, ContentPresenter等),就能创建这个单元对应的类厂,在类厂上也有SetValue,SetBinding,AddHandler等方法用类似具体对象的方法修饰这个单元。类厂有Name属性,用来被引用(如在触发器中)。类厂有AppendChild方法,可以继续添加枝杈。
模板视图内容通常是静态的,仅设置模板则会使单元原有的动态特性也消失(如按钮接受按键后没有视觉变化),但实际功能不变。

2. 模板触发器

模板触发器类似在组件的Style中定义。

3. 模板动态创建和代码交互

上述模板可是内容和模板触发器可以按照XAML定义逻辑在代码中创建,目前唯独发现ControllableStoryboardAction(如StopStoryBoard等)在指定正确的BeginStoryBoardName后,在运行时仍出现相应名称的BeginStoryBoard单元未找到错误,而导出的Template看上去没有问题。

模板输出:
System.Windows.Markup.XamlWriter.Save(Control.Template templat, Stream stream)

4. 模板的数据绑定

获取模板应用对象属性
在模板中的控件中使用语法:PropName=”{TemplateBinding PropName}”,就可将该控件属性绑定到模板应用对象的属性上

使用数据绑定
PropName=”{Binding RelativeSource={RelativeSource TemplatedParent}, Path=somePath, [Mode=…]}” />
则绑定到模板应用对象的somePath属性上。(TemplateBinding不支持Freezable类型,这个情形下用数据绑定)

由代码指定上下文
可以在代码中设置DataContext,而不用RelativeSource。

5. 用样式指定模板


上述样式在XAML中必须在模板之后定义。

6. 预定义模板组件名

与定义模板组件名形如PART_,在自定义模板中如需对应,则必须以此命名。

7. 自建组件

分成两类:
用户控件:汗设计期界面(类似Window),通常整合已有控件,继承自UserControl( : ContentControl : Control)类。
自定义控件:无设计期界面;全新的控件,通常继承自Control类,也可基于一个已有的可用组件。
两者都能创建下述关联属性和接力事件

创建自建组件需要同时建立其关联属性和路由事件等。所有WPF组件均最终继承自DependencyObject。

8. 创建属性(依赖属性,Dependency Property)
为充分利用WPF框架,控件属性一般以依赖属性形式出现。
1. 依赖属性均为组件中(继承自DependencyObject)类型为DependencyProperty的公共只读静态成员,名称形如Property,其中部分是这个属性的对外名称。
2. 然后在组件的静态构造函数中注册:
FrameworkPropertyMetadata md = new FrameworkPropertyMetadata();
如果构造函数用(defaultValue, FrameworkPropertyMetadataOptions.Inherits)则可使该属性为可继承的(在控件树中传递)。
组件.Property = DependencyProperty.Register(“”, typeof(属性类型), typeof(组件), md);
3. 在组件类中定义属性常规接口,DependencyObject.GetValueSetValue是WPF属性系统内建的接口:
public 属性类型
{
  get { return (属性类型)GetValue(组件.Property);
  set { SetValue(组件.Property, value); }
}

依赖属性改变回调:
使用上述FrameworkPropertyMetadata构造函数含参版本,代入一个PropertyChangedCallback,即可指定回调,形如:static void PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)

9. 创建接力事件(路由事件,Routed Event):
1. 定义RoutedEvent的公共只读静态成员,名称形如Event,其中标识这个时间的名称。
2. 在组件的静态构造函数中注册,这里上浮事件用Bubble模式,下沉事件(通常前缀Preview)当为Tunnel,如果不用接力则为Direct:EventManager.RegisterRoutedEvent(“", RoutingStrategy.Bubble, typeof(事件处理代表类型),  typeof(组件名称))
3. 定义事件处理函数代表,通常形如:delegate void TheEventHandlerDelegate(object sender, EventArgs e),其中EventArgs继承自RoutedEventArgs;如果用默认的则是delegate void RoutedEventHandler(object sender RoutedEventArgs e)。
4. 在组件类中定义事件常规接口:UIElement.AddHandlerRemoveHandler是WPF接力事件系统内建接口,以下语法一般是为WPF的路由事件特设的:
public event 事件处理代表类型
{
    add { AddHandler(Event, value);
    remove { RemoveHandler(Event, value);
}
5. 从内部触发事件
PreviewEventArgs args = new PreivewEventArgs(PreveiwEvent);
组件.RaiseEvent(args); // 下沉接力
EventArgs args = new EventArgs(Event);
// 或args.RoutedEvent = Event;
组件.RaiseEvent(args); // 上浮接力
这个过程中不需要考虑args.Handled的值,因为只要其设置为true,则RaiseEvent不会使事件被触发和接力。

10. 主题

系统主题
主要包括:SystemColors,SystemFonts,SystemParameters。
用{DynamicResource {x:Static SystemColors.ColorBrushKey}}”形式引用;在代码中:SystemColors.ConrolBrush。

主题模板
默认Generic.xaml。除了经典Windows主题Classic.Xaml,其他主题命名一般形式:<主题名>.<主题基色>.xaml。
在AssemblyInfo.cs文件中以下标记指定主题模板资源查找路径:
[assembly: ThemeInfo(专用主题搜索路径,  通用主题搜索路径)]
资源搜索路径包括
ResourceDictionaryLocation.None:不找
ResourceDictionaryLocation.SourceAssembly:从程序所在位置找
ResourceDictionaryLocation.ExternalAssembly:找<当前程序Assembly名>.<主题名>.dll
通常需要建立一个Themes文件夹存放主题模板文件,并设立Generic.xaml用于所有未指定情形。

11. 使用Windows窗体(Windows Forms)控件

对话框
System.Windows.Forms.OpenFileDialog和SaveFileDialog,链入System.Windows.Forms库后即可直接在代码中使用,XAML中相应声明。
System.Windows.Forms.ColorDialog:色彩需要转换:System.Drawing.Color color1->System.Windows.Media.Color color2,先color1.ToArgb()转int,再位操作提出a,r,g,b四个字节,color2=System.Windows.Forms.Color.FromArgb(a,r,g,b)。

控件载体 WindowsFormsHost
使用时需再链入WindowsFormsIntegrity并使用System.Windows.Forms.Integration名空间,并在XAML中声明。有意义属性直接在Windows窗体控件的标记中设置,位置尺寸在WindowsFormsHost上设置。
事件直接在Windows窗体控件中设置,不再是接力事件,需要立即响应。
在代码中必须通过WindowsFormsHost.Child属性获得引用并转换到具体的Windows窗体控件类型后使用该控件。

常见Windows窗体在WPF中应用

MaskedTextBox
掩码元素,掩码设置在Mask属性中:
0(必须), 9(可选)数字;#可选数字、空格或加减号;L必现,?可选字母;&必现,C可选字符;A或a可选字母或数字;.小数点;,千分号;:时间分隔号;/日期分隔号;$货币号;<,>,|后续改为小写,大写,恢复;/转义;其他字符表示不可改内容。
小数点,货币号的具体呈现等由FormatProvider决定。

PropertyGrid
可在运行时交互式地修改某个控件的属性。其object SelectedObject属性用于绑定到目标控件(任意设计期属性可设控件),只能通过代码完成绑定;PropertySort属性指定属性排序依据。

参考资料:
[1] http://msdn.microsoft.com/library/ms742882.aspx
[2] http://devlicious.com/blogs/christopher_bennage/archive/2008/07/04/templatebinding-a-bridge-between-styles-and-templates.aspx
[3] http://social.msdn.microsoft.com/Forums/cs-CZ/wpf/thread/61112239-e1d9-414d-ac32-8e329ed0c5f0
[4] Templates for items controls http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemspanel.aspx

posted @ 2009-12-04 20:23  quanben  阅读(240)  评论(0编辑  收藏  举报