WPF实例

9.5 WPF实例--TextReader
前面简单介绍了WPF应用程序的类型和开发模式,本节将通过一个WPF独立应用程序实例TextReader进一步介绍WPF应用程序开发的一般过程。
9.5.1 TextReader界面设计
实例TextReader将开发一个可以打开文本文档的小软件,它具有自动刷新目录、自动判断文件类型是否为文本类型,并查看文本类型的功能,同时还可以查看文件和目录等详细属性。
由于WPF修改了应用程序中窗体的布局方式,使得界面布局更加灵活,但也对开发人员提出了更高的要求,而且使对WPF还不太熟悉的初学者更加不知所措。WPF应用程序的界面设计有以下4个步骤:
(1) 根据软件需求,整理出软件中大致的窗体数量以及各个窗体的功能。在TextReader实例中,需要两个窗体,其中一个是主窗体(也是启动窗体),它具有提供文件导航、文件内容查看功能;另外一个窗体是关于窗体,提供实例程序的版本信息等。
(2)对每个窗体根据功能进行大体布局上的考虑,重点考虑界面分几层、各层的布局方式(网格、绝对定位、流等)、各层区域的划分、每个区域内的布局和进一步分区,逐层进行,直到全部完成。以TextReader主窗体为例,最下层用Grid整体布局,共分3行2列,第1行作为菜单,第3行作为状态栏,第2行则是主界面。主界面区域按列分成左右两部分,左边为目录和文件导航树,右边为文件内容显示区域,如图9-12所示。
(3)重复第(2)步操作,知道界面大致框架基本完成,还涉及控件的选择、控件停靠方向、控件边框位置等细节问题。
(4)基本框架完成,开始进行着色,根据界面风格要求,对各控件进行背景色、前景色、背景图片等外观美化,从下到上,直到满意为止。其中,实例TextReader的界面设计效果如图9-12所示。
第9章  WPF开发实例(续3)9.5  WPF实例--TextReader - hxzon00 - hxzon00的博客
(点击查看大图)图9-12 实例TextReader界面设计效果
注意:界面设计图中只有窗体工作区,不包括窗体标题栏等非工作区内容。
示例代码9-15是该窗体布局的XAML代码,其中还包括了事件处理函数等,这些将在随后的几节中进行介绍,这里主要关心界面的布局和外观代码。其中,<Menu>空间表示菜单;<MenuItem>表示具体菜单项;<StatuBar>表示状态栏;<StatuBarItem>表示具体的状态栏项;<GridSpliter>节点表示一个分隔条,可以调整左右Grid列的宽度;<TreeView>是树形控件,在本例中用来加载目录和文件信息;<RichTextBox>是一个文本编辑框,本例中用来以只读方式加载和浏览文件内容;<TextBox>控件是文本输入框,用来以只读方式显示文件和目录的详细属性。
示例代码9-15:
<Window x: href="http://schemas.microsoft.com/winfx/2006/xaml/presentation">http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPF实例--TextReader" Height="513" Width="602"
WindowStartupLocation="CenterScreen" Icon="logo.ico"
Closing="Window_Closing" Loaded="Window_Loaded" Name="wndMain">
<Grid Name="grid1" ShowGridLines="False" Background="DarkGoldenrod">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="416*" />
<RowDefinition Height="29" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="417*" />
</Grid.ColumnDefinitions>

<Menu Grid.ColumnSpan="2" Name="menu1" FontSize="15" Background="DarkKhaki">
<MenuItem Header="开始" Name="miStart" Background="DarkKhaki">
<MenuItem Header="关于" Name="miAbout" Click="miAbout_Click"
Background="LightCoral" />
<Separator />
<MenuItem Header="退出" Name="MiExit" Click="MiExit_Click"
Background ="LightCoral" />
</MenuItem>
</Menu>

<TreeView Grid.Row="1" Margin="0,9" Name="tvDic"
SelectedItemChanged="tvDic_SelectedItemChanged"
MouseDoubleClick="tvDic_MouseDoubleClick"/>

<GridSplitter Grid.Column="1" Margin="6,0,0,0" Name="gridSplitter1" MinWidth="2"
HorizontalAlignment="Left" Width="2" Grid.Row="1" Background="Beige" />

 

<TabControl Grid.Column="1" Grid.Row="1" Margin="14,9,8,9" Name="tabView"
FontSize="15" Background="Tan">
<TabItem Header="属性" Name="tbiProp" Cursor="IBeam"
ToolTip="选中文件或目录的详细属性" Background="Tan">       
<TextBox Grid.Column="1" Grid.Row="1" Margin="10,10,10,10" Name="tbProp"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"
IsReadOnly="True" FontSize="15" Foreground="Blue"
Background="SlateGray"></TextBox>
</TabItem>
<TabItem Header="文件内容" Background="Tan">
<RichTextBox Name="rtbText" IsReadOnly="True" VerticalScrollBarVisibility= "Visible"
HorizontalScrollBarVisibility="Visible" />
</TabItem>
</TabControl>

<StatusBar Grid.Row="2" Margin="0,0,0,-3" Name="sbMain" Grid.ColumnSpan="2" Height="32"
VerticalAlignment="Bottom">
<StatusBarItem Name="sbiFile" Background="Wheat">打开文件...</StatusBarItem>
</StatusBar>
</Grid>
</Window>

 

9.5.2 显示子窗体
在应用程序开发过程中,经常需要弹出新的窗体。在WPF中,窗体有两种弹出模式,一种是通过 System.Windows.Window.Show()方法显示新窗体,新窗体和父窗体之间相互独立,可以同时捕获用户操作。另一种是通过 System.Windows.Window.ShowDialog()方法以模式对话框的方式显示新窗体,新窗体捕获用户操作,父窗体在子窗体存在期间不能进行任何操作,常见的属性设置等窗体就是这种模式。
在TextReader实例中,以模式对话框形式显示关于对话框,要显示一个新的窗体大致有以下3个步骤:
(1)创建要显示的新窗体的对象。
(2)初始化要显示的窗体,如果没有需要初始化的数据,则不进行此操作。
(3)通过System.Windows.Window.Show()或System.Windows.Window.ShowDialog() 方法显示新的对话框。
示例代码9-16是TextReader实例中显示About子窗体的具体实现,首先创建WndAbout窗体新对象wnd,然后进行wnd的Owner参数初始化,最后通过ShowDialog()方法显示对话框。
示例代码9-16:
//关于菜单处理函数
private void miAbout_Click(object sender, RoutedEventArgs e)
{
//创建"关于"对话框对象
WndAbout wnd = new WndAbout();
//设置新建的关于窗体属于当前窗体
wnd.Owner = this;
//用模式对话框形式显示"关于"对话框
wnd.ShowDialog();
}
9.5.3 关闭窗体
在WPF应用程序中,关闭窗体通过System.Windows.Window.Close()方法实现,在调用Close()方法之后,会立即引发窗体的Closing事件,开发人员可以在Closing事件处理函数中取消关闭操作。
如示例代码9-17所示,其中MiExit_Click()方法中通过this.Close()语句关闭当前窗体,而在Window_Closing()事件处理函数中,则通过e.Cancel=true语句取消关闭操作。
示例代码9-17:
//退出菜单事件处理函数
private void MiExit_Click(object sender, RoutedEventArgs e)
{
//关闭程序
this.Close( );
}
//退出前Closing事件处理函数
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
//退出提示
if (MessageBox.Show("你真的要退出应用程序吗?",
"提示", MessageBoxButton.YesNo,
MessageBoxImage.Question) == MessageBoxResult.No)
{
e.Cancel = true;    //取消退出操作
return;
}
}
9.5.4 使用TreeView控件
树形控件是以树形结构表达元素的方式,是比较常用的控件之一,在WPF控件库中,同样提供了树形控件--TreeView。
在WPF中,TreeView控件只是提供显示容器,具体的节点通过TreeViewItem完成,TreeView控件的Items属性表示了它所包含的子节点。TreeViewItem控件是一个可递归的控件,它也用Items属性表示子节点,层层深入,构成一个完整的树形结构。
可以通过TreeView控件的Items属性添加和删除它的子节点,Items属性的每个元素都是object类型,所以可以包含任意类型的元素,TreeView在显示时会自动通过元素的ToString()方法获取要显示的文本。在这一点上TreeViewItem和TreeView是完全一致的。
TreeView控件不包含节点展开和收拢事件(这和Win Form窗体中的Tree控件不同),它包含SelectedItemChanged事件,用来监视树形结构中选中节点的变化。
TreeViewItem控件具有展开(Expanded)事件和收拢(Collapsed)事件,通过这两个事件可以监视节点的展开和收拢,并给出相应的处理。
在TextReader实例中,要显示目录和文件结构,需要用一种典型的树状结构,所以TreeView控件是首选。本例通过以下思路来完成目录的动态加载:
(1)文件系统中的目录和文件都作为TreeView控件中的节点,用TreeViewItem空间表示,而目录和文件的详细路径则保存在TreeViewItem.Tag属性中。
(2)在窗体启动时加载操作系统中所有的逻辑分区(例如,C:/、D:/等),实际上它们都是目录。
(3)监视树中节点的Expanded事件,如果是一个目录第一次展开,则加载该目录下直接的所有子目录和文件,不包括子目录的子目录和文件。
(4)监视TreeView控件的SelectedItemChanged事件,并根据当前选中节点的路径获取目录或文件的详细属性,并显示到界面上。
(5)当双击TreeView控件中的节点时,如果节点是文件,则判断文件类型;如果节点是文本格式文件,则将文本内容加载到界面上。
示例代码9-18是TextReader中与TreeView控件有关的代码,其中LoadLogicDrive()方法将系统中的逻辑分区信息加载到TreeView控件中,LoadDicTree()方法则将选中目录中的子目录和文件加载到该目录节点下。Dic_Expanded()是所有目录节点的Expanded事件处理函数,它首先判断该目录是不是第一次展开,如果是则加载该目录下的信息,否则不加载。
示例代码9-18:
namespace FileViewer
{
/// <summary>
/// Window1.xaml 的交互逻辑
/// </summary>
public partial class WndMain : Window
{
public WndMain( )
{
InitializeComponent( );
}
          /// <summary>
/// 加载指定目录下的文件和目录
/// </summary>
/// <param name="tv">要展开的目录</param>
/// <returns>true则表示加载成功</returns>
private bool LoadDicTree(TreeViewItem tvi)
{
tvi.Items.Clear( );         //清除原有的所有节点(第一次展开标记)  
//获取目录路径
string dicStr = ((string) tvi.Tag).Trim( );
if (Directory.Exists(dicStr))   //如果目录存在
{
//获取目录信息
DirectoryInfo dicInfo = new DirectoryInfo(dicStr);
//遍历目录中所有子目录
foreach (DirectoryInfo item in dicInfo.GetDirectories())
{
//如果为系统目录,则不显示到界面
if ((item.Attributes & FileAttributes.System) ==
FileAttributes.System)
continue;
//创建TreeViewItem对象
TreeViewItem tviDic = new TreeViewItem( );
//保存目录路径到Tag属性
tviDic.Tag = item.FullName;
//设置节点显示文本
tviDic.Header = item.Name;
//节点添加一个子节点"tmp"作为第一次展开标记
tviDic.Items.Add("tmp");
//添加节点Expanded事件处理函数
tvi.Expanded += new RoutedEventHandler(this.Dic_Expanded);
//将节点添加到该父目录下
tvi.Items.Add(tviDic);
}
//遍历目录中所有子文件
foreach (FileInfo item in dicInfo.GetFiles())
{
//创建TreeViewItem对象
TreeViewItem tviFile = new TreeViewItem( );
//保存文件路径到Tag属性
tviFile.Tag = item.FullName;
//设置节点显示文本
tviFile.Header = item.Name;
//将节点添加到父目录下
tvi.Items.Add(tviFile);
}
return true;
}
else                        //目录不存在,不处理
{
return false;
}
}
          //加载系统中逻辑分区信息到目录树
private void LoadLogicDrive( )
{
//遍历所有逻辑分区
foreach (string drv in Directory.GetLogicalDrives())
{
//创建TreeViewItem对象
TreeViewItem tvi = new TreeViewItem( );
//将分区路径保存到Tag属性               
tvi.Tag = drv;
//设置节点显示文本
tvi.Header = drv;
//添加节点Expanded事件处理函数
tvi.Expanded += new RoutedEventHandler(this.Dic_Expanded);
//是目录,则添加"tmp"字符串子节点,用作第一次展开标记
tvi.Items.Add("tmp");               
//将节点添加到目录树
this.tvDic.Items.Add(tvi);               
}
}
          //窗体加载事件处理函数
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.tvDic.Items.Clear( );   //清除目录树中所有节点
this.LoadLogicDrive( );    //加载操作系统中所有逻辑分区
}
          //节点展开事件处理函数
void Dic_Expanded(object sender, RoutedEventArgs e)
{
//获取展开的节点
TreeViewItem tvi = (TreeViewItem) e.Source;
if (tvi.Tag == null)        //如果节点不带有Tag数据,则返回
{
return;
}
if ((tvi.Items.Count == 1) &&   //如果节点Tag数据是string类型且为"tmp"
(tvi.Items[0] is string) && //则是第一次展开,则加载子
目录等信息到树形结构中
((string) tvi.Items[0] == "tmp"))
{
this.LoadDicTree(tvi);      //加载目录的子目录和文件
}
}
          //选中文件或目录发生变化事件处理函数
privatevoidtvDic_SelectedItemChanged(objectsender,RoutedProperty
ChangedEventArgs <object> e)
{
//获取该节点的路径
string dicStr = (string)((TreeViewItem) this.tvDic.SelectedItem).Tag;
if (Directory.Exists(dicStr))   //是一个目录
{
//显示属性面板
this.tabView.Visibility = Visibility.Visible;
//获取目录信息
DirectoryInfo dicInfo = new DirectoryInfo(dicStr);
string hint = "";           //生成目录属性文本
hint += string.Format("目        录:   {0}/n", dicInfo.Name);
hint += string.Format("路        径    {0}/n", dicInfo.FullName);
hint += string.Format("根   目   录:   {0}/n", dicInfo.Root);
hint += string.Format("父   目   录:   {0}/n", dicInfo.Parent);
hint += string.Format("子 目 录 数:   {0}/n", dicInfo.
GetDirectories( ).Length);
hint += string.Format("文   件   数:   {0}/n", dicInfo.
GetFiles( ).Length);
hint += string.Format("创 建 时 间:   {0}/n", dicInfo.CreationTime);
hint += string.Format("最近访问时间:   {0}/n", dicInfo.LastAccessTime);
hint += string.Format("最近修改时间:   {0}/n", dicInfo.LastWriteTime);
hint += string.Format("目 录 属 性:   {0}/n", dicInfo.Attributes);
this.tbProp.Text = hint;    //显示目录属性到界面
}
else if (File.Exists(dicStr))    //是一个文件
{
//显示属性面板
this.tabView.Visibility = Visibility.Visible;
//获取文件属性
FileInfo fileInfo = new FileInfo(dicStr);
string hint = "";           //生成文件属性文本
hint += string.Format("文   件   名:   {0}/n", fileInfo.Name);
hint += string.Format("完 整 路 径:   {0}/n", fileInfo.FullName);
hint += string.Format("扩   展   名:   {0}/n", fileInfo.Extension);
hint += string.Format("父   目   录:   {0}/n", fileInfo.Directory);
hint += string.Format("创 建 时 间:   {0}/n", fileInfo.CreationTime);
hint += string.Format("最近访问时间:   {0}/n", fileInfo.LastAccessTime);
hint += string.Format("最近修改时间:   {0}/n", fileInfo.LastWriteTime);
hint += string.Format("文 件 属 性:   {0}/n", fileInfo.Attributes);
this.tbProp.Text = hint;    //显示文件属性到界面
}
else
{                              
//不是文件也不是目录,隐藏属性面板
this.tabView.Visibility = Visibility.Hidden;
}
}
          //目录树双击事件处理函数
private void tvDic_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
//获取双击发生的节点
TreeViewItem tvi = (TreeViewItem) this.tvDic.SelectedItem;
              if (tvi == null)              //如果节点为空,返回
{
return;
}
              if (tvi.Items.Count > 0)      //是目录,不是文件,返回
{
return;
}
              string fileName = (string) tvi.Tag; //获取文件名
if (!File.Exists(fileName))     //文件不存在,返回
{
return;
}

//获取文件信息
FileInfo fInfo = new FileInfo(fileName);
//根据后缀名判断是否为文本格式的文件
switch (fInfo.Extension.ToUpper( ))     
{
case ".TXT":     //文本文件
case ".LOG":     //日志文件
case ".INI":     //ini配置信息
case ".XML":     //xml文件
case ".XAML":    //xaml代码文件
case ".BAT":     //批处理文件
case ".CPP":     //C++代码文件
case ".C":     //C代码文件
case ".H":     //C、C++头文件
case ".CS":     //C#代码文件
case ".VB":     //VB代码文件
case ".HTML":    //Html网页代码文件
case ".ASPX":    //ASP.NET代码文件
case ".VBS":     //VBScript代码文件
case ".JS":     //JavaScript代码文件
case ".CSS":     //CSS格式定义文件
case ".SLN":     //VS解决方案文件
this.ViewTxtFile(fileName); //加载文本文件到界面上
break;
case ".RTF":
this.ViewRtfFile(fileName); //加载rtf文件到界面上
break;
default:
//提示不支持的文件格式
MessageBox.Show("暂时还不支持该类型文件查看!", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
break;
}
}
}
}
实例TextReader中目录树的运行效果如图9-13所示,其中,界面左边是目录树,没有展开的目录也没有加载任何信息,只有在第一次展开之后才会加载具体的目录信息。界面右边的属性页面显示了当前选中文件或目录的详细属性,通过代码中的tvDic_SelectedItem Changed()方法实现。
第9章  WPF开发实例(续3)9.5  WPF实例--TextReader - hxzon00 - hxzon00的博客
(点击查看大图)图9-13 TextReader目录树运行效果
技巧:在实例TextReader中,目录树加载之所以分目录进行加载,主要是为了提高效率,一次性加载所有目录信息需要等待很长时间,而且并不是所有目录信息都会用到,真正使用的其实是很少的一部分。
9.5.5 使用RichTextBox控件
文本编辑和阅读是应用软件最常用的功能之一,WPF中提供两个最基本的文本输入控件:TextBox控件和RichTextBox控件。 TextBox控件提供简单的文本编辑和阅读支持,可以进行多行显示,也可以设置字体、字号、颜色等信息,但这些信息并不能保存并记录到文件中,TextBox控件的使用相当简单,这里就不再进一步介绍。
顾名思义,RichTextBox控件也是用于文本编辑和阅读的,但是它比TextBox功能强大,它可以编辑RTF格式的文档信息。要加载文档信息到RichTextBox通常需要以下5个步骤:
(1)准备文件路径、文件类型(如文本格式、RTF格式等)信息,确保文件存在等。
(2)获取基于要显示的RichTextBox控件的TextRange对象。
(3)通过FileString用读取的方式打开文件。
(4)通过TextRange用指定的文件格式从文件流加载数据。
(5)加载完成后关闭文件。
示例代码9-19演示了如何加载文件信息到RichTextBox控件中,其中ViewTxtFile()方法加载文本(Text)格式的文件,ViewRtfFile()方法加载RTF(Rich Text Format)格式的文件。
示例代码9-19:
//加载文件内容到RichTextBox控件
private void ViewTxtFile(string fileName)
{
FileStream fStream = null;      //文件流
this.tabView.SelectedIndex = 1; //界面切换到文件内容
      try                               //异常处理
{
TextRange range;            //获取TextRange对象
range = new TextRange(this.rtbText.Document.ContentStart,
this.rtbText. Document. ContentEnd);
//打开文件
fStream = new FileStream(fileName, FileMode.Open);
//加载到RichTextBox控件
range.Load(fStream, DataFormats.Text);
//添加提示信息到状态栏
this.sbiFile.Content = string.Format("打开文件--{0}", fileName);
}
catch (Exception ex)            //捕获异常并提示
{
MessageBox.Show("加载文件发生错误: /n" + ex.Message, "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
}
finally
{
if (fStream != null)        //最终关闭文件
{
fStream.Close( );
}
}
}
//加载文件内容到RichTextBox控件
private void ViewRtfFile(string fileName)
{
FileStream fStream = null;      //文件流
this.tabView.SelectedIndex = 1; //界面切换到文件内容
      try                               //异常处理
{
TextRange range;            //获取TextRange对象
range = new TextRange(this.rtbText.Document.ContentStart,
this.rtbText.Document. ContentEnd);
//打开文件
fStream = new FileStream(fileName, FileMode.Open);
//加载到RichTextBox控件
range.Load(fStream, DataFormats.Rtf);
//添加提示信息到状态栏
this.sbiFile.Content = string.Format("打开文件--{0}", fileName);
}
catch (Exception ex)            //捕获异常并提示
{
MessageBox.Show("加载文件发生错误: /n" + ex.Message, "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
}
finally
{
if (fStream != null)        //最终关闭文件
{
fStream.Close( );
}
}
}
在实例TextReader中,文件浏览的运行效果如图9-14所示,其中,界面左边是目录树,右边的"文件内容"选项卡显示了打开文件的具体内容,包括文字、图形等数据。状态栏提示了打开文件的路径。
第9章  WPF开发实例(续3)9.5  WPF实例--TextReader - hxzon00 - hxzon00的博客
(点击查看大图)图9-14 文件浏览的运行效果
9.6 小结
Windows Presentation Foundation(WPF)是.NET 3.0推出的新组件,并且在.NET 3.5中得到增强,本章首先从WPF的基础概念、体系结构开始,让读者了解WPF的基本原理;然后,介绍WPF的开发模式、XAML代码等知识;最后,通过实例介绍WPF独立应用程序和WPF浏览器应用程序的开发过程。
通过本章的学习,读者应该对WPF有一个简单的了解,并能够用WPF开发出简单的应用程序。读者应该掌握以下知识点:
什么是WPF?WPF有什么作用?
什么是XAML?XAML在WPF开发中起到什么作用?
WPF开发模式是什么样的?
如何进行WPF独立应用程序的开发?
如何使用WPF窗体和控件?
在WPF应用程序中应该如何布局?
如何进行WPF浏览器应用程序的开发?
在WPF浏览器应用程序中如何进行页面的导航?
RichTextBox的使用方法。
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值