我写《WPF SDK深入研究》一书到了瓶颈部分,因此停下来,看一下Charles Petzold大师写的,很多地方让我茅塞顿开,因此,做读书笔记系列,升华大师的思想,理清自己的思路,同时与大家分享我的心得。

1

       没有WPF新东西,略过。

2

1Background是一个Brush对象,而不是Color对象:

            this.Background = new SolidColorBrush(Colors.Black);

而不可以:

            this.Background = Colors.Black;

2Brush相关的类,都位于System.Windows.Media中:

结构:

Brush(抽象类)

       GradientBrush(抽象类)

              LinearGradientBrush

              RadialGradientBrush

       SolidColorBrush

       TileBrush(抽象类)

              DrawingBrush

              ImageBrush

              VisualBrush

3Freezable

因为Brush继承自Freezable类,实现了Chenged事件,brush只要一有改变,就会触发该事件,客户区就会被重绘。

以下语句

            brush = Brushes.Black;

也能产生

            brush = new SolidColorBrush(Colors.Black);

的效果。唯一的区别,前者是利用Brushes类的静态只读属性取得的SolidColorBrush对象,处于“冻结”状态,不能再被改动——这种“冻结”机制派生于Freezable类,大大提高了效率。

那么,以下语句的搭配就会运行期报错:

            brush = Brushes.Black;

            Color clr = Color.FromRgb(1, 2, 3);

            Brush.Color = clr;

为了解决这个问题,我们要制造一个没有冻结的复制版本,取代第一条语句:

            brush = Brushes.Black.Clone();

其实,这一句,才和new SolidColorBrush(Colors.Black);是等效的。

此外,从SystemColors返回的系统级笔刷对象也都是冻结的。

4)渐变笔刷之:LinearGradientBrush

默认Point坐标使用相对坐标:左上角(0, 0),右下角(1, 1),其间所有Point的坐标都小于1

也可以指定Point使用决定坐标定位,这是一个MappingMode枚举值。我们可以这样设置Brush笔刷对象的这个属性:

            brush.MappingMode = BrushMappingMode.Absolute;   //默认值为RelativeToBoundingBox

LinearGradientBrush的最简单形式,四个参数,两个Color和两个Point,表现为构造函数:

new LinearGradientBrush(Colors.Red, Colors.Black, new Point(0, 0), new Point(1, 1)

Colors.Red参数对应new Point(0, 0)参数,

Colors.Black参数对应new Point(1, 1)参数,

此外,也可以取代Point,而使用angle:以360度为一周。

GradientSpreadMethod枚举:

            brush.SpreadMethod = GradientSpreadMethod.Pad;

负责填充剩余的区域,有三个值(假设由RedBlue渐变):

Pad,默认值,剩余区域延续之前的颜色。

Reflect,剩余区域继续渐变,没有突变,为此要逆转。

Repeat,剩余区域继续渐变,有突变。与上面的区别我会把图补上。

LinearGradientBrush仅有的两个属性:StartPointEndPoint从而确定brush的范围。


GradientStops
属性

GradientStops,为渐变添加更多的颜色,而不仅局限于开始和结束两种,这是一个GradientStopCollection集合,其中的元素是GradientStop

Application=Code+Markup 读书笔记 1-4章_控件            GradientStopCollection gsList = new GradientStopCollection();
Application=Code+Markup 读书笔记 1-4章_控件            gsList.Add(
new GradientStop(Colors.Red, 0.01));
Application=Code+Markup 读书笔记 1-4章_控件            gsList.Add(
new GradientStop(Colors.Black, 0.02));
Application=Code+Markup 读书笔记 1-4章_控件
Application=Code+Markup 读书笔记 1-4章_控件            brush.GradientStops 
= gsList;
Application=Code+Markup 读书笔记 1-4章_控件

GradientStops的构造函数:new GradientStop(Colors, Offset)

这里Offset表示在StartPointEndPoint的方向长度为L的地方。L的计算公式:

L = (EndPoint - StartPoint) * Offset

5)渐变笔刷之:RadialGradientBrush

RadialGradientBrush类似于LinearGradientBrush

没有StartPointEndPoint。

3个属性CenterRadiusXRadiusY,默认值都是0.5

有一个GradientOrigin属性,作为渐变发生的原点,默认值都是(0.5, 0.5)

6)传统.NET中的三种Timer,其中只有System.Windows.Forms中的TimerTick事件是发生在同一个当前主线程中(其他两个TimerSystem.ThreadingSystem.Timers中,它们的Tick事件都发生在不同的线程中)。

WPF中,要使用System.Windows. Threading中的DispatcherTimer,才能在主线程中控制Freeable对象——因为Freeable创建于主线程。

7)总结,Window中有四个属性是Bursh类型的:

BackgroundForeground以及OpacityMaskBorderBrush

3 内容

1ContentWindow最重要的属性,决定在窗口中显示什么东西,Object类型,即可以放置类型对象,当然Window中不能放置Window,因为Window必须是根元素。

2Content属性中的Object分为两类:字符串,派生于UIElement的可视化元素。

WPF中,UIElement很重要,用来实现键盘、鼠标等事件的处理,它有一个OnRender方法。

派生于UIElement的类,重写OnRender方法,可以自定义新的绘图:

Application=Code+Markup 读书笔记 1-4章_控件    public class SimpleEllipse : FrameworkElement
Application=Code+Markup 读书笔记 1-4章_默认值_08Application=Code+Markup 读书笔记 1-4章_控件_09    
Application=Code+Markup 读书笔记 1-4章_控件_10{
Application=Code+Markup 读书笔记 1-4章_抽象类_11        
protected override void OnRender(DrawingContext drawingContext)
Application=Code+Markup 读书笔记 1-4章_抽象类_12Application=Code+Markup 读书笔记 1-4章_控件_13        
Application=Code+Markup 读书笔记 1-4章_控件_10{
Application=Code+Markup 读书笔记 1-4章_抽象类_11            drawingContext.DrawEllipse(Brushes.Red, 
new Pen(Brushes.Black, 24), new Point(00), 1010);
Application=Code+Markup 读书笔记 1-4章_默认值_16        }

Application=Code+Markup 读书笔记 1-4章_默认值_17    }

于是可以直接使用这个SimpleEllipse,并作为Content显示:

            SimpleEllipse elip = new SimpleEllipse();

            Content = elip;

3FrameworkElement是直接派生于UIElement的唯一类。而WPF中所有的元素都派生于FrameworkElement,这样UIElement就做到了最大化的抽象,如果以后有了新的框架,如WPF2,我们可以另建派生于UIElement的类FrameworkElement2,然后重新派生新的元素。

4)不要混淆ContentElementContentControl

ContentControl是控件,具有Content属性。

ContentElement只是其他控件的一部分。


4 按钮及其他控件

1Command命令

基于一个单一点来路由控件的消息,为此在ButtonBase类和MenuItem类中定义了Command属性。我们可以将ButtonCommand属性设置为以下5个类的静态属性:

ApplicationCommandsComponentCommandsMediaCommandsNavigationCommand——以上4个类属于System.Windows.Input

EditingCommands——属于System.Windows.Document

这些类的静态属性都为RoutedUICommand类型,后面将会演示如何自定义一个RoutedUICommand类型。

使用如下:

            button1.Command = ApplicationCommands.Paste;

            CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, PasteOnExecute, PasteCanExecute));

对应的两个事件处理器如下:

Application=Code+Markup 读书笔记 1-4章_控件        void PasteOnExecute(Object sender, ExecutedRoutedEventArgs e)
Application=Code+Markup 读书笔记 1-4章_默认值_08Application=Code+Markup 读书笔记 1-4章_控件_09        
Application=Code+Markup 读书笔记 1-4章_控件_10{
Application=Code+Markup 读书笔记 1-4章_抽象类_11            Title 
= Clipboard.GetText();
Application=Code+Markup 读书笔记 1-4章_默认值_17        }

Application=Code+Markup 读书笔记 1-4章_控件
Application=Code+Markup 读书笔记 1-4章_控件        
void PasteCanExecute(Object sender, CanExecuteRoutedEventArgs e)
Application=Code+Markup 读书笔记 1-4章_默认值_08Application=Code+Markup 读书笔记 1-4章_控件_09        
Application=Code+Markup 读书笔记 1-4章_控件_10{
Application=Code+Markup 读书笔记 1-4章_抽象类_11            e.CanExecute 
= Clipboard.ContainsText();
Application=Code+Markup 读书笔记 1-4章_默认值_17        }

一定要在PasteCanExecute中判断是否支持当前按钮是否有效,比如说当前剪切板中有一幅图片,则PasteCanExecute会将e.CanExecute设置为false,按钮就会立刻被禁用(disabled)

这样,这个ApplicationCommands.Paste可以在很多地方使用,只是在不同地方,指定的处理方法不同

我们可以指定按钮的文字设置为该命令的标准文字,如下显示“粘贴”或“Paste”:

            button1.Content = ApplicationCommands.Paste.Text;

2ToggleButton

ToggleButton不是抽象类,可以用来代替它的子类RadioButtonCheckBox控件。它也有IsChecked属性,表现为按钮凸出还是凹陷两种样式,注意到这是一个可空类型,所以获取到它的值后要进行强制类型转换:

(bool)btn.IsChecked

对于派生于此的RadioButtonCheckBox控件,它们的IsChecked属性也是要这样处理。空值,即“是”与“不是”的第三种选择:“取消选择”,在XAML中表现为

<ToggleButton Name="r"IsChecked="{x:Null}" >

C#中,即:

       r.IsChecked = null;