前天(2008年4月2日)在
当当网 购买了一本书:
《
Windows Presentation Foundation 程序设计指南 》
[美]
Charles Petzold 著,蔡学镛 译,胡志鹏、魏颢、成功 审校
ISBN: 978-7-121-05115-9,电子工业出版社,2008年1月第1次印刷
Applications = Code + Markup:
A Guide to the Microsoft Windows Presentation Foundation
ISBN: 0-7356-1957-3; Microsoft Press; August 2006; 1002 pages
Errata:
http://www.charlespetzold.com/wpf/errata.xml
于是就开始了WPF学习之旅。
这本书英文原作写得相当好,中文翻译也很好。英文原作是2006年8月出版的,中译本是在2008年1月出版的,出得有点迟了,而且不知为什么中译本出版时没有按照英文版的勘误表进行更正(倒是有好几处审校者的质疑,其实都是英文版的勘误表中已经更正过的)。
目前发现的错误(不断更新中...):
第190页,倒数第2自然段中间:
如果 Stretch 等于 Stretch.Uniform
(默认 ),MeasureOverride 会利用 sizeAvailable 参数计算出
正确的长宽比,并维持这样的比例 ,且让一个维度(宽或高)等于 sizeAvialable 的 Width 或 Height。
英文原文是:
For
Stretch.Uniform (
the default setting ),
MeasureOverride uses the
sizeAvailable argument to calculate
a size that maintains the correct aspect ratio but has one dimension equaling either the
Width or
Height dimension of
sizeavailable .
我认为应该这样翻译:
如果 Stretch 等于 Stretch.Uniform(
这是默认值 ),MeasureOverride 会利用 sizeAvailable 参数计算出
一个尺寸,该尺寸保持正确的长宽比 ,且让一个维度(宽或高)等于 sizeAvailable 的 Width 或 Height。
第219页,程序代码之后的第2自然段,第1句:
除了
override FrameworkElement 与 Control,建立自定义的控件还有别的方式,使用
XAML 比使用代码的方式更常见,
利用样本的定义达到此目的 。
英文原文是:
Besides overriding
FrameworkElement and
Control , there is another approach to creating custom controls that is used much more in XAML than in procedural code.
This is through the definition of a template
.
我认为应该这样翻译:
除了
override FrameworkElement 与 Control,建立自定义的控件还有别的方式,使用
XAML 比使用代码的方式更常见,
就是通过定义一个模板来达到此目的 。
第235页,第2自然段,第2句:
Panel 最大的好处是,
它有 Children Property 可以进行孩子的排序 。
英文原文是:
The big gift of
Panel is
the definition of Children property for storing the children .
我认为应该这样翻译:
Panel 最大的好处是,
它有一个用来存储孩子们的 Children 属性 。
第239页,第5自然段之后,漏了以下一个自然段:
In other words, the grid is behaving as we might hope and expect. To further test it, dig out the
ColorGrid control from the last chapter and replace
UniformGrid with
UniformGridAlmost .
换句话说,grid 的行为可能符合我们的期望和预期。为了进一步测试它,找出上一章的 ColorGrid 控件,并且将其中的所有 UniformGrid 都替换为 UniformGridAlmost。
第451页,
NotePadClone.Help.cs 程序,中间位置:
itemHelp.SubmenuOpened += ViewOnOpen;
这条语句应该删除(英文原版中就错了)。
第466页,第1句:
构造函数为此
XML 资源建立一个 Uri 对象,然后
使用 静态的 Application.GetResourceStream
property ,取得一个 StreamResourceInfo 对象。
这句话是英文原文的错误,不是译者的翻译错误。这句话应该是这样的:
构造函数为此
XML 资源建立一个 Uri 对象,然后
调用 静态的 Application.GetResourceStream
方法 ,取得一个 StreamResourceInfo 对象。
第468页,第1自然段,第2句:
你可以使用此程序,加载本章至今的任何
XAML 文件
(扩展名是 .xml) 。
英文原文是:
You can use this program to load any of the XAML files shown is this chapter so far,
including those with a file name extension of .xml .
我认为应该这样翻译:
你可以使用此程序,加载本章至今的任何
XAML 文件
,包括那些扩展名是 .xml 的文件。(译者注:XAML 文件默认的扩展名是 .xaml)
第490页,
XamlCruncher.cs 程序,中间位置:
split = new GridSplitter();
split.HorizontalAlignment = HorizontalAlignment.Center;
split.VerticalAlignment = VerticalAlignment.Stretch;
split.
Height = 6;
grid.Children.Add(split);
Grid.SetRow(split, 0);
Grid.SetColumn(split, 1);
Grid.SetRowSpan(split, 3);
应改为(英文原版中就错了,但该书网站上提供下载的源程序代码是正确的):
split = new GridSplitter();
split.HorizontalAlignment = HorizontalAlignment.Center;
split.VerticalAlignment = VerticalAlignment.Stretch;
split.
Width = 6;
grid.Children.Add(split);
Grid.SetRow(split, 0);
Grid.SetColumn(split, 1);
Grid.SetRowSpan(split, 3);
第512页,最后1自然段(除了程序代码以外):
然而,GradientStops
property 必须变成一个
property
element :
英文原文是:
The
GradientStops property is of type
GradientStopCollection , so we can put in an object element for that class:
我认为应该这样翻译:
GradientSopts
property 的类型是 GradientStopCollection,于是我们可以将其放到一个
object element 中:
第525页,程序代码之后的第1自然段:
注意 StackPanel
element tag 定义了一个
XML 命名空间的前缀 s,代表 System 命名空间(clr-namespace:System;assembly=mscorlib),这允许
引用此 Resources collection 中的 Double 结构 。
英文原文是:
Notice that the
StackPanel element tag defines an XML namespace prefix of
s for the
System namespace (
clr-namespace:System;assembly=mscorlib ), which allows
referencing the Double structure in the Resources collection .
我认为应该这样翻译:
注意 StackPanel
element tag 定义了一个
XML 命名空间的前缀 s,代表 System 命名空间(clr-namespace:System;assembly=mscorlib),这允许
在此 Resources collection 中引用 Double 结构 。
第569页,程序代码之后的第1自然段,第2句:
然而,Page2 类
没有这一关键的 Navigate 方法,可以用于直接访问 NavigationWindow 对象 。
英文原文是:
However, the
Page2 class
doesn't have direct access to the NavigationWindow object with that crucial Navigate method .
我认为应该这样翻译:
然而,Page2 类
无法直接访问 NavigationWindow 对象,该对象具有关键的 Navigate 方法 。
第571页,最后1自然段,第2句:
但是这里还有一个有趣的实验:在 FrameNavigationDemoWindow.xaml 中,将 Window 改成 NavigationWindow,然后重新编译。
这句话有问题,建议改为:
但是这里还有一个有趣的实验:在 FrameNavigationDemoWindow.xaml 中,将 Window 改成 NavigationWindow,
并添加一个 NavigationWindow.Content property element , 然后重新编译。
第584页底部到第585页顶部,
DirectoryPage.xaml 源代码:
DirectoryPage.xaml <!-- ================================================
DirectoryPage.xaml (c) 2006 by Charles Petzold
================================================ -->
<PageFunction xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:io="clr-namespace:System.IO;assembly=mscorlib"
xmlns:tree="clr-namespace:Petzold.RecurseDirectoriesIncrementally"
x:Class="Petzold.ComputerDatingWizard.DirectoryPage"
x:TypeArguments="io:DirectoryInfo">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" FontSize="16" FontStyle="Italic" HorizontalAlignment="Center"> Computer Dating Wizard </TextBlock>
<tree:DirectoryTreeView x:Name="treevue" Grid.Row="
1 " />
<!-- Buttons at bottom-right corner of page. -->
<Grid Grid.Row="
2 ">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Click="CancelButtonOnClick"
IsCancel="True" MinWidth="60" Margin="6">
Cancel
</Button>
<Button Grid.Column="2" Name="btnOk" Click="OkButtonOnClick"
IsEnabled="False" IsDefault="True" MinWidth="60" Margin="6">
OK
</Button>
</Grid>
</Grid>
</PageFunction>
应改为:
DirectoryPage.xaml <!-- ================================================
DirectoryPage.xaml (c) 2006 by Charles Petzold
================================================ -->
<PageFunction xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:io="clr-namespace:System.IO;assembly=mscorlib"
xmlns:tree="clr-namespace:Petzold.RecurseDirectoriesIncrementally"
x:Class="Petzold.ComputerDatingWizard.DirectoryPage"
x:TypeArguments="io:DirectoryInfo">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<tree:DirectoryTreeView x:Name="treevue" Grid.Row="
0 " />
<!-- Buttons at bottom-right corner of page. -->
<Grid Grid.Row="
1 ">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Click="CancelButtonOnClick"
IsCancel="True" MinWidth="60" Margin="6">
Cancel
</Button>
<Button Grid.Column="2" Name="btnOk" Click="OkButtonOnClick"
IsEnabled="False" IsDefault="True" MinWidth="60" Margin="6">
OK
</Button>
</Grid>
</Grid>
</PageFunction>
下面是修改前后程序运行的屏幕截图:
第585页,程序代码之后的第1自然段,倒数第2句:
另一个命名空间的声明关联起“tree”前缀和第16章 RecurseDirectoriesIncrementally 工程的命名空间。
这句话后面漏了一句:
That's where the DirectoryTreeView class comes from that forms the bulk of this page.
我认为应该这样翻译:
另一个命名空间的声明关联起“tree”前缀和第16章 RecurseDirectoriesIncrementally 工程的命名空间。
而 RecurseDirectoriesIncrementally 命名空间正是此页面主体部分的 DirectoryTreeView 类所在的命名空间。
第586页,程序代码之后的第1自然段,第1句:
构造函数为
DirectoryTree 控件设置
ViewSelectionChanged 事件处理函数,好让
OK 按钮“只有在磁盘驱动器目录被选取的情况下”才会被
enable 。
英文原文是(这句话中的“
SelectionChanged ”应该是“
SelectedItemChanged ”):
The constructor attaches a
SelectionChanged event handler to the
DirectoryTreeView control so that the OK button is enabled only if a disk directory has been selected.
这认为应该这样翻译:
构造函数为
DirectoryTreeView 控件设置
SelectedItemChanged 事件处理函数,好让
OK 按钮“只有在磁盘驱动器目录被选取的情况下”才会被
enable 。
第594页,程序代码之后的第1自然段,第1句:
这两个 Hyperlink
element 包含的不只是 NaviagateUri
attribute (引用两个
XML 文件),也包含了 TargetName
attribute (引用
BookList.xaml 页面
上方 的 Frame)。
英文原文是(这句话中的
“
BookList.xaml ”应该是“
BookReader.xaml ”):
The two
Hyperlink elements contain not only
NavigateUri attributes that reference two XAML files, but also
TargetName attributes that reference the
Frame at the left of the
BookList.xaml page.
我认为应该这样翻译:
这两个 Hyperlink
element 包含的不只是 NaviagateUri
attribute (引用两个
XML 文件),也包含了 TargetName
attribute (引用
BookReader.xaml 页面
左边 的 Frame)。
第600页,第1句:
WindowOnLoaded 事件处理函数会用 Tile 对象和 Empty 对象来为 UniformGrid 初始化。
应该改为(这句话英文原文就是错的):
PageOnLoaded 事件处理函数会用 Tile 对象和 Empty 对象来为 UniformGrid 初始化。
第607页,程序代码之后的第1自然段最后2句:
大多数情况下 Label.Content
tag 不是必须的,你可以试着移除它。但是在这里,确实是必要的。
经实验,在这里,Label.Content
tag 也不是必须的。也就是说,我试着移除它,程序照正常工作。
第609页,最后1自然段,第1句:
现在 ScrollBar
的 目标
是由 Label 源
来控制 。
英文原文是:
Now the
ScrollBar target
is governing the
Label source.
我认为应该这样翻译:
现在 ScrollBar 目标
控制 Label 源。
第612页,除了程序代码这外的第3个自然段(在该页正中间):
这段
XAML 在我看起来好像没问题,但却抛出一个异常,告诉我“
类型为‘System.Windows.Data.Binding’的对象无法转换成‘System.String’ ”,这里显然指的是 Text
property 的类型。此消息对我来说非常奇怪,因为它言下之意是解析器正在忽略这个再自然不过的数据绑定定义。
在我的计算机上,抛出的异常是:
不能在类型“Run”的“Text”属性上设置“Binding”。只能在 DependencyObject 的 DependencyProperty 上设置“Binding”。
这个异常消息再正常不过了。我想本书作者看到的奇怪的异常消息可能是他使用 CTP 版本的 WPF 软件的缘故。
第615页,程序代码之后的第2个自然段,第1句:
第一个 ScrollBar 定义一个 OneWayToSource 绑定,
其 Number property 是第一个 SimpleElement 。
英文原文是:
The first
ScrollBar defines a
OneWayToSource binding with the
Number property of the first
SimpleElement .
我认为应该这样翻译:
第一个 ScrollBar 定义一个 OneWayToSource 绑定,
绑定到第一个
SimpleElement 的 Number property 。
第615页,程序代码之后的第3个自然段,第1句和第2句:
第二个 ScrollBar 定义一个 TwoWay 绑定,
其 Number property 是第一个 SimpleElement ,且你将会看到这个也顺利工作。虽然 SimpleElement 没有显式的通知机制
ScrollBar ,操作第一个 ScrollBar 产生的 Number property 的改变,会被此绑定检测到,结果第二个会跟随第一个 ScrollBar 变化。
英文原文是:
The second
ScrollBar defines a
TwoWay binding
with the Number property of the first SimpleElement
, and you'll see that this one works as well. Although
SimpleElement has no explicit notification mechanism, changes to the
Number property as a result of manipulating the first
ScrollBar are detected in this binding, and the second
ScrollBar tracks the first
ScrollBar .
我认为应该这样翻译:
第二个 ScrollBar 定义一个 TwoWay 绑定,
绑定到第一个
SimpleElement 的 Number property ,且你将会看到这个也顺利工作。虽然 SimpleElement 没有显式的通知机制,操作第一个 ScrollBar 产生的 Number property 的改变,会被此绑定检测到,结果第二个
ScrollBar 会跟随第一个 ScrollBar 变化。
第615页,程序代码之后的第4个自然段,第1句:
第二个 SimpleElement
element 定义了一个 OneWay 数据绑定。
英文原文是:
The second
SimpleElement element defines a
OneWay data binding
with the second ScrollBar
.
我认为应该这样翻译:
第二个 SimpleElement
element 定义了一个 OneWay 数据绑定
,绑定到第二个 ScrollBar 。
第627页,倒数第2自然段,最后2句:
然而,有可能你真正需要的是“被此
字段
property 所引用的对象”的某个
property 。这种情况下,你需要使用绑定。
英文原文是:
However, it's possible that what you really need is a property of the object referenced by the
static property. In that case, you need a binding.
我认为应该这样翻译:
然而,有可能你真正需要的是“被此
静态
property 所引用的对象”的某个
property 。这种情况下,你需要使用绑定。
第631页,从第2自然段开始,到“就这样”。
原书上这一大段是论述如何定义一个名为 DateTimeChanged 的
public 事件,使用
WPF 数据绑定逻辑去更新 DateTime
property 。但是经我实验,这没有效果。
第639页,第4自然段,第1句和2句:
Style 类型定义在 System.Windows 中。其派生自
Object ,且没有子类。
应改为(英文原文就是错的):
Style 类型定义在 System.Windows 中。其派生自
DispatcherObject ,且没有子类。
第646页,程序代码之后的第2自然段:
如果你的应用中有许多对话框,而这些对话框的 Stack Panel 包含一些
radio
buttons (放在
Group
box 内),为了让这些
Radio Button 看起来更美观。RadioButton 的 TargetType 是很好的 Style 范例。
英文原文是:
If you have a bunch of dialog boxes in an application, and these dialog boxes have some radio buttons on stack panels within group boxes,
you know you need to apply a Margin property to each RadioButton
to make it look reasonably attractive. This is an excellent application of a
Style with a
TargetType of
RadioButton .
我认为应该这样翻译:
如果你的应用中有许多对话框,而这些对话框的 Stack Panel 包含一些
radio
buttons (放在
Group
box 内),为了让这些
Radio Button 看起来更美观
,你知道需要为每个 Radio Button 应用 Margin property 。RadioButton 的 TargetType 是很好的 Style 范例。
第652页,除了程序代码之外的倒数第3自然段,第1句:
答案是 Binding 搭配 RelativeSource,
这在本章最后会讨论到 。
英文原文是:
The solution is a
Binding using
RelativeSource , which I discussed toward the end of the previous chapter.
我认为应该这样翻译:
答案是 Binding 搭配 RelativeSource,
这已经在上一章最后讨论过 。
第656页,程序代码之后的第1自然中间:
当 Seal 方法被调用
时 ,只读的
IsSeal
property 被设定为
true 。
英文原文是:
The read-only
IsSealed
property becomes
true when the
Seal method is called,
which happpens when the style is in use .
我认为应该这样翻译:
当
Sytle 被使用时,其 Seal 方法被调用,只读的
IsSealed
property 被设定为
true 。
第677页,程序代码之后的第3自然段,最后1句:
TextBox 与 RichTextBox 控件具有相对简单的模板,因为这两个控件其实是
Border
element ,而
Border 包含 ScrollViewer 控件,ScrollViewer 再包含实际的编辑器,而编辑器则完全使用代码实现。
这句话英文原文就是这样,但在我的计算机上通过 DumpControlTemplate 程序却得到以下结论:
TextBox 与 RichTextBox 控件具有相对简单的模板,因为这两个控件其实是
ListBoxChrome
element ,而
ListBoxChrome 包含 ScrollViewer 控件,ScrollViewer 再包含实际的编辑器,而编辑器则完全使用代码实现。
第679页,除了代码之外的倒数第2自然段,第2句:
ContentPresenter 定义了 ContentTemplate
property (
对象 为 DataTemplate),为的是要定义一个“用来显示内容”的模板。
ContentPresenter defines a property named
ContentTemplate
of type
DataTemplate for defining a template used to display content.
我认为应该这样翻译:
ContentPresenter 定义了 ContentTemplate
property (
类型 为 DataTemplate),为的是要定义一个“用来显示内容”的模板。
第680页,程序代码之后的第2自然段,最后1句:
此 UniformGrid 包含一个 TextBlock (用来显示雇员的名称)和一个 StackPanel (具有
两个 TextBlock
element ,用来显示雇员的生日)。
The
UniformGrid has a
TextBlock for the employee's name and then a
StackPanel with
six
TextBlock elements that effectively format the employee's birth date.
我认为应该这样翻译(英文原文误为“
six ”):
此 UniformGrid 包含一个 TextBlock (用来显示雇员的名称)和一个 StackPanel (具有
五个 TextBlock
element ,用来显示雇员的生日)。
第694页,第2自然段,第1句:
下面的 Resource section ,先定义控件的
layout 。
英文原文是:
Following the Resources section , the definition of the layout of the control begins.
我认为应该这样翻译:
在 Resources section 之后 ,先定义控件的
layout 。
第699页,DatePicker.cs 程序代码,最后一段:
// Buttons are duplicated by PageDown and PageUp keys.
protected override void OnPreviewKeyDown(KeyEventArgs args)
{
base.OnKeyDown(args);
if (args.Key == Key.PageDown)
{
FlipPage(true);
args.Handled = true;
}
else if (args.Key == Key.PageUp)
{
FlipPage(false);
args.Handled =
false ;
}
}
应改为(英文原版就是错的):
// Buttons are duplicated by PageDown and PageUp keys.
protected override void OnPreviewKeyDown(KeyEventArgs args)
{
base.On
Preview KeyDown(args);
if (args.Key == Key.PageDown)
{
FlipPage(true);
args.Handled = true;
}
else if (args.Key == Key.PageUp)
{
FlipPage(false);
args.Handled =
true ;
}
}
第705页,除了程序代码之外的第4自然段:
XPath
property 是一个
XML Path Language 字符串。关于
XML Path Language ,你可以在
www.w3.org/TR/xpath 看到它的
文件 。
英文原文是:
The
XPath property is a string defined in XML Path Language as
documented at
www.w3.org/TR/xpath ,
except that no functions are allowed .
我认为应该这样翻译:
XPath
property 是一个
XML Path Language 字符串
(除了不支持 XPath 函数) 。关于
XML Path Language ,你可以在
www.w3.org/TR/xpath 看到它的
文档 。
第738页,程序代码中部(
NavigationBar.cs ),应改为(其中红色的一对大括号是新增的。英文原版就错了):
void TextBoxOnLostFocus(object sender, KeyboardFocusChangedEventArgs args)
{
int current;
if (Int32.TryParse(txtboxCurrent.Text, out current))
{
if (current > 0 && current <= coll.Count)
collview.MoveCurrentToPosition(current - 1);
}
else
txtboxCurrent.Text = strOriginal;
}