WPF学习(204)

wpf和winform

winform -> 新建windows窗体程序
控件的描述全在一个Designer的cs文件中,控件事件则在对应的cs文件里。

wpf -> 新建wpf程序

感觉winform就是基于mfc再封装了一层c#,界面设计等还是wpf吧

代码隐藏文件

界面描述放在xaml文件里的
为界面增加功能(事件呀等)的cs代码,就放入.xaml.cs文件中
隐藏文件默认是放入xaml文件同一个目录中的。

界面第一句:

1
2
<Window x:Class="WpfApplication1.MainWindow">
//第一句就指定了,对应隐藏文件的类

路由事件

路由事件有点想mfc的common,但是它要多点。
lbutondown这些事件是标准事件,它只会发给被显示订阅的控件。

像keydown这种路由事件它会:
先走下钻事件(Preview开头的事件),再走上钻事件(正常开头的事件);
pre的Window事件->pre的Grid网格事件->pre的焦点控件事件->焦点控件事件->Grid网格事件->Window事件

如需要停止路由,直接e.Handled = true;

布局控件

把这几个基本的布局控件搞了,就直接干代码了。太麻烦了!!

Canvas控件

书上说这个可以完全自由地对控件的位置进行安排,且HorizontalAligmentVerticalAlignment并不能改变这些元素位置

测试发现,窗口的变大变小如果是Grid则会跟随变化里面子控件的大小;而Canvas则不会,里面的子控件大小位置被固定了似的。

可以用Canvas.Top等这些属性来限定子控件在Canvas里面的布局,但我估计实际应用慢慢看吧。。。。

DockPanel控件

它会从上往下(越接近顶层,越优先)。布局子控件,除非有限制(height、width),不然会尽量布满布局。

它是从剩余空间来进行计算(DockPanel.Dock="Top"以剩余的上半部分为基础)

StackPanel控件

它的设置更多的设置本身,它来控制内部子控件的堆叠方向,不会占满布局。

Orientaion、VerticalAlignment、HorizontalAlignment来设置内部控件的堆叠方向、内部控件对齐方向。

WrapPanel控件

是StackPanel的扩展版本;容纳不下的控件会被安排到下一行

Grid控件

透明窗口

1
2
3
4
5
6
7
//窗口透明
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"//这个属性感觉很智能,它还可以设置窗体的透明色。
//控件透明
//另外控件的透明度用 Opacity

控件标题内容换行

1
2
Content="wrewrwere&#13;wrewrewrwe"
Text="oiomlkljoijlo&#x0a;klklkkjkjjk"

如上,很方便。但还是纠结了,本来Windows换行符是\r\n的,c#代码换行也是如此。
于是有下:
空格 ( )
Tab ( )
回车 ( ) 难怪上面是13
换行 ( )

高宽百分比设置

今天试了下,发现 Height和Width 不仅可以用数字表示,还可以 30*这种百分比表示。

这样的话就会把所有子控件的加上,再除以相应数字。得出百分比,进行布局设置。

grid跨行跨列

对于grid可以分区:

1
2
3
4
5
6
7
//行的使用
<Grid.RowDefinitions>
<RowDefinition Height="34"/>
<RowDefinition />
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
//子控件使用 Grid.Row="0" 等语法就可以放置到相应区里面

但是当有个控件比较特殊,需要占多个行或列时(上面grid划分好了的行和列)
就可以使用Grid.RowSpan(列同理) 它会从 Grid.Row(列同理)开始,占满接下来几个的行(列)

x命名空间

tab控件使用

注意TabItem子控件只能有一个,所以一般用布局控件来代替。

x:Name和Name

所有的Type在xaml中都能使用x:Name,即使这个type 没有定义一个叫着Name的Property。
因为xmal会在后台为其分配一个field来存储,以便在Binding等里面使用。

像FrameworkElement, VisualStateGroup的某些type,我们在xaml中可以使用Name来代替x:Name,
因为它们定义了Name Property,但是最关键的是在type上声明了一个Attribute,[RuntimeNameProperty(“Name”)],
正是这个attribute把Name映射到了x:Name上,所以让Name可以做到x:Name的功能。

正因为如此,name属性超级好用,在xaml给个name属性,相应的隐藏文件就可以直接使用此name来代表此控件

数据绑定

https://www.tutorialspoint.com/wpf
https://msdn.microsoft.com/zh-cn/library/ms752347(v=vs.110).aspx

Content="{Binding}" 默认关联属性.aspx)

本地对象绑定

如上所说,绑定和Name相关。

先看本地对象绑定:

1
2
3
4
5
6
7
8
9
10
11
12
<CheckBox Name="ee" Content="Play against computer" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="8,2,0,0" />
<Label Content="Number" Margin="8,28,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" IsEnabled="{Binding ElementName=ee, Path=IsChecked}" />
<ComboBox x:Name="hhe" Width="86" Margin="180,28,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" SelectedIndex="0">
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
</ComboBox>
<TextBox Text="{Binding ElementName=hhe, Path=SelectedItem.Content}" Width="86" Margin="180,68,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" />
//如下形式是同样的
<ComboBox.IsEnabled>
<Binding ElementName="ee" Path="IsChecked" />
</ComboBox.IsEnabled>

先看Label的绑定,Binding通过ElementNameName属性找到了CheckBox对象,并绑定到它身上,再通过IsChecked属性来传值。实际效果:check选中,label就enable(相反同理)
再看TextBox绑定,Binding还是绑定到ComboBox上,且它文本绑定到了选中的内容上。实际效果:ComboBox选中哪项,TextBox就显示相应的选中内容。

静态绑定到外部对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//静态资源的定义最好尽量放外面,不然识别不到外部类
<Grid.Resources>
<local:ComBoxTests x:Key="com"></local:ComBoxTests>
</Grid.Resources>
<ComboBox x:Name="hhe" Width="86" Margin="180,28,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" SelectedIndex="0" ItemsSource="{Binding Source={StaticResource com}}">
</ComboBox>
<TextBox Text="{Binding ElementName=hhe, Path=SelectedItem}" Width="86" Margin="180,68,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" GotFocus="TimeTextBox_GotFocus" />
//代码方面
public class ComBoxTests : ObservableCollection<string>
{
public ComBoxTests()
: base()
{
Add("str1");
Add("str2");
Add("str3");
}
}
/////再来个例子
<sys1:String x:Key="Name">sunji</sys1:String>
<TextBlock Name="txtbName" Text="{StaticResource Name}"/>

如上,向combobox添加了三个选项,但是TextBox的内部绑定却不得不改了,我也不知道。。想说绑定规则太多了。。。

Mode的类型

Mode = OneWay 就仅仅只是绑定,控件修改的数据也不会更新到绑定对象里面去
Mode = TwoWay 绑定后,若控件修改了数据也会更新到绑定的对象上面去。
Mode = OneWayToSource 依赖性只更新到绑定对象上面去。

详细看:https://www.tutorialspoint.com/wpf/wpf_data_binding.htm

动态绑定到外部对象

对于双向绑定来说这个是必须的。
重写INotifyPropertyChanged接口

来区分下INotifyPropertyChanged和mode
如上,mode有三种模式不多讲了。
1、只重写INotifyPropertyChanged接口,确实能进行双向绑定。
2、只设置Mode = TwoWay也可以双向绑定。
但是它们有个区别。第二种情况,当数据更新时,它并不会立刻更新界面;要等界面再来取一次才行。第一种情况mode就默认了为TwoWay

所以mode和INotifyPropertyChanged配合才是最好的

这个控件可以像其他控件那样,随便放哪个地方,然后自己设置menu内容。
Menu.Icon设置单个选项的图标。
InputGestureText只是个显示内容,不会和指令绑定。
Click子项直接相应此属性的函数就好了。

Command

多说项:CanExecute返回false,会自动把控件设置为禁止状态。

先看下默认的,主要讲下:

Command,确实它能和指令绑定,但是默认只能和wpf能解析的指令绑定(自定义命令,下次再讲述吧)。
它设置命令后,如果想拦截命令的执行:

1
2
3
4
5
6
<... Command="Save">
<Window.CommandBindings>
<CommandBinding Command="Save" CanExecute="NewCommand_CanExecute" Executed="NewCommand_Executed" />
</Window.CommandBindings>
//CanExecute 是开始展开menu时执行函数,以此来判断哪些项不允许点击和点击
//Executed 是点击了menuitme是执行的函数

自定义command主要有俩种:

  1. 自定义ICommand

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class MyCommand : ICommand
    {
    public event EventHandler CanExecuteChanged
    {
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
    }
    public bool CanExecute(object parameter)
    {
    return true;
    }
    public void Execute(object parameter)
    {
    MessageBox.Show("www");
    }
    }
    调用时:
    要么:this.btn.Command = new MyCommand();或者<Button Command="{Binding MyCommand}
    要么:<local:MyCommand x:Key="ww" /> <Button Command="{StaticResource ww}" />
    这种`Command`信息比较少,适合默认执行类型。真要加信息也得加`CommandParameter`
  2. 自定义RoutedCommand

    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
    public static RoutedCommand RMyCommand =
    new RoutedCommand("MYC", typeof(ForTest));
    public ForTest()
    {
    InitializeComponent();
    tc.Command = RMyCommand;
    CommandBinding comBind = new CommandBinding();
    comBind.Command = RMyCommand;
    comBind.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute);
    comBind.Executed += new ExecutedRoutedEventHandler(cb_Execute);
    gg.CommandBindings.Add(comBind);
    }
    private void cb_Execute(object sender, ExecutedRoutedEventArgs e)
    {
    tb.Clear();
    e.Handled = true;
    }
    private void cb_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
    if (string.IsNullOrEmpty(tb.Text))
    {
    e.CanExecute = false;
    }
    else
    {
    e.CanExecute = true;
    }
    e.Handled = true;
    }
    如上,用`RoutedCommand`新建一个(第三个参数是快捷键);
    大部分是多余的,和默认相比,中心思想就是新建一个command,添加到绑定。

渐变画刷

LinearGradientBrush

我在自定义控件界面时,想对背景色渐变。
这个是线性渐变,默认渐变线是对角线。
用startpoint endpoint来设置渐变线。
GradientStop 它表示渐变停止点,Offset来设置渐变线中百分比的偏移量(如:offset=”0.0” 这个点是从对角线左上开始的,有下个点它就停止在下个点的offset)

RadialGradientBrush

这个是个圆形的渐变。
渐变线就是圆的各个半径。
Center书面说它是圆的中心(这个指的是圆形状的中心),可以想象它是控制所有圆的宽窄,通过它来控制所有圆心
GradientOrigin渐变色开始位置(可以从圆边,和圆心)
RadiusX、RadiusY这俩个是半径

样式和模板

样式:http://www.wpf-tutorial.com/styles/using-styles/
分层、显隐式(x:Key; Style=”{StaticResource HeaderStyle}”)
//可以同一设置控件的属性内容

控件模板

模板:http://www.cnblogs.com/dingli/archive/2011/07/20/2112150.html

1
2
3
4
5
6
7
8
9
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
...
//通过ControlTemplate的VisualTree内容,可以重做控件外观模板
//通过tigger,<ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="true"> ,可以对控件的事件作出响应
//控件内容显示用ContentPresenter要比ContentControl轻量一些

转换接口

对于静态绑定到内部对象太重要了,这个接口。
比如:通过输入no,来禁止控件;一个是字符一个是bool,绑定就不会成功,所以需要转换。

1
2
3
4
5
6
<local:Convert x:Key="toconvert"></local:Convert>
<RadioButton IsEnabled="{Binding ElementName=tbt, Path=Content, Converter={StaticResource toconvert}}" />
//上面是cs文件处理,代码上继承IValueConverter自己转换便好
//由于上面是单向绑定,所以实现Convert便好
//当面对双向绑定时,ConvertBack也需要同时实现

动画

使用的结构也比较多,简单说下:
动画必须放入模板里面Storyboard,这样一个模板可以包含多个动画可以同时触发。
触发方式有俩种:
1、放入EventTrigger进行触发,只是要多个BeginStoryboard这样才能表示为此事件发生,模板开始触发。
2、放入Resources内(这个时候只需要Storyboard就够了)。因为有name属性,代码里面直接:name.Begin(),模板开始触发。

自定义控件

custom control有点没看透,直接讲user control。

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
#region 属性
public static DependencyProperty ShowStringProperty =
DependencyProperty.Register("ShowString", typeof(string), typeof(UserTest),
new PropertyMetadata("默认值", new PropertyChangedCallback(OnChangeShowString)));
static void OnChangeShowString(DependencyObject obj, DependencyPropertyChangedEventArgs args)//设置属性被修改时的回调函数
{
UserTest tt = obj as UserTest;
tt.t1.Text = (string)args.NewValue;
}
public string ShowString
{
set { SetValue(ShowStringProperty, value); }
get { return (string)GetValue(ShowStringProperty); }
}
#endregion
#region 事件
static readonly RoutedEvent ShowStringEvent =
EventManager.RegisterRoutedEvent("ShowEvent", RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<object>), typeof(UserTest));
public event RoutedPropertyChangedEventHandler<object> ShowEvent
{
add { AddHandler(ShowStringEvent, value); }
remove { RemoveHandler(ShowStringEvent, value); }
}
public void BtnClickme(object sender, RoutedEventArgs e)//这是自定义控件给button加了个事件,这样这个函数就会通知外面我的自定义事件
{
RoutedPropertyChangedEventArgs<object> arg =
new RoutedPropertyChangedEventArgs<object>(sender, e, ShowStringEvent);
RaiseEvent(arg);
}
#endregion
<local:UserTest HorizontalAlignment="Center" VerticalAlignment="Bottom" ShowString="wangqiu" ShowEvent="UserTest_ShowEvent" ></local:UserTest>

都是先注册静态变量创建,再编写属性设置。

依赖属性

wpf的控件是有共享的内存的,所以上面属性注册全用依赖属性注册。
官方说转换的时候 DependencyProperty.UnsetValue 会清空当前属性值。null 传给依赖属性是不行的,会被寻找其它值。

ControlTemplate和ContentTemplate

前者可以改变控件的外形。后者改变控件的内容。
比如你的button,想变成圆形,就用前者。你想Content="asasas"这些内容圆形显示时就用后者。

由于强大的xaml,使的内容和形状都可以用控件表述。但是表述内容和DataTemplate联合无疑是好的选择: http://www.cnblogs.com/zhouhb/p/3284827.html

ZIndex

数字越小则层数越低,数字最大的在最前面

RenderTransform

1
2
3
<Label.RenderTransform>
<RotateTransform Angle="180"/>
</Label.RenderTransform>

1、RotateTransform:元素旋转:

Angle:旋转角度。
CenterX:旋转水平中心。
CenterY旋转的垂直中心}这两个是绝对定位坐标,
而RenderTransformOrigin是相对定位的

2、ScaleTransform:会对元素水平,垂直方向上缩放。

CenterX:缩放水平中心。CenterY缩放的垂直中心
ScaleX:水平缩放倍数 ScaleY:垂直缩放倍数。

3、SkewTransform:元素倾斜:

CenterX:倾斜水平中心。CenterY倾斜的垂直中心
AngleX:水平倾斜角度。AngleY:垂直倾斜角度。

4、TranslateTransform:元素移动(只对RenderTransform有效果)

X=水平移动像素,Y=垂直移动像素。

OpacityMask

简单形状的渐变直接fill渐变画刷就好了(而且画刷颜色直接指定透明度)。
可是路径和图片也要渐变的改变透明度只能用OpacityMask

Opacity是指定控件的透明度

1
2
3
4
5
6
<Image.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFFFFFFF" Offset="0"/>
<GradientStop Color="#00FFFFFF" Offset="1"/>
</LinearGradientBrush>
</Image.OpacityMask>

Dispatcher

在 WPF 中绝大部分控件都继承自 DispatcherObject,
甚至包括 Application。这些继承自 DispatcherObject 的对象具有线程关联特征,
也就意味着只有创建这些对象实例,且包含了 Dispatcher 的线程(通常指默认 UI 线程)才能直接对其进行更新操作

// //