构建应用程序:WPF与Windows Forms实践
1. WPF应用程序构建
在WPF应用程序开发中,我们构建了一个总统照片识别程序。以下是详细步骤和关键代码分析。
1.1 列表框事件处理与模板设置
为列表框中的图像点击事件定义了事件处理程序:
SelectionChanged="PresPhotoListBox_SelectionChanged"
每个列表项的源设置为绑定,意味着我们将绑定到父元素(在网格中定义)的源。同时,
ItemContainerStyle
设置为资源部分中定义的样式。
为了绘制列表框中的每个项目,我们使用了
ListBox.ItemTemplate
:
<ListBox.ItemTemplate>
<DataTemplate>
<Border VerticalAlignment="Center"
HorizontalAlignment="Center" Padding="4"
Margin="2" Background="White">
<Image Source="{Binding Path=ImageURI}" />
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
这里,
DataTemplate
是必要的,因为我们要显示从数据中获取的图像而不仅仅是简单文本。在
DataTemplate
中,我们放置了一个
Border
对象,内部包含一个
Image
对象。
Image
的源通过绑定到
ImageURI
属性来设置。
1.2 完整的XAML文件
以下是完整的XAML文件示例:
<Window x:Class="PhotoCatalog.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PhotoCatalog"
Title="President Identifier" ShowInTaskbar="False" Height="256" Width="253">
<Window.Resources>
<LinearGradientBrush x:Key="ListBoxGradient"
StartPoint="0,0"
EndPoint="0,1">
<GradientStop Color="#90000000"
Offset="0" />
<GradientStop Color="#40000000"
Offset="0.005" />
<GradientStop Color="#10000000"
Offset="0.04" />
<GradientStop Color="#20000000"
Offset="0.945" />
<GradientStop Color="#60FFFFFF"
Offset="1" />
</LinearGradientBrush>
<Style x:Key="SpecialListStyle"
TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}" >
<Border BorderBrush="Gray"
BorderThickness="1" CornerRadius="6"
Background="{DynamicResource ListBoxGradient}" >
<ScrollViewer VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Visible">
<StackPanel IsItemsHost="True"
Orientation="Horizontal"
HorizontalAlignment="Left" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SpecialListItem"
TargetType="{x:Type ListBoxItem}">
<Setter Property="MaxHeight" Value="75" />
<Setter Property="MinHeight" Value="75" />
<Setter Property="Opacity" Value=".75" />
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetProperty="MaxHeight" To="85" />
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetProperty="Opacity" To="1.0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1"
Storyboard.TargetProperty="MaxHeight" />
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
<local:Images x:Key="Presidents">
<local:ImageURL
ImageURI="http://www.whitehouse.gov/history/presidents/images/gw1.gif"
Name="George Washington" />
<local:ImageURL ImageURI=".../ja2.gif" Name="John Adams" />
<local:ImageURL ImageURI=".../tj3.gif" Name="Thomas Jefferson" />
<local:ImageURL ImageURI=".../jm4.gif" Name="James Madison" />
<local:ImageURL ImageURI=".../jm5.gif" Name="James Monroe" />
<local:ImageURL ImageURI=".../ja6.gif" Name="John Quincy Adams" />
<local:ImageURL ImageURI=".../aj7.gif" Name="Andrew Jackson" />
<local:ImageURL ImageURI=".../mb8.gif" Name="Martin Van Buren" />
<local:ImageURL ImageURI=".../wh9.gif" Name="William H. Harrison" />
<local:ImageURL ImageURI=".../jt10.gif" Name="John Tyler" />
<local:ImageURL ImageURI=".../jp11.gif" Name="James K. Polk" />
<local:ImageURL ImageURI=".../zt12.gif" Name="Zachary Taylor" />
<local:ImageURL ImageURI=".../mf13.gif" Name="Millard Fillmore" />
<local:ImageURL ImageURI=".../fp14.gif" Name="Franklin Pierce" />
<local:ImageURL ImageURI=".../jb15.gif" Name="James Buchanan" />
<local:ImageURL ImageURI=".../al16.gif" Name="Abraham Lincoln" />
<local:ImageURL ImageURI=".../aj17.gif" Name="Andrew Johnson" />
<local:ImageURL ImageURI=".../ug18.gif" Name="Ulysses S. Grant" />
<local:ImageURL ImageURI=".../rh19.gif" Name="Rutherford B. Hayes" />
<local:ImageURL ImageURI=".../jp11.gif" Name="James Garfield" />
<local:ImageURL ImageURI=".../jg20.gif" Name="Chester A. Arthur" />
</local:Images>
</Window.Resources>
<Grid Width="300" Height="170"
DataContext="{StaticResource Presidents}">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel >
<TextBlock FontSize="14" Grid.Row="0" >
United States Presidents
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="1" Grid.ColumnSpan="3" >
<ListBox Style="{StaticResource SpecialListStyle}"
Name="PresPhotoListBox" Margin="0,0,0,20"
SelectionChanged="PresPhotoListBox_SelectionChanged"
ItemsSource="{Binding }"
IsSynchronizedWithCurrentItem="True" SelectedIndex="0"
ItemContainerStyle="{StaticResource SpecialListItem}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Border VerticalAlignment="Center"
HorizontalAlignment="Center" Padding="4"
Margin="2" Background="White">
<Image Source="{Binding Path=ImageURI}" />
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
1.3 事件处理
当用户更改列表框中的选定项时,会触发事件处理程序:
private void PresPhotoListBox_SelectionChanged(
object sender, SelectionChangedEventArgs e)
{
ListBox lb = sender as ListBox;
if (lb != null)
{
if (lb.SelectedItem != null)
{
string chosenName = (lb.SelectedItem as ImageURL).Name.ToString( );
Title = chosenName;
}
}
else
{
throw new ArgumentException(
"Expected ListBox to call selection changed in PresPhotoListBox_SelectionChanged");
}
}
在这个事件处理程序中,我们首先将发送者转换为
ListBox
,然后检查所选项目是否为空。如果不为空,我们提取所选项目的名称并将其设置为窗口的标题。
2. Windows Forms应用程序构建
在Windows Forms应用程序开发中,我们构建了一个文件复制程序。以下是详细步骤和关键代码分析。
2.1 创建应用程序
步骤如下:
1. 打开Visual Studio 2008,选择“Create ➝Project”。
2. 在“New Project”窗口中,创建一个新的Visual C#应用程序,并从“Templates”窗口中选择“Windows Forms Application”。
3. 将项目命名为“Windows Form File Copier”。
用户界面由以下控件组成:
| 控件类型 | 控件名称 |
| ---- | ---- |
| 树视图(左) | tvwSource |
| 树视图(右) | tvwDestination |
| 文本框 | txtTargetDir |
| 标签 | lblSource、lblTarget、lblStatus |
| 按钮 | btnClear、btnCopy、btnDelete、btnCancel |
| 复选框 | chkOverwrite |
创建UI的具体操作步骤:
1. 点击表单并在“Properties”窗口中进行操作。
2. 展开“Size”属性,将“Width”设置为585,“Height”设置为561。
3. 将“Text”属性更改为“File Copier”,名称更改为“frmFileCopier”,并将“AutoSizeMode”更改为“GrowOnly”。
4. 拖动两个树视图控件到表单上,并按照要求放置。
5. 继续拖动其他控件到表单上并命名。
2.2 创建事件处理程序
在Visual Studio中有四种常见的创建事件处理程序的方法:
1.
手动命名
:点击控件,在“Properties”窗口中切换到“Events”模式(点击闪电按钮),找到要挂钩的事件,在事件旁边的框中输入名称,按“Enter”键,Visual Studio 2008将创建事件处理程序存根并让你填充细节。
2.
自动命名
:在“Properties”窗口中,不输入名称,直接在属性名称旁边的空间中双击。Visual Studio将通过连接控件名称和事件名称来创建一个名称。例如,双击“Click”事件将创建一个名为“btnCancel_Click”的事件处理程序。
private void btnCancel_Click(object sender, EventArgs e)
{
}
- 共享事件处理程序 :下拉现有事件处理程序列表,指示Visual Studio该按钮的“Click”事件将共享一个已有的事件处理方法。
- 快速创建 :双击控件。每个控件都有一个默认事件(对于按钮,默认事件是“Click”),双击按钮将创建一个事件处理程序。
2.3 填充树视图控件
两个
TreeView
控件的工作方式基本相同,但有一些区别:
- 左控件
tvwSource
列出目录和文件,
CheckBoxes
属性设置为
true
,允许多选。
- 右控件
tvwTargetDir
仅列出目录,
CheckBoxes
属性设置为
false
,强制单选。
我们将填充两个
TreeView
控件的公共代码封装到
FillDirectoryTree
方法中,并在表单的构造函数中调用:
FillDirectoryTree(tvwSource, true);
FillDirectoryTree(tvwTargetDir, false);
FillDirectoryTree
方法的实现如下:
private void FillDirectoryTree(TreeView tvw, bool isSource)
{
tvw.Nodes.Clear();
string[] strDrives = Environment.GetLogicalDrives();
foreach (string rootDirectoryName in strDrives)
{
try
{
DirectoryInfo dir = new DirectoryInfo(rootDirectoryName);
dir.GetDirectories();
TreeNode ndRoot = new TreeNode(rootDirectoryName);
tvw.Nodes.Add(ndRoot);
if (isSource)
{
GetSubDirectoryNodes(ndRoot, ndRoot.Text, true, 1);
}
else
{
GetSubDirectoryNodes(ndRoot, ndRoot.Text, false, 1);
}
}
catch
{
// 跳过不可用的驱动器
}
}
}
在这个方法中,我们首先清空
TreeView
的节点集合,然后获取系统上的所有逻辑驱动器。对于每个驱动器,我们检查其是否可用,如果可用则创建一个根节点并添加到
TreeView
中。最后,根据是否为源控件调用
GetSubDirectoryNodes
方法来递归填充子目录。
GetSubDirectoryNodes
方法的实现如下:
private void GetSubDirectoryNodes(
TreeNode parentNode, string fullName, bool getFileNames, int level)
{
DirectoryInfo dir = new DirectoryInfo(fullName);
DirectoryInfo[] dirSubs = dir.GetDirectories();
foreach (DirectoryInfo dirSub in dirSubs)
{
if ((dirSub.Attributes & FileAttributes.Hidden) != 0)
{
continue;
}
TreeNode subNode = new TreeNode(dirSub.Name);
parentNode.Nodes.Add(subNode);
if (level < MaxLevel)
{
GetSubDirectoryNodes(subNode, dirSub.FullName, getFileNames, level + 1);
}
}
}
在这个方法中,我们首先获取子目录列表,然后遍历每个子目录。如果子目录是隐藏的,则跳过。否则,创建一个新的节点并添加到父节点中。如果当前级别小于最大级别,则递归调用
GetSubDirectoryNodes
方法来填充子目录。
总结
通过以上两个示例,我们展示了如何使用WPF和Windows Forms技术构建应用程序。WPF更侧重于声明式编程,而Windows Forms则更注重传统的控件操作和事件处理。在实际开发中,我们可以根据项目的需求和特点选择合适的技术。同时,我们也学习了如何处理事件、填充控件和递归遍历目录等关键技术。希望这些内容对你有所帮助。
构建应用程序:WPF与Windows Forms实践
3. 技术对比与选择考量
在实际开发中,选择WPF还是Windows Forms技术需要综合多方面因素进行考量。以下是两者的详细对比:
3.1 编程范式
- WPF :WPF是一种高度声明式的编程模型,大量的界面布局和样式设置可以通过XAML来完成。例如,在总统照片识别程序中,列表框的样式、模板以及渐变背景等都是通过XAML声明定义的。这种方式使得界面设计和代码逻辑分离,提高了开发效率和可维护性。
- Windows Forms :Windows Forms采用传统的面向对象编程范式,通过拖动控件到表单上并设置属性来创建界面,更注重控件的操作和事件处理。例如,在文件复制程序中,我们通过在设计器中拖动树视图、按钮等控件,并在代码中处理它们的事件来实现功能。
3.2 界面表现力
- WPF :WPF提供了丰富的图形和动画功能,能够创建出更加美观和富有交互性的界面。例如,在总统照片识别程序中,列表项的鼠标悬停动画效果就是通过WPF的动画功能实现的。
- Windows Forms :Windows Forms的界面表现力相对较弱,主要依赖于系统提供的标准控件,自定义界面的能力有限。
3.3 性能和兼容性
- WPF :WPF在处理复杂界面和大量数据时可能会有一定的性能开销,因为它需要更多的资源来渲染图形和处理动画。但它在高分辨率和多显示器环境下表现较好。
- Windows Forms :Windows Forms的性能相对稳定,对于简单的应用程序,它的启动速度和响应速度都比较快。而且,由于它是早期的技术,兼容性较好,能够在较旧的操作系统上运行。
3.4 开发难度
- WPF :WPF的学习曲线较陡,需要掌握XAML、数据绑定、样式和模板等知识。对于初学者来说,理解和运用这些概念可能会有一定的难度。
- Windows Forms :Windows Forms的开发相对简单,易于上手。对于有一定编程基础的开发者来说,能够快速掌握其开发方法。
4. 实际应用场景分析
根据上述技术对比,我们可以为不同的应用场景选择合适的技术。
4.1 适合WPF的场景
- 需要高度定制化界面的应用程序 :例如,多媒体应用、游戏、设计工具等。WPF的丰富图形和动画功能可以满足这些应用对界面美观性和交互性的要求。
- 数据驱动的应用程序 :WPF的数据绑定功能使得数据和界面之间的同步变得非常方便。例如,在数据展示和编辑应用中,可以使用WPF将数据直接绑定到界面控件上,实现数据的实时更新。
4.2 适合Windows Forms的场景
- 传统的桌面应用程序 :例如,办公软件、小型工具等。这些应用通常对界面要求不高,更注重功能的实现和性能的稳定。
- 对兼容性要求较高的应用程序 :如果需要在较旧的操作系统上运行应用程序,Windows Forms是一个不错的选择。
5. 开发技巧与最佳实践
无论是使用WPF还是Windows Forms,都有一些开发技巧和最佳实践可以提高开发效率和代码质量。
5.1 WPF开发技巧
- 合理使用XAML :XAML是WPF的核心,合理使用XAML可以减少代码量,提高代码的可读性和可维护性。例如,将样式和模板定义在资源字典中,以便在多个地方复用。
- 数据绑定的优化 :在使用数据绑定时,要注意性能问题。避免在数据绑定时进行复杂的计算和查询,尽量将数据处理逻辑放在ViewModel中。
- 动画的使用 :动画可以增强用户体验,但要注意不要过度使用,以免影响性能。在使用动画时,要合理设置动画的持续时间和帧率。
5.2 Windows Forms开发技巧
- 事件处理的优化 :在处理事件时,要避免在事件处理程序中编写过多的代码。可以将复杂的逻辑封装到独立的方法中,提高代码的可维护性。
-
控件的布局管理
:合理使用布局控件,如
TableLayoutPanel、FlowLayoutPanel等,可以使界面布局更加灵活和美观。 - 错误处理 :在代码中要进行充分的错误处理,避免程序因异常而崩溃。例如,在文件复制程序中,要处理文件读写错误、目录不存在等异常情况。
6. 未来趋势展望
随着技术的不断发展,WPF和Windows Forms也在不断演进。
6.1 WPF的发展趋势
- 与现代技术的融合 :WPF可能会与现代的前端技术,如React、Vue.js等进行融合,进一步提高界面的开发效率和用户体验。
- 跨平台支持 :虽然目前WPF主要用于Windows平台,但未来可能会有更多的跨平台支持,使其能够在其他操作系统上运行。
6.2 Windows Forms的发展趋势
- 维护和优化 :由于Windows Forms已经有大量的应用程序在使用,微软可能会继续对其进行维护和优化,以确保其在新的操作系统上的兼容性和性能。
- 集成新的功能 :Windows Forms可能会集成一些新的功能,如对触摸屏幕的支持、与云服务的集成等。
7. 总结与建议
通过对WPF和Windows Forms技术的详细介绍和对比,我们可以得出以下结论:
- 技术选择 :根据项目的需求和特点,选择合适的技术。如果需要高度定制化的界面和丰富的交互效果,建议选择WPF;如果是传统的桌面应用程序,对兼容性要求较高,Windows Forms是一个不错的选择。
- 学习和实践 :无论是WPF还是Windows Forms,都需要不断学习和实践,掌握其开发技巧和最佳实践。可以通过阅读相关的书籍、文章,参与开源项目等方式来提高自己的开发水平。
- 持续关注技术发展 :随着技术的不断发展,WPF和Windows Forms也在不断演进。要持续关注技术的发展趋势,以便在未来的项目中做出更明智的选择。
希望本文能够帮助你更好地理解WPF和Windows Forms技术,并在实际开发中做出正确的决策。如果你有任何问题或建议,欢迎在评论区留言讨论。
超级会员免费看
6万+

被折叠的 条评论
为什么被折叠?



