WPF-图片按钮


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
public class ImageButton:Button
{
static ImageButton() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
///ImageSize NormalImage HowerImage PressedImage BorderVisiblity
///


public double ImageSize
{
get { return (double)GetValue(ImageSizeProperty); }
set { SetValue(ImageSizeProperty, value); }
}

// Using a DependencyProperty as the backing store for ImageSize. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageSizeProperty =
DependencyProperty.Register("ImageSize", typeof(double),
typeof(ImageButton), new FrameworkPropertyMetadata(30.0,FrameworkPropertyMetadataOptions.AffectsRender));



public string NormalImage
{
get { return (string)GetValue(NormalImageProperty); }
set { SetValue(NormalImageProperty, value); }
}

// Using a DependencyProperty as the backing store for NormalImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NormalImageProperty =
DependencyProperty.Register("NormalImage", typeof(string), typeof(ImageButton),
new FrameworkPropertyMetadata("",FrameworkPropertyMetadataOptions.AffectsRender,ImageSourceChanged));


public string HoverImage
{
get { return (string)GetValue(HoverImageProperty); }
set { SetValue(HoverImageProperty, value); }
}

public static readonly DependencyProperty HoverImageProperty =
DependencyProperty.Register("HoverImage", typeof(string), typeof(ImageButton),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender, ImageSourceChanged));



public string PressedImage
{
get { return (string)GetValue(PressedImageProperty); }
set { SetValue(PressedImageProperty, value); }
}

// Using a DependencyProperty as the backing store for PressedImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PressedImageProperty =
DependencyProperty.Register("PressedImage", typeof(string), typeof(ImageButton), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender, ImageSourceChanged));



public string DisabledImage
{
get { return (string)GetValue(DisabledImageProperty); }
set { SetValue(DisabledImageProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisabledImageProperty =
DependencyProperty.Register("DisabledImage", typeof(string), typeof(ImageButton), new FrameworkPropertyMetadata("",FrameworkPropertyMetadataOptions.AffectsRender,ImageSourceChanged));



public Visibility BorderVisibility
{
get { return (Visibility)GetValue(BorderVisibilityProperty); }
set { SetValue(BorderVisibilityProperty, value); }
}

// Using a DependencyProperty as the backing store for BorderVisibility. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BorderVisibilityProperty =
DependencyProperty.Register("BorderVisibility", typeof(Visibility), typeof(ImageButton), new FrameworkPropertyMetadata(Visibility.Hidden,FrameworkPropertyMetadataOptions.AffectsRender));


private static void ImageSourceChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
Application.GetResourceStream(new Uri("pack://application:,,,"+(string)e.NewValue));
}
}

资源样式

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
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:ImageButton.Controls">


<ControlTemplate x:Key="ImageButtonTemplate1" TargetType="{x:Type Controls:ImageButton}">
<Grid x:Name="grid">
<Border x:Name="bg"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="3"
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=BorderVisibility}">
<StackPanel Orientation="Horizontal" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
<Image x:Name="ButtonImage" Source="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=NormalImage}"
Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=ImageSize}"
Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=ImageSize}" ToolTip="{TemplateBinding ToolTip}"/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
</StackPanel>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="ButtonImage" Property="Source" Value="{Binding HoverImage,RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="ButtonImage" Property="Source" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=PressedImage}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="ButtonImage" Property="Source" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=DisabledImage}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="Controls:ImageButton" BasedOn="{x:Null}">
<Setter Property="Padding" Value="3"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="ImageSize" Value="32"/>
<Setter Property="BorderThickness" Value="3"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
<Setter Property="Background" Value="{DynamicResource ButtonBorderBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Template" Value="{StaticResource ImageButtonTemplate1}"/>
</Style>
<Style x:Key="ButtonStyleOne" TargetType="Controls:ImageButton" BasedOn="{StaticResource {x:Type Controls:ImageButton}}">
<Setter Property="NormalImage" Value="/ImageButton;component/Resources/Normal.png"/>
<Setter Property="HoverImage" Value="/ImageButton;component/Resources/Happy.png"/>
<Setter Property="PressedImage" Value="/ImageButton;component/Resources/Tounge.png"/>
<Setter Property="DisabledImage" Value="/ImageButton;component/Resources/Sad.png"/>
<Setter Property="ToolTip" Value="Button Two"/>
</Style>
<Style x:Key="ButtonStyleTwo" TargetType="Controls:ImageButton" BasedOn="{StaticResource ButtonStyleOne}">
<Setter Property="NormalImage" Value="/ImageButton;component/Resources/Happy.png"/>
<Setter Property="HoverImage" Value="/ImageButton;component/Resources/Tounge.png"/>
<Setter Property="PressedImage" Value="/ImageButton;component/Resources/Surprize.png"/>
<Setter Property="ToolTip" Value="Button Two"/>
</Style>
<Style x:Key="ButtonStyleThree" TargetType="Controls:ImageButton"
BasedOn="{StaticResource ButtonStyleTwo}">
<Setter Property="NormalImage" Value="/ImageButton;component/Resources/Normal.png"/>
<Setter Property="ToolTip" Value="Button Three"/>
<Setter Property="BorderVisibility" Value="Visible"/>
</Style>
</ResourceDictionary>

<ResourceDictionary x:Class="Consilium.Mobile.Tablet.Theme.ThemeDictionary"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Color x:Key="WindowsBackgroundColor1">#FF000000</Color>
<Color x:Key="WindowsBackgroundColor2">#FF1A1A1A</Color>

<LinearGradientBrush x:Key="WindowBackground" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="{StaticResource WindowsBackgroundColor1}" Offset=".5"/>
<GradientStop Color="{StaticResource WindowsBackgroundColor2}" Offset="1"/>
</LinearGradientBrush>

<SolidColorBrush x:Key="TextBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ButtonBackgroundBrush" Color="#FF999999" />
<SolidColorBrush x:Key="ButtonBorderBrush" Color="#FF333333" />
</ResourceDictionary>
<Application x:Class="ImageButton.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ImageButton"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ImageButton;component/Themes/ImageButton.xaml"/>
<ResourceDictionary Source="/ImageButton;component/Themes/Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

Xaml

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
<Window x:Class="ImageButton.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ImageButton" xmlns:controls="clr-namespace:ImageButton.Controls"
mc:Ignorable="d"
Title="MainWindow" Background="{DynamicResource WindowBackground}" ResizeMode="NoResize">
<ScrollViewer Width="500" Height="500">
<StackPanel>
<controls:ImageButton Style="{DynamicResource ButtonStyleOne}"/>
<controls:ImageButton Style="{DynamicResource ButtonStyleOne}" BorderVisibility="Visible"/>
<controls:ImageButton Style="{DynamicResource ButtonStyleOne}"
BorderVisibility="Visible" IsEnabled="False"/>

<controls:ImageButton Style="{DynamicResource ButtonStyleOne}" Content="Test Button"/>
<controls:ImageButton Style="{DynamicResource ButtonStyleOne}" BorderVisibility="Visible" Content="Test Button"/>
<controls:ImageButton Style="{DynamicResource ButtonStyleOne}"
BorderVisibility="Visible" IsEnabled="False" Content="Test Button"/>
<controls:ImageButton Style="{DynamicResource ButtonStyleTwo}"/>
<controls:ImageButton Style="{DynamicResource ButtonStyleThree}"/>
</StackPanel>
</ScrollViewer>
</Window>

自定义控件

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Tempeture
{
public class AddBfh : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
float f = (float)value;
return string.Format("{0}℃",f);
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Temp:Button
{
static Temp() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(Temp),new FrameworkPropertyMetadata(typeof(Temp)));
}

#region 基本控件
TextBlock txtMaxValue = null;
TextBlock txtMinValue = null;
TextBlock txtCurrentValue = null;
TextBlock txtPostion = null;
Rectangle rect = null;
#endregion
#region 设置绑定
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
AddBfh addBfh = new AddBfh();
txtMaxValue = GetTemplateChild("MaxValue") as TextBlock;
txtMinValue = GetTemplateChild("MinValue") as TextBlock;
txtCurrentValue=GetTemplateChild("CurrentValue") as TextBlock;
txtPostion=GetTemplateChild("Positions") as TextBlock;
rect = GetTemplateChild("RectValue") as Rectangle;

txtMaxValue.SetBinding(TextBlock.TextProperty,new Binding("MaxValue") { Source=this,Converter=addBfh});
txtMinValue.SetBinding(TextBlock.TextProperty, new Binding("MinValue") { Source=this,Converter=addBfh});

txtCurrentValue.SetBinding(TextBlock.TextProperty,new Binding("CurrentValue") { Source=this,Converter=addBfh});
txtPostion.SetBinding(TextBlock.TextProperty, new Binding("MeterPosition") { Source=this});
rect.SetBinding(Rectangle.HeightProperty, new Binding("CurrentValue") { Source = this }) ;
}

#endregion
#region 最小值


public float MinValue
{
get { return (float)GetValue(MinValueProperty); }
set { SetValue(MinValueProperty, value); }
}

// Using a DependencyProperty as the backing store for MinValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MinValueProperty =
DependencyProperty.Register("MinValue", typeof(float), typeof(Temp), new PropertyMetadata(0.0f));


#endregion
#region 最大值


public float MaxValue
{
get { return (float)GetValue(MaxValueProperty); }
set { SetValue(MaxValueProperty, value); }
}

// Using a DependencyProperty as the backing store for MaxValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MaxValueProperty =
DependencyProperty.Register("MaxValue", typeof(float), typeof(Temp), new PropertyMetadata(100.0f));


#endregion
#region 当前值


public float CurrentValue
{
get { return (float)GetValue(CurrentValueProperty); }
set { SetValue(CurrentValueProperty, value); }
}

// Using a DependencyProperty as the backing store for CurrentValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentValueProperty =
DependencyProperty.Register("CurrentValue", typeof(float), typeof(Temp), new PropertyMetadata(50f,CurrentValueChange));

private static void CurrentValueChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as Temp).StoryboardPlay(e);
}

private void StoryboardPlay(DependencyPropertyChangedEventArgs e)
{
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation();
da.To = double.Parse(e.NewValue.ToString());
da.Duration = new Duration(TimeSpan.Parse("0:0:1"));
rect.BeginAnimation(Rectangle.HeightProperty,da);
}


#endregion
#region 位置信息


public string MeterPosition
{
get { return (string)GetValue(MeterPositionProperty); }
set { SetValue(MeterPositionProperty, value); }
}

// Using a DependencyProperty as the backing store for MeterPosition. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MeterPositionProperty =
DependencyProperty.Register("MeterPosition", typeof(string), typeof(Temp), new PropertyMetadata("Bash"));


#endregion
}
}

资源样式

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
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Tempeture">
<Style TargetType="{x:Type local:Temp}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Temp}">
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="Tempreature" Width="250" Height="280" Clip="F1 M 0,0L 250,0L 250,280L 0,280L 0,0">
<Canvas x:Name="background" Width="250" Height="280" Canvas.Left="0" Canvas.Top="0">
<Path x:Name="Path" Width="230" Height="230" Canvas.Left="10" Canvas.Top="4.99991" Stretch="Fill" Data="F1 M 20,4.99991L 230,4.99991C 235.523,4.99991 240,9.47702 240,14.9999L 240,225C 240,230.523 235.523,235 230,235L 20,235C 14.4772,235 10,230.523 10,225L 10,14.9999C 10,9.47702 14.4772,4.99991 20,4.99991 Z ">
<Path.Fill>
<LinearGradientBrush StartPoint="0.5,1.25168" EndPoint="0.5,-0.25168">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#FF000000" Offset="0"/>
<GradientStop Color="#FF4C4E4C" Offset="0.748837"/>
<GradientStop Color="#FF989D98" Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Path.Fill>
<Path.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="3.77953" Opacity="0.599998" Color="#FF000000" Direction="315"/>
</Path.Effect>
</Path>
<Viewbox x:Name="Group" Width="58" Height="191" Canvas.Left="15" Canvas.Top="29">
<Canvas Width="58" Height="191">
<Path x:Name="Path_0" Width="20" Height="147" Canvas.Left="18.875" Canvas.Top="1.726" Stretch="Fill" StrokeThickness="6" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Stroke="#FF96C240" Fill="#0096C240" Data="F1 M 25.875,4.72554L 25.875,4.72554C 29.741,4.72554 32.875,7.85956 32.875,11.7255L 32.875,138.725C 32.875,142.591 29.741,145.725 25.875,145.725L 25.875,145.725C 22.009,145.725 18.875,142.591 18.875,138.725L 18.875,11.7255C 18.875,7.85956 22.009,4.72554 25.875,4.72554 Z ">
<Path.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="3.77953" Opacity="0.599998" Color="#FF000000" Direction="315"/>
</Path.Effect>
</Path>
<Ellipse x:Name="Ellipse" Width="49.647" Height="46.889" Canvas.Left="4.052" Canvas.Top="136.683" Stretch="Fill" Fill="#FF96C240">
<Ellipse.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="3.77953" Opacity="0.599998" Color="#FF000000" Direction="315"/>
</Ellipse.Effect>
</Ellipse>
<Rectangle x:Name="RectValue" Height="131" Canvas.Left="19.875" Canvas.Top="7" Width="18" Fill="#FF96C240" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</Viewbox>
<Grid Width="230" Height="37" Canvas.Left="10" Canvas.Top="235">
<TextBlock x:Name="Positions" FontSize="25" HorizontalAlignment="Center" VerticalAlignment="Center">
</TextBlock>
</Grid>
<Grid Width="130" Height="49" Canvas.Left="98" Canvas.Top="72">
<TextBlock x:Name="CurrentValue" Text="123℃" Foreground="#FF96C240" FontSize="50" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Effect>
<DropShadowEffect/>
</TextBlock.Effect>
</TextBlock>
</Grid>
<Grid Width="60" Height="31" Canvas.Left="60" Canvas.Top="14">
<TextBlock x:Name="MaxValue" FontSize="20" Foreground="#FF96C240" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Effect>
<DropShadowEffect/>
</TextBlock.Effect>
</TextBlock>
</Grid>
<Grid Width="60" Height="31" Canvas.Left="60" Canvas.Top="144">
<TextBlock x:Name="MinValue" Text="100℃" FontSize="20" Foreground="#FF96C240" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Effect>
<DropShadowEffect/>
</TextBlock.Effect>
</TextBlock>
</Grid>
</Canvas>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

<Window x:Class="Sample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Sample" xmlns:tempeture="clr-namespace:Tempeture;assembly=Tempeture"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<tempeture:Temp x:Name="myMeter" MaxValue="100"/>
<TextBox Text="{Binding ElementName=myMeter,Path=CurrentValue,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Window>

WPF-页面和导航


导航应用程序演绎

1.将导航的内容寄宿于窗口中
2.Xaml浏览器应用程序 扩展名.xbap 可以直接在浏览器中运行.

4个核心

1.页面:WPF将导航内容封装为多个页面.
2.超链接
3.NavigationServices
4.Journal 每次连接通过Jourmal记录作为历史记录.

Page的宿主窗口

Page的宿主窗口包括浏览器,导航窗口和Frame.
NavigationWindow是一个顶层窗口,不允许嵌入到其他控件中,而Frame则为轻量级,可以嵌入到其他控件,如NavigationWindow或者Page,甚至其他Frame中.Frame默认没有导航工具栏,可以设置其NavigationUIVisiblity属性为Visible使其工具栏是否可见.

导航链接

超链接

1
2
<HyperLink NavigateUri="Page2.xaml">开始阅读路由事件
</HyperLink>

段落导航 NavigateUri的设置方法是”页面名#元素名”
HyperLink Click NavigationService Navigate方法导航.

1
2
3
private Hyperlink_Click(object sender,RoutedEventArgs e){
NavigationService.Navigate(newUri("pack://application:,,,/Page2.xaml"));
}

其他方式导航

NavigationCommands.BrowseBack NavigationCommands.BrowseForward

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<Page x:Class="WpfApp1.Pages.LoginPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1.Pages"

Title="登陆页面" ShowsNavigationUI="True" WindowTitle="登录页面" Loaded="Page_Loaded" PreviewLostKeyboardFocus="Page_PreviewLostKeyboardFocus">

<Border BorderBrush="Black" BorderThickness="2" Margin="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="6*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="用户名" Margin="5"/>
<TextBox Style="{x:Null}" x:Name="name" Grid.Column="1" Margin="5"/>
<TextBlock Grid.Row="1" Text="密码" Margin="5"/>
<PasswordBox x:Name="password" Grid.Row="1" Grid.Column="1" Margin="5"/>
<Button x:Name="btn" Grid.Row="2" Height="25" Width="80" HorizontalAlignment="Left" Margin="5 0 0 0"
Click="btn_Click" Content="登录"/>
<TextBlock Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center">
<Hyperlink NavigateUri="RegisterPage.xaml">
忘记密码了... ...
</Hyperlink>
</TextBlock>
<TextBlock Margin="0" Grid.Row="3" Grid.ColumnSpan="2" x:Name="hyperlinktext"
HorizontalAlignment="Center" VerticalAlignment="Center">
如果没有注册,请单击
<Hyperlink Click="Hyperlink_Click">
注册
</Hyperlink>
页面
<LineBreak/>
</TextBlock>
</Grid>
</Border>
</Page>

public partial class LoginPage : Page
{
public LoginPage()
{
InitializeComponent();
}


public string FocusElement
{
get { return (string)GetValue(FocusElementProperty); }
set { SetValue(FocusElementProperty, value); }
}

// Using a DependencyProperty as the backing store for FocusElement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FocusElementProperty =
DependencyProperty.Register("FocusElement", typeof(string), typeof(LoginPage), new FrameworkPropertyMetadata(null,FrameworkPropertyMetadataOptions.Journal));


private void btn_Click(object sender, RoutedEventArgs e)
{
List<User> users =((App) App.Current).users;
int usercount = users.Count;
User user = new User(name.Text,password.ToString());
for (int i = 0; i < usercount; i++)
{
if (name.Text == users[i].Name && password.Password == users[i].Password) {
WelcomePage page = new WelcomePage(user,false);
NavigationService.Navigate(page);
return;
}
}
NavigationService.Navigate(new Uri("pack://application:,,,/Pages/ErrorPage.xaml"));
}

private void Page_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (e.NewFocus==this.name || e.NewFocus==this.password)
{
this.FocusElement=(string)(((DependencyObject)e.NewFocus).GetValue(FrameworkElement.NameProperty));

}
}

private void Page_Loaded(object sender, RoutedEventArgs e)
{
if (this.FocusElement != null) {
IInputElement element = (IInputElement)LogicalTreeHelper.FindLogicalNode(this,this.FocusElement);
Keyboard.Focus(element);
}

}

private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
RegisterPage registerPage = new RegisterPage();
registerPage.Return += RegisterPage_Return;
this.NavigationService.Navigate(registerPage);
}

private void RegisterPage_Return(object sender, ReturnEventArgs<User> e)
{
if (e == null) {
return;
}
User user = e.Result;
if (user != null) {
this.name.Text = user.Name;
this.password.Password = password.Password;
}
List<User> list = ((App)(App.Current)).users;
list.Add(user);
}
}

<PageFunction x:Class="WpfApp1.Pages.RegisterPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1.Pages" xmlns:bookmodels="clr-namespace:BookModels;assembly=BookModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="RegisterPage"
x:TypeArguments="bookmodels:User">

<Border BorderBrush="Black" BorderThickness="2" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>

<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="用户名" Margin="5"/>
<TextBox x:Name="name" Grid.Column="1" Margin="5"/>
<TextBlock Grid.Row="1" Text="密码" Margin="5"/>
<PasswordBox x:Name="password" Grid.Row="1" Grid.Column="1" Margin="5"/>
<TextBlock Grid.Row="2" Text="再次输入密码" Margin="5"/>
<PasswordBox x:Name="Secondpassword" Grid.Row="2" Grid.Column="1" Margin="5"/>
<StackPanel Orientation="Horizontal" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button Content="注册" Margin="30 0 0 0" Click="Button_Click"/>
<Button Content="取消" Margin="30 0 30 0" Command="Close"/>
</StackPanel>

</Grid>
</Border>
</PageFunction>

public partial class RegisterPage : PageFunction<User>
{
public RegisterPage()
{
InitializeComponent();
user = new User();
}

private User user;

private void Button_Click(object sender, RoutedEventArgs e)
{
User user = CreateUser();
if (user == null) return;
else
OnReturn(new ReturnEventArgs<User>(user));
}

private User CreateUser()
{
if (string.IsNullOrEmpty(name.Text) || string.IsNullOrEmpty(password.Password)||
string.IsNullOrEmpty(Secondpassword.Password) || password.Password!=Secondpassword.Password)
{
BiaoZhuWindow biaoZhu=new BiaoZhuWindow();
biaoZhu.mes.Content = "用户名或密码有误!";
biaoZhu.Show();
return null;
}
user = new User() { Name=name.Text,Password=Secondpassword.Password};
return user;
}
}

WPF-Command命令


Command

1
2
3
<Window.CommandBindings>
<CommandBinding Command="Save" Executed="SaveExecuted" CanExecute="SaveCanExecute"/>
</Window.CommandBindings>

CommandBinding有两个重要事件,执行关联的Command时,会触发该Executed事件的处理函数,触发CanExecute事件时,相应的事件处理函数需要将传递来的参数e的CanExecute属性设为true或false.告知WPF命令系统该命令是否可用.

Command模型

WPFCommand模型包含如下4部分:
1.Command:应用程序需要执行的任务.
2.CommandBinding:连接Comamnd和特定应用程序逻辑,如前面CommandBinding连接Save命令及其处理函数.
3.Command Source:触发Command的对象,如前述的菜单栏和工具栏,单击可以触发绑定的Command.
4.Command target:Command应用再上面的对象.如前述的TextBox.

Command

1.ICommand接口
一个Command代表应用程序的任务.在WPF中所有Command都要实现ICommand接口,该接口有两个方法(Executed CanExecute)和一个事件(CanExecuteChanged)
1.Execute方法:当Command被触发时调用该方法,执行与命令相对应的操作.
2.CanExecute方法:用来判断该命令是否可应用到当前Command target上,如果该方法返回true可以
3.CanExecutedChanged事件:Command有执行或者不执行两种状态,状态改变时触发该事件.一般监听该事件的是CommandSource,它监听到该事件后会调用绑定的Command的CanExecute方法检查当前Command的状态,然后决定然后决定CommandSource是启用还是禁用.
Recommand的InputGuestures属性用来指定鼠标和键盘动作,从而可以关联命令和相应的鼠标键盘动作.
WPF内置的Command库
ApplicationCommands NavigationCommands EditingCommands ComponentCommands MeidaCommands

Command Sources

CommandSource是能触发Command的对象按钮和菜单,包括键盘和鼠标操作都可以是Command Source.
CommandSource必须实现ICommandSource接口,该接口定义了3个属性
1.Command:Command Source触发的Command.
2.CommandParameter:Command执行时需要的参数.大多数情况下命令的执行不需要参数.
3.CommandTarget:Command应用对象.
WPF中实现ICommandSource接口的有ButtonBase,MenuItem,Hyperlink和InputBinding.

Commanding

CommandBinding关联Command和实现Command的事件处理函数
CommandBinding类包括Command属性,以及PreviewExecuted,Executed,PreviewCanExecute和CanExecute事件.如果一个元素派生自UIElement,ContentElement和UIElement3D之一,那么CommandBinding对象可以添加到该元素中.

1
2
3
4
5
6
7
8
<Window.CommandBindings>
<CommandBinding Command="Close" Executed="CloseCommand"/>
<ComandBinding Command="Save" Executed="SaveCommand"/>
</Window.CommandBindings>

CommandBinding SaveCommandBinding=new CommandBinding(ApplicationCommands.Save,SaveExecuted,SaveCanExecute);
this.CommandBindings.Add(SaveCommandBinding);

Command Target

Command Target是Command会应用在上面的对象.ComandSource 需要实现ICommandSource接口,其中就有一个属性是CommandTarget.当CommandSource不明确指定Command Target属性时,会把当前获得的焦点的元素认为是Comand Target.

非规则窗口

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
<Window x:Class="WpfApp1.BiaoZhuWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1.Pages"
Title="BiaoZhuWindow"
Background="Transparent" AllowsTransparency="True" WindowStyle="None"
SizeToContent="WidthAndHeight" MouseLeftButtonDown="Window_MouseLeftButtonDown">
<Window.CommandBindings>
<CommandBinding Command="Close" Executed="Close"/>
</Window.CommandBindings>
<!--窗口的形状都绘制在Canvas面板中面-->
<Canvas Width="200" Height="200">
<!--窗口的轮廓-->
<Path Stroke="DarkGray" StrokeThickness="2">
<Path.Fill>
<LinearGradientBrush StartPoint="0.2,0" EndPoint="0.8,1">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="White" Offset="0.45"/>
<GradientStop Color="LightBlue" Offset="0.9"/>
<GradientStop Color="Gray" Offset="1"/>
</LinearGradientBrush>
</Path.Fill>
<Path.Data>
<PathGeometry>
<PathFigure StartPoint=" 40,20" IsClosed="True">
<LineSegment Point="160,20"/>
<ArcSegment Point="180,40" Size="20,20" SweepDirection="Clockwise"/>
<LineSegment Point="180,80"/>
<ArcSegment Point="160,100" Size="20,20" SweepDirection="Clockwise"/>
<LineSegment Point="90,100"/>
<LineSegment Point="60,150"/>
<LineSegment Point="60,100"/>
<LineSegment Point="40,100"/>
<ArcSegment Point="20,80" Size="20,20" SweepDirection="Clockwise"/>
<LineSegment Point="20,40"/>
<ArcSegment Point="40,20" Size="20,20" SweepDirection="Clockwise"/>

</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<!--拖拽我的标签-->
<Label FontSize="15" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Width="200" Height="120" x:Name="mes" Content="早上好!"/>
<!--关闭按钮-->
<Button Command="Close" Canvas.Left="155" Canvas.Top="30">
<Button.Template>
<ControlTemplate>
<Canvas>
<Rectangle Width="15" Height="15" Stroke="Black"
RadiusX="3" RadiusY="3">
<Rectangle.Fill>
<SolidColorBrush x:Name="myAnimateBrush" Color="Red"/>
</Rectangle.Fill>


</Rectangle>
<Line X1="3" Y1="3" X2="12" Y2="12" Stroke="White" StrokeThickness="2"/>
<Line X1="12" Y1="3" X2="3" Y2="12" Stroke="White" StrokeThickness="2"/>
</Canvas>
</ControlTemplate>
</Button.Template>
</Button>
</Canvas>
</Window>

WPF-路由事件


回顾.NET事件模型

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
namespace BookModels
{
public delegate void WhiteBee(string param);//声明委托
public class XiaoLongNv
{
public event WhiteBee WhiteBeeEvent;//玉峰事件
private string msg = "I am in the bottom of JueQingGu";
public void OnFlyBee() {
Console.WriteLine("XiaoLongNv fly the bee in the bottom of JueQingGu,Hope Yangguo one day see.");
WhiteBeeEvent(msg);
}
}
public class LaoWantong{
public void ProcessBeeLetter(string msg) {
Console.WriteLine("LaoWantong: Samll Bee,Small Bee,dont fly.");
}
}
public class HuangRong {
public void ProcessBeeLetter(string msg) {
Console.WriteLine("Huangrong:\"{0}\",that is ...",msg);
}
}
public class YangGuo {
public void ProcessBeeLetter(string msg)
{
Console.WriteLine("Yangguo:\"{0}\",I Will get together with her!",msg);
}

public void Sign() {
Console.WriteLine("YangGuo sadly say:Longer,where are you... ...");
}
}
}
XiaoLongNv longNv = new XiaoLongNv();
LaoWantong laoWantong = new LaoWantong();
HuangRong huangRong = new HuangRong();
YangGuo yangGuo = new YangGuo();

longNv.WhiteBeeEvent += laoWantong.ProcessBeeLetter;
longNv.WhiteBeeEvent+=huangRong.ProcessBeeLetter;
// longNv.WhiteBeeEvent+=yangGuo.ProcessBeeLetter;//没有订阅事件
longNv.OnFlyBee();
yangGuo.Sign();

不过这种事件看起来不像.Net事件.改写后

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
namespace BookModels
{
public class WhiteBeeEventArgs : EventArgs
{
public readonly string _msg;
public WhiteBeeEventArgs(string msg)
{
_msg = msg;
}

}

public class XiaoLongNv2 {
public event WhiteBeeEventHandler WhiteBeeEvent;
private string msg = "I am in the bottom of JueQingGu";
public void OnFlyBee()
{
Console.WriteLine("XiaoLongNv fly the bee in the bottom of JueQingGu,Hope Yangguo one day see.");
WhiteBeeEventArgs args = new WhiteBeeEventArgs(msg);
WhiteBeeEvent(this, args);
}
}
public class LaoWantong2
{
public void ProcessBeeLetter(object sender,WhiteBeeEventArgs e)
{
Console.WriteLine("LaoWantong: Samll Bee,Small Bee,dont fly.");
}
}
public class HuangRong2
{
public void ProcessBeeLetter(object sender,WhiteBeeEventArgs e)
{
Console.WriteLine("Huangrong:\"{0}\",that is ...", e._msg);
}
}
public class Yangguo2 {
public void ProcessBeeLetter(object sender,WhiteBeeEventArgs e)
{
XiaoLongNv2 xiaoLongNv2 = sender as XiaoLongNv2;
if (xiaoLongNv2 != null) {
Console.WriteLine("Yangguo:\"{0}\",I Will get together with her!",e._msg);
}

}

public void Sign()
{
Console.WriteLine("YangGuo sadly say:Longer,where are you... ...");
}
}
//声明委托
public delegate void WhiteBeeEventHandler(object sender, WhiteBeeEventArgs e);
}
XiaoLongNv2 xiaoLongNv2 = new XiaoLongNv2();
LaoWantong2 laoWantong2 = new LaoWantong2();
HuangRong2 huangRong2=new HuangRong2();
Yangguo2 yangguo2 = new Yangguo2();

XiaoLongNv2 xiaoLongNv21 = new XiaoLongNv2();
xiaoLongNv21.WhiteBeeEvent+=laoWantong2.ProcessBeeLetter;
xiaoLongNv21.WhiteBeeEvent += huangRong2.ProcessBeeLetter;
xiaoLongNv21.OnFlyBee();
yangguo2.Sign();

1.委托类型名称以EventHandler结束,原型有一个void返回值并接受两个输入参数,即一个Object类型和一个EventArgs类型(或继承自EventArgs)

1
public delegate void WhiteBeeEventHandler(object sender,WhiteBeeEventArgs e);

2.事件的命名为委托去掉EventHandler之后剩余的部分.

1
public event WhiteBeeEventHandler WhiteBee;

3.继承自EventArgs的类型应该以EventArgs结尾.

1
2
3
4
5
6
public class WhiteBeeEventArgs:EventArgs{
public readonly string _msg;
public WhiteBeeEventArgs(string msg){
this._msg=msg;
}
}

路由事件的定义

以Button的Click事件为例.该事件是个路由事件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class ButtonBase:ContentControl,ICommandSource{
//路由事件的定义
public static readonly RoutedEvent ClickEvent;
//传统的事件包装器
public event RoutedEventHandler Click{
add{
base.AddHandler(ClickEvent,value);
}
remove{
base.Removehandler(ClickEvent,value);
}
}
//事件的注册
static ButtonBase(){
ClickEvent=EventManager.RegisterRoutedEvent("Click",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(ButtonBase));
}
}

同依赖属性一样,路由事件也需要注册,不同的是使用EventManager.RegisterRoutedEvent方法.
同依赖属性,用户不会直接使用路由事件,而是使用传统的Clr事件.有两种方式关联事件及其处理函数.
在代码中,仍然按照原方法关联和解除关联事件处理函数(+=/-=).

1
2
3
4
5
6
7
8
9
10
11
12
13
Button b2=new Button();
//关联事件及其处理函数
b2.Click+=new RoutedEventHandler(Onb2Click);
//事件处理函数
void Onb2Click(object sender,RoutedEventArgs e){

}
*********************************************************
Button b2=new Button();
//关联事件及其处理函数
b2.AddHandler(Button.ClickEvent,new RoutedEventHandler(Onb2Clcik));
//事件处理函数

传统的事件触发往往直接调用其委托(因为事件的本质是委托),而路由事件则通过一个RaiseEvent方法触发,调用该方法后所有关联该事件的对象都会得到通知.

1
2
RoutedEventArgs e=new RoutedEventArgs(ClickEvent,this);
base.RaiseEvent(e);

路由事件通过EventManager.RegisterRoutedEvent方法注册,通过AddHandler和RemoveHandler来关联和解除关联的事件处理函数,通过RaiseEvent方法来触发事件,通过传统的CLR事件封装后供用户调用,使得用户同使用传统的CLR事件一样使用路由事件.

路由事件的旅行

1.路由事件的旅行策略
旅行当中,一般只出现两种角色:1.事件源.由其触发事件,是路由事件的起点.2.是事件监听者,通常针对监听的事件有一个相应的事件处理函数,当路由事件经过事件监听者,就好比经过一个客栈,要做短暂停留,由事件处理函数来处理该事件.
路由事件的策略有如下三种:
1.Bubbing:事件从事件源出发一路上溯直到根节点,很多路由事件使用该策略.
2.Direct 事件从事件源出发,围绕事件源转一圈结束.
3.Tunneling 事件源触发事件后,事件从根节点出发下沉直到事件源.

改变旅行策略因素之一 事件处理函数

最基本的路由事件处理函数原型
public delegate void RoutedEventHandler(Object sender,RoutedEventArgs e)
事件处理函数之间有微小差异.鼠标事件的处理函数原型
public delegate void MouseEventhandler(Object sender,MouseEventArgs e)
这种事件处理函数有如下两个特点
1.返回原型为void.
2.有两个参数,第一个是一个Object类型的对象.表示拥有该事件处理函数的对象.第二个是RoutedEventArgs或者是RoutedEventArgs的派生类.带有其路由事件的信息.
RoutedEventArgs结构包括4个成员变量.
Source 表明触发事件的源 如当键盘事件发生时,触发事件的源是当前获得焦点的对象.当鼠标事件发生时,触发事件的源是鼠标所在的最上层对象
OriginalSource 表明触发事件的源.一般来说OriginalSource和Source相同,区别在于Source表示逻辑树上的元素;OriginalSource是可视树中的元素.如单击窗口的边框,Source为Window,OriginalSource为Border.
RoutedEvent 路由事件对象
Handled 布尔值 为true 表示该事件已处理,这样可以停止路由事件.
Handled属性是改变路由事件旅行的元凶,一旦在某个事件处理函数中将Handled的值设置为true.路由事件就停止传递.
一个事件被标记为处理,事件处理函数则不可处理该事件,WPF提供了一种机制,即使事件被标记为处理,事件处理函数仍然可以处理.但是关联事件及其处理函数需要稍作处理.AddHandler重载了两个方法,其中之一如下,需要将第三个参数设为true.
public void Addhandler(RoutedEvent routedEvent,Delegate handler,bool handledEventsToo)

改变旅行策略因素之二 类和实例事件处理函数

事件处理函数有两种类型,一是前面所说的普通事件处理函数.二是通过EventManager.RegisterClassHandler方法将一个事件处理函数和一个类关联起来,这种事件处理函数,
称为类事件处理函数.其优先权高于前者.也就是说事件在旅行时,会先光临类事件处理函数,然后再光临类事件处理函数.

路由事件示例

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
public class MySimpleButton:Button{
static MySimpleButton(){}
//创建和注册该事件,该事件路由策略为Bubble.
public static readonly RoutedEvent CustomClickEvent=EventManager.RegisterRoutedEvent(
"CustomClick",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(MySimpleButton)
);
//Clr事件的包装器、
public event RoutedEventHandler CustomClick{
add{AddHandler(CustomClickEvent,value);}
remove{RemoveHandler(CustomClickEvent,value);}
}
//触发CustomClickEvent
void RaiseCustomClickEvent(){
RoutedEventArgs newEventArgs=new RoutedEventArgs(MySimpleButton.CustomClickEvent);
RaiseEvent(newEventArgs);

}
//OnClick触发CustomClickEvent
protected override void OnClick(){
RaiseCustomClickEvent();
}
}

<Window x:Class="WpfApp1.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
xmlns:custom="clr-namespace:BookModels;assembly=BookModels"
mc:Ignorable="d"
Title="Window2" Height="450" Width="800" Loaded="Window_Loaded">
<Grid Margin="3" custom:MySimpleButton.CustomClick="InsertList" Name="grid1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<custom:MySimpleButton x:Name="simpleBtn" CustomClick="InsertList">
MySimpleButton
</custom:MySimpleButton>
<ListBox Margin="5" Name="lstMessage" Grid.Row="1"/>
<CheckBox Margin="5" Grid.Row="2" Name="chkHandle">Handle first event
</CheckBox>
<Button Grid.Row="3" HorizontalAlignment="Right" Margin="5" Padding="3"
Click="Button_Click">Clear List
</Button>
</Grid>
</Window>

public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
//MySimpleButton的类事件处理函数处理过Window能够得到通知
this.simpleBtn.ClassHandlerProcessed += SimpleBtn_ClassHandlerProcessed;
}

private void SimpleBtn_ClassHandlerProcessed(object sender, EventArgs e)
{
eventCounter++;
string message = $"#{eventCounter}:\r\n Windows Class Handler \r\n Sender:{sender}";
lstMessage.Items.Add(message);
}
private void ProcessHandlersToo(object sender, RoutedEventArgs e)
{
eventCounter++;
string message = $"#{eventCounter}:\r\n ProcessHandlerToo \r\n Sender:{sender} \r\n Source:{e.Source} \r\n Original Source:{e.OriginalSource}";
lstMessage.Items.Add(message);
}
public int eventCounter = 0;
private void InsertList(object sender, RoutedEventArgs e)
{
eventCounter++;
string message=$"#{eventCounter}:\r\n InsertList \r\n Sender:{sender} \r\n Source:{e.Source} \r\n Original Source:{e.OriginalSource}";
lstMessage.Items.Add(message);
e.Handled = (bool)chkHandle.IsChecked;
}

private void Button_Click(object sender, RoutedEventArgs e)
{
eventCounter = 0;
lstMessage.Items.Clear();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
grid1.AddHandler(MySimpleButton.CustomClickEvent,new RoutedEventHandler(ProcessHandlersToo),true);
}
}

WPF-属性与依赖


属性与依赖

依赖属性是一种类型为DependencyProperty的属性,其依赖属性标识则是依赖属性的实例.
1.DependencyObject:继承该类后才可以注册和拥有依赖属性
2.WPF属性系统:WPF通过提供一系列服务扩展普通的.Net属性.这些服务总称为WPF属性系统.
3..Net属性包装器:指属性的get和set的实现.在这个实现中均调用DependencyObject的GetValue和SetValue方法.
1.依赖属性对资源引用的支持
2.依赖属性对样式的支持
3.依赖属性对动画的支持
4.依赖属性对数据绑定的支持
5.依赖属性对属性值继承的支持
6.依赖属性对元数据重载的支持
依赖属性和普通.Net属性区别之一是有一个元数据对象.通过设置元数据对象,可以改变依赖属性的状态和行为.一般用到的元数据类是PropertyMetaData和FrameworkPropertyMetaData,前者是后者的基类.
PropertyMetaData—>UIPropertyMetaData—>FrameworkPropertyMetaData.
一般依赖属性的元数据类型为PropertyMetaData,而大部分控件的依赖属性,如按钮的WIdth及Background这样的依赖属性就会用到FrameworkPropertyMetaData元数据对象.
一般元数据对象包括如下类型的信息:
1.默认值
2.引用回调函数 其中PropertyChangedCallback,在属性值发生改变时调用;CoerceValueCallback用于限制属性值.
3.如果是框架级别的一些属性,则会有一些标识告知WPF该属性的某些状态信息.

7.依赖属性对WPF设计器的集成支持
###依赖属性的组成部分

1
public static DependencyProperty Register(string name,Type propertyType,Type ownerType,PropertyMetadata typeMetadata,ValidateValueCallback validateValue validateValueCallback)

第一个参数是指的属性名,这个名字和依赖属性相关的.Net属性名相同,第二个参数是指该.Net属性的类型.第三个属性是指该依赖属性属于什么类型.第四个参数是依赖属性的元数据.第五个参数是值验证的回调函数.该回调函数是负责检验值的最后一道关卡.

自定义依赖属性

实现依赖属性必须满足以下条件:
1.该类必须继承自DependencyObject类.只有DependencyObject类才可以注册和拥有依赖属性.
2.该类中必须定义一个public static readonly成员变量,类型为DependencyProperty.
3.该依赖属性必须以属性名+Property命名
4.必须调用DependencyProperty的注册方法,在WPF属性系统中注册该依赖属性或者使用依赖属性的AddOwner方法.两种方法均返回一个DependencyProperty类型的标识并将其保存在定义的DependencyProperty成员变量中.
5.为依赖属性实现一个.Net属性包装器.

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
//依赖属性
public static readonly DependencyProperty SpaceProperty;
//.Net属性包装器
public int Space{set{setValue(SpaceProperty,value);} get{return (int)GetValue(SpaceProperty);}}
//静态的构造函数
//定义元数据
static SpaceButton{
FrameworkPropertyMetadata metadata=new FrameworkPropertyMetadata();
metadata.DefaultValue=0;
metadata.PropertyChangedCallback+=OnSpacePropertyChanged;
//注册依赖属性
SpaceProperty=DependencyProperty.Register("Space",typeof(int),typeof(SpaceButton),metadata,ValidataSpaceValue);
}
//值验证的回调函数
static bool ValidateSpaceValue(object obj){
int i=(int)obj;
return i>=0;
}
//属性值改变的回调函数
static void OnSpacePropertyChanged(DependencyObject obj,DependencyPropertyChangedEventArgs args){
SpaceButton btn=obj as SpaceButton;
string txt=btn.Content as string;
if(txt==null)return;
btn.Content=btn.SpaceOutText(txt);
}
//该方法为字符间距添加空格
string SpaceOutText(string str){
if(str==null)return null;
StringBuilder build=new StringBuilder();
//在其中添加Space个空格
foreach(char ch in str){
build.Append(ch+new string(' ',Space));
return build.Tostring();
}
}
}

为依赖属性增加属性值继承的特性.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static SpaceWindow(){
FrameworkPropertyMetadata metadata=new FrameworkPropertyMetadata();
metadata.Inherits=true;
SpaceProperty=SpaceButton.SpaceProperty.AddOwner(typeof(SpaceWindow));
SpaceProperty.OverriderMetadata(typeof(Window),metadata);
}

public static readonly DependencyProperty SpaceProperty;
public int Space{
set{
SetValue(SpaceProperty,value);
}
get{
return (int)GetValue(SpaceProperty);
}
}

注册依赖属性Space不是通过注册而来,而是从SpaceButton的Space属性的AddOwner方法得来的.
即依赖属性可以选择把自身添加给其他属性,这是普通属性不可实现的.需要特别注意元数据不能再使用,必须新建一个,为了实现属性值继承,将Inherit标识为true.

所有规则大排队

1.直接设置的值>样式中触发器设置的值>样式中Setter设置的值.

依赖属性设置优先级列表.

优先级列表.
1.限制 Coerce 2.动画 3. 本地值 包括再代码和Xaml中直接设置的值,以及动态资源引用和数据绑定.
4.模板的父类 TemplateParent Triggers Setters 在模板的父类中Triggers设置依赖属性值的优先级高于在Setter中设置依赖属性值.5.样式触发器 主要指在Application或者Page中的样式,不包括主题样式.
6.模板触发器 7.样式设置 8.主题样式 9.属性值继承 10.元数据的默认值

附加属性和”等餐号”

附加属性的本质

附加属性实质上是一个依赖属性,与普通的依赖属性相比有以下不同
1.注册不再是通过Register方法注册,而是通过RegisterAttached方法注册.
2.没有普通的.Net属性包装器,而是通过Get和Set属性名来实现属性包装.
3.没有普通的.Net属性.
一个附加属性IsBubbleSource

1
2
3
4
5
6
7
public static readonly DependencyProperty IsBubbleSourceProperty=DependencyProperty.RegisterAttached("IsBubbleSource",typeof(Boolean),typeof(AquariumObject),new FrameworkPropertyMetadata(false,FrameworkPropertyMetadataOptions.AffectsRender));
public static void SetIsBubbleSource(UIElement element,Boolean value){
element.SetValue(IsBubbleSourceProperty,value);
}
public static Boolean GetIsBubbleSource(UIElement element){
return (Boolean)element.GetValue(IsBubbleSourceProperty);
}