使用Extended.Wpf.Toolkit控件库

Extended.Wpf.Toolkit控件库是对WPF内置控件库的补充,提供了非常多的好用控件,可以帮助我们开发出交互性更强的桌面应用程序,是WPF程序开发的又一利器!

本文针对其中非常经典的几个控件做简要介绍,在后续项目中实际使用时可以更深入了解各控件用法。

主要控件简介

文档:https://github.com/xceedsoftware/wpftoolkit

示例程序:Extended.WPF.Toolkit.Live.Explorer

  • AvalonDock:为窗体程序添加停靠窗体,使得WPF能开发出类似MFC中的单文档和多文档应用程序。

  • PropertyGrid:非常经典的属性面板功能,在MFC和WinForm中均有该控件,这个控件高度还原了WinFrom中的控件样式。

  • CollectionControlButton & CollectionControl & CollectionControlDialog:对一组对象进行属性设置,在WinForm中大量存在。

  • Wazard:借助该控件可以开发出向导式的程序,可以用于创建项目并设置参数的场景。

AvalonDock用法

对该控件的使用,重点在于如何进行界面布局,以下是官方实例代码。

在实际项目中建议单独安装Dirkster.AvalonDock包,而不直接使用扩展控件库中的该控件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
<xcad:DockingManager x:Name="_dockingManager"
Margin="10"
AllowMixedOrientation="True">
<xcad:LayoutRoot x:Name="_layoutRoot">
<xcad:LayoutPanel Orientation="Horizontal">
<!--左侧Properties面板(展开状态)-->
<xcad:LayoutAnchorablePane DockWidth="200">
<xcad:LayoutAnchorable ContentId="properties"
Title="Properties"
CanHide="False"
CanClose="False"
AutoHideWidth="240">
<xctk:PropertyGrid NameColumnWidth="110"
SelectedObject="{Binding ElementName=_layoutRoot, Path=LastFocusedDocument.Content}"
AutoGenerateProperties="False">
<xctk:PropertyGrid.PropertyDefinitions>
<xctk:PropertyDefinition TargetProperties="Background" />
<xctk:PropertyDefinition TargetProperties="BorderBrush" />
<xctk:PropertyDefinition TargetProperties="BorderThickness" />
<xctk:PropertyDefinition TargetProperties="FontSize" />
<xctk:PropertyDefinition TargetProperties="FontStyle" />
<xctk:PropertyDefinition TargetProperties="Width" />
<xctk:PropertyDefinition TargetProperties="Height" />
</xctk:PropertyGrid.PropertyDefinitions>
</xctk:PropertyGrid>
</xcad:LayoutAnchorable>
</xcad:LayoutAnchorablePane>

<!--中间文档区域-->
<xcad:LayoutDocumentPaneGroup>
<xcad:LayoutDocumentPane>
<xcad:LayoutDocument ContentId="document1"
Title="Document 1">
<Button Content="Document 1 Content"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</xcad:LayoutDocument>
<xcad:LayoutDocument ContentId="document2"
Title="Document 2">
<TextBox Text="Document 2 Content"
AcceptsReturn="True" />
</xcad:LayoutDocument>
</xcad:LayoutDocumentPane>
</xcad:LayoutDocumentPaneGroup >

<!--右侧两个面板Alarms、Journal(展开状态)-->
<xcad:LayoutAnchorablePaneGroup DockWidth="125">
<xcad:LayoutAnchorablePane>
<xcad:LayoutAnchorable ContentId="alarms"
Title="Alarms"
CanClose="True">
<ListBox>
<s:String>Alarm 1</s:String>
<s:String>Alarm 2</s:String>
<s:String>Alarm 3</s:String>
</ListBox>
</xcad:LayoutAnchorable>

<xcad:LayoutAnchorable ContentId="journal"
Title="Journal">
<RichTextBox>
<FlowDocument>
<Paragraph FontSize="14"
FontFamily="Segoe">
This is the content of the Journal Pane.
<LineBreak />
A
<Bold>RichTextBox</Bold> has been added here
</Paragraph>
</FlowDocument>
</RichTextBox>
</xcad:LayoutAnchorable>
</xcad:LayoutAnchorablePane>
</xcad:LayoutAnchorablePaneGroup>
</xcad:LayoutPanel>

<!--左侧停靠面板(非展开状态)-->
<xcad:LayoutRoot.LeftSide>
<xcad:LayoutAnchorSide>
<xcad:LayoutAnchorGroup>
<xcad:LayoutAnchorable Title="Agenda"
ContentId="agenda">
<TextBlock Text="Agenda Content"
Margin="10"
FontSize="18"
FontWeight="Black"
TextWrapping="Wrap" />
</xcad:LayoutAnchorable>
<xcad:LayoutAnchorable Title="Contacts"
ContentId="contacts">
<TextBlock Text="Contacts Content"
Margin="10"
FontSize="18"
FontWeight="Black"
TextWrapping="Wrap" />
</xcad:LayoutAnchorable>
</xcad:LayoutAnchorGroup>
</xcad:LayoutAnchorSide>
</xcad:LayoutRoot.LeftSide>
</xcad:LayoutRoot>
</xcad:DockingManager>

总的来说,创建基于AvalonDock控件的布局主要分为三个部分:

  • 创建展开状态的面板(Properties左侧面板、Alarms和Journal右侧面板组):xcad:LayoutAnchorablePane创建单个面板,xcad:LayoutAnchorablePaneGroup创建面板组。
  • 创建中间文档面板组:默认占据其余位置,由<xcad:LayoutDocumentPaneGroup>代码创建。
  • 创建上下左右四个方向的非展开状态的面板:由<xcad:LayoutAnchorGroup>创建。

其中前两类面板的位置由<xcad:LayoutPanel Orientation="Horizontal">这行代码控制,为水平布局,面板依次布置水平布置;非展开状态面板的位置由<xcad:LayoutRoot.LeftSide>代码决定,可以为四个方向分别指定非展开面板。

PropertyGrid用法

对该控件的使用,重点在于如何定义数据源,并设置不同属性的调整控件样式,以下PropertyGrid控件绑定后台ThisStudent类实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<xctk:PropertyGrid x:Name="propertyGrid" Width="300" Background="WhiteSmoke"
AutoGenerateProperties="False"
ShowTitle="False"
ShowDescriptionByTooltip="True"
ShowSearchBox="True"
FilterWatermark="查询"
ShowSortOptions="False"
ShowSummary="False"
SelectedObject="{Binding ThisStudent}">
<xctk:PropertyGrid.PropertyDefinitions>
<xctk:PropertyDefinition TargetProperties="Name,Age,Grade,BirthDate,Hobby,IsRight"/>
</xctk:PropertyGrid.PropertyDefinitions>

<xctk:PropertyGrid.EditorDefinitions>
<xctk:EditorTemplateDefinition >
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type s:DateTime}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<xctk:DateTimePicker ShowButtonSpinner="False"
Value="{Binding Value}"
Format="ShortDate"/>
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>

<xctk:EditorTemplateDefinition >
<xctk:EditorTemplateDefinition.TargetProperties>
<s:String>IsRight</s:String>
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<CheckBox Content="正确"
IsChecked="{Binding Value}"/>
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
</xctk:PropertyGrid.EditorDefinitions>
</xctk:PropertyGrid>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public partial class Student : ObservableObject
{
[Display(Name = "名字", GroupName = "个人信息", Order = 0, Description = "请输入全名")]
[ObservableProperty]
private string? _name;

[Display(Name = "年龄", GroupName = "个人信息", Order = 1, Description = "学生年龄")]
[ObservableProperty]
private int _age;

[Display(Name = "成绩", GroupName = "个人信息", Order = 1, Description = "学生成绩")]
[ObservableProperty]
private double _grade;

[Display(Name = "出生日期", GroupName = "详细信息", Order = 1, Description = "学生出生日期")]
[ObservableProperty]
private DateTime _birthDate = DateTime.Now;

private string? _hobby = "爬山";
[Display(Name = "爱好", GroupName = "详细信息", Order = 1, Description = "请选择一种爱好")]
[ItemsSource(typeof(ComboBoxItemsSource))]
public string? Hobby
{
get => _hobby;
set => SetProperty(ref _hobby, value);
}

[ObservableProperty]
[Display(Name = "正确与否", GroupName = "详细信息", Order = 1, Description = "他的话是正确的吗?")]
private bool _isRight = true;

#region ComboBox的数据源定义
private class ComboBoxItemsSource : IItemsSource
{
ItemCollection IItemsSource.GetValues()
{
return new ItemCollection { "爬山", "骑行", "跑步", "健身" };
}
}
#endregion
}

定义后台数据

  • 如果希望修改后台数据后,界面能马上更新,那么就应该定义可通知属性,本例使用CommunityToolkit.Mvvm库简化代码。
  • 可以为属性设置界面显示名称、分组名、属性描述、顺序等信息,[Display(Name = "爱好", GroupName = "详细信息", Order = 1, Description = "请选择一种爱好")]

指定需要显示的属性

如果设置AutoGenerateProperties="False",那么就需要手动指定要将后台类实例的哪些属性显示到PropertyGrid上;如果AutoGenerateProperties="True",默认所有属性会被显示。

1
2
3
<xctk:PropertyGrid.PropertyDefinitions>
<xctk:PropertyDefinition TargetProperties="Name,Age,Grade,BirthDate,Hobby,IsRight"/>
</xctk:PropertyGrid.PropertyDefinitions>

自定义属性编辑器

  • 对于常见数据类型,都内置有默认的属性编辑器(即修改属性的方式,文本框、日期选择器等)。
  • 用户可以为属性自定义编辑器,指定属性有以下三种方式:
1
2
3
4
<xctk:EditorTemplateDefinition TargetProperties="FirstName,LastName,WritingFont">
<xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
1
2
3
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type s:DateTime}" />
</xctk:EditorTemplateDefinition.TargetProperties>
1
2
3
4
<xctk:EditorTemplateDefinition.TargetProperties>
<sys:String>Age</sys:String>
<xctk:TargetPropertyType Type="{x:Type sys:DateTime}" />
</xctk:EditorTemplateDefinition.TargetProperties>
  • 通过以下方式可以在XAML中自定义属性编辑器:
1
2
3
<DataTemplate>
<CheckBox Content="正确" IsChecked="{Binding Value}"/>
</DataTemplate>
  • 也可以创建更加复杂的编辑器(对于复杂情况可以使用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class FirstNameEditor : Xceed.Wpf.Toolkit.PropertyGrid.Editors.ITypeEditor
{
public FrameworkElement ResolveEditor(Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
{
TextBox textBox = new TextBox();
textBox.Background = new SolidColorBrush(Colors.Red);

var _binding = new Binding("Value");
_binding.Source = propertyItem;
_binding.ValidatesOnExceptions = true;
_binding.ValidatesOnDataErrors = true;
_binding.Mode = propertyItem.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay;
BindingOperations.SetBinding(textBox, TextBox.TextProperty, _binding);
return textBox;
}
}
1
2
3
4
5
public class CustomAttributEditorPerson
{
[Editor(typeof(FirstNameEditor), typeof(FirstNameEditor))]
public string FirstName { get; set; }
}

ComboBox指定数据源的简便方式

在这个控件库中,可以更方便地为ComboBox指定列表数据源,创建一个实现IItemsSource接口的类,实现其GetValues()方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[ItemsSource(typeof(ComboBoxItemsSource))]
public string? Hobby
{
get => _hobby;
set => SetProperty(ref _hobby, value);
}

// ComboBox的数据源定义
private class ComboBoxItemsSource : IItemsSource
{
ItemCollection IItemsSource.GetValues()
{
return new ItemCollection { "爬山", "骑行", "跑步", "健身" };
}
}

(转载本站文章请注明作者和出处lihaohello.top,请勿用于任何商业用途)

评论