Lesson 02
wpf中xmal的标签是申明一个对象的
在XAML中为对象属性赋值
1。Attribute=value形式(大部分是通过字符串进行赋值)(详见HappyWPF项目)
2。属性标签形式(详情见WPF3项目)
3。 使用标签扩展的形式
导入程序集和引用其中的命名空间(详见BIAOQIANKUOZHANGWPF和ControlLibrary)
项目下面reference添加引用ControlLibrary
Windows x:对x名称空间的使用
xmlns:x 对x名称空间的申明
x名称空间详解:
x:null
Data Binding:
Binding基础:
6.3.1把控件作为Binding源与Binding的标记扩展
Bing构造器本身可以接受Path作为参数 6.3.2控制Binding的方向和数据更新 Binding数据流向的属性是Mode,它的类型是BindingMode枚举可取值OneWay,TwoWay,OnTime,OneWayToSource和Default,Default的值是指Binding的模式会根据实际情况来确定,若是可编辑的像TextBox.Text属性,Default就是双向的6.3.6没有source的Binding–使用DataContext作为Binding的源
当UI上的多个控件都使用Binding关注同一个对象时,可以用DataContext
6.3.7使用集合对象作为列表控件的ItemsSource
InitializeComponent();
List stuList = new List()
{
new Student(){Id=0,Name=“Tim”,Age=29},
new Student(){Id=1,Name=“Tom”,Age=28},
new Student(){Id=2,Name=“Kyle”,Age=27},
new Student(){Id=3,Name=“Tony”,Age=26},
new Student(){Id=4,Name=“Vina”,Age=25},
new Student(){Id=5,Name=“Mike”,Age=24}
};
//为ListBox设置Binding
this.listBoxStudents.ItemsSource = stuList;
this.listBoxStudents.DisplayMemberPath = “Name”;
//为TextBox设置Bindig
this.textBoxId.SetBinding(TextBox.TextProperty, new Binding(“SelectedItem.Age”) { Source=this.listBoxStudents});
6.3.8使用ADO.NET对象作为Binding源
6.3.9使用xml数据作为Binding的源
或者: XmlDataProvider xdp = new XmlDataProvider();
xdp.Source = new Uri(@“D:\RawData.xml”);//指定XML文档所在的位置,调用xml文件的内容
xdp.XPath = @"/StudentList/Student";
this.listViewStudents.DataContext = xdp;
this.listViewStudents.SetBinding(ListView.ItemsSourceProperty,new Binding());
}
6.3.10使用LINQ检索结果作为Binding的源
使用LINQ,我们可以方便地操作集合对象,DataTable对象和XML而不必动辄就把好几层Foreach循环嵌套在一起
XAML:
C#:
using System.Xml.Linq;
private void Button_Click_1(object sender, RoutedEventArgs e)
{
//List stuList = new List()
//{
// new Student(){Id=0,Name=“Tim”,Age=29},
// new Student(){Id=1,Name=“Tom”,Age=28},
// new Student(){Id=2,Name=“Kyle”,Age=27},
// new Student(){Id=3,Name=“Tony”,Age=26},
// new Student(){Id=4,Name=“Vina”,Age=25},
// new Student(){Id=5,Name=“Mike”,Age=24},
//};
//this.listViewStudents.ItemsSource = from stu in stuList where stu.Name.StartsWith(“T”) select stu;//要从一个已经填充好的List对象中检索出所有名字为T开头的学生
XDocument xdoc = XDocument.Load(@“D:\RawData.xml”);//使用System.Xml.Linq命名空间才能使用这个方法//当数据存储在XML文件里
this.listViewStudents.ItemsSource = from element in xdoc.Descendants(“Student”)
where element.Attribute(“Name”).Value.StartsWith(“T”)
select new Student()
{
Id = int.Parse(element.Attribute(“Id”).Value),
Name = element.Attribute(“Name”).Value,
Age = int.Parse(element.Attribute(“Age”).Value)
};
}
}
}
6.3.11 使用ObjectDataProvider对象作为Binding的Source
很难保证一个类的所有数据都通过属性暴露出来,比如我们所需要的数据是方法的返回值,这时候就需要使用ObjectDataBinding作为Binding源的数据对象。ObjectDataBinding类的作用是用来包装以方法暴露数据的对象。
xaml:
c#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.SetBinding();
}
private void SetBinding()
{
ObjectDataProvider odp = new ObjectDataProvider();//创建一个ObjectDataProvider
odp.ObjectInstance = new Calculator();//然后用一个Calculator对象为ObjectInstance属性复制
odp.MethodName = "Add";//指定要调用calculator方法中名为Add的方法
odp.MethodParameters.Add("0");
odp.MethodParameters.Add("0");
//以ObjectDataProvider对象为Source创建Binding
Binding bindingToArg1 = new Binding("MethodParameter[0]")
{
Source = odp,
BindsDirectlyToSource=true,//告诉Binding对象只负责把UI元素手机的数据直接写入其Source
UpdateSourceTrigger=UpdateSourceTrigger.PropertyChanged//有更新立刻传回Source
};
Binding bindingToArg2 = new Binding("MethodParameter[1]")
{
Source = odp,
BindsDirectlyToSource = true,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
Binding bindingToResult = new Binding(".") { Source = odp };//当数据源本身就代表数据的时候就是要"."作Path
//将Binding关联到UI元素上
this.textBoxArg1.SetBinding(TextBox.TextProperty,bindingToArg1);
this.textBoxArg2.SetBinding(TextBox.TextProperty, bindingToArg2);
this.textBoxArg3.SetBinding(TextBox.TextProperty, bindingToResult);
}
}
6.3.12 使用Binding的RelativeSource
XAML:
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
RelativeSource rs = new RelativeSource(RelativeSourceMode.FindAncestor);
rs.AncestorLevel = 2;//以Binding目标控件为起点的层级偏移量
rs.AncestorType = typeof(DockPanel);//告诉Binding寻找哪个类型的对象作为自己的源,不是这个类型的对象会被跳过
Binding binding = new Binding("Name") { RelativeSource = rs };
this.textBox1.SetBinding(TextBox.TextProperty, binding);
// RelativeSource rs = new RelativeSource();//TextBox关联自身的Name属性
// rs.Mode = RelativeSourceMode.Self;
// Binding binding = new Binding("Name") { RelativeSource = rs };
// this.textBox1.SetBinding(TextBox.TextProperty, binding);
}
}
6.4.1 Binding的数据检验
类:重写 Validate方法
public class RangeValidationRule:ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
double d = 0;
if (double.TryParse(value.ToString(), out d))
{
if (d >= 0 && d <= 100)
{
return new ValidationResult(true, null);
}
}
return new ValidationResult(false, "Validation Failed");
}
}
xaml:
c#:
public MainWindow()
{
InitializeComponent();
Binding binding = new Binding(“Value”) { Source = this.slider1 };
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
RangeValidationRule rvr = new RangeValidationRule();//数据检验
rvr.ValidatesOnTargetUpdated = true;//检验Slider(Source)是否超出范围
binding.ValidationRules.Add(rvr);
this.textBox1.SetBinding(TextBox.TextProperty, binding);
}
c#:
public MainWindow()
{
InitializeComponent();
Binding binding = new Binding(“Value”) { Source = this.slider1 };
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
RangeValidationRule rvr = new RangeValidationRule();//数据检验
rvr.ValidatesOnTargetUpdated = true;//检验Slider(Source)是否超出范围
binding.ValidationRules.Add(rvr);
binding.NotifyOnValidationError = true;//检验失败会发出警告
this.textBox1.SetBinding(TextBox.TextProperty, binding);
this.textBox1.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(this.ValidationError));
}
void ValidationError(object sender, RoutedEventArgs e)
{
if (Validation.GetErrors(this.textBox1).Count > 0)
{
this.textBox1.ToolTip = Validation.GetErrors(this.textBox1)[0].ErrorContent.ToString();//程序运行如果检验失败,TextBox的ToolTip就会提醒用户
}
}
}
6.4.2 Binding的数据转换
xaml:
c#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Binding的数据转换
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void buttonLoad_Click(object sender, RoutedEventArgs e)
{
List planeList = new List()
{
new Plane(){Category=Category.Bomber,Name="B-1",State=State.Unknown},
new Plane(){Category=Category.Bomber,Name="B-2",State=State.Unknown},
new Plane(){Category=Category.Fighter,Name="F-22",State=State.Unknown},
new Plane(){Category=Category.Fighter,Name="Su-47",State=State.Unknown},
new Plane(){Category=Category.Bomber,Name="B-52",State=State.Unknown},
new Plane(){Category=Category.Fighter,Name="J-10",State=State.Unknown}
};
this.listBoxPlane.ItemsSource = planeList;
}
//保存到D盘
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (Plane p in listBoxPlane.Items)
{
sb.AppendLine(string.Format(“Category={0},Name={1},State={2}”, p.Category, p.Name, p.State));
}
File.WriteAllText(@“D:\PlaneList.txt”,sb.ToString());
}
}
}
CategoryToSourceConverter:调用接口
public class CategoryToSourceConverter : IValueConverter
{ //
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Category c = (Category)value;
switch ©
{
case Category.Bomber:
return @"\Icons\a.png";
case Category.Fighter:
return @"\Icons\b.png";
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
StateToNullableBoolConverter :
public class StateToNullableBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
State s = (State)value;
switch (s)
{
case State.Locked:
return false;
case State.Available:
return true;
case State.Unknown:
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool? nb = (bool?)value;
switch (nb)
{
case true:
return State.Available;
case false:
return State.Locked;
case null:
default:
return State.Unknown;
}
}
6.5 MultiBinding(多路Binding)
xaml:
LogonMultiBindingConverter:
public class LogonMultiBindingConverter:IMultiValueConverter
{ //判断输入内容是否一致
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (!values.Cast().Any(text => string.IsNullOrEmpty(text))
&& values[0].ToString() == values[1].ToString()
&& values[2].ToString() == values[3].ToString())
{
return true;
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
MainWindows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.setMultiBinding();
}
private void setMultiBinding()
{ //准备好基础的Binding
Binding b1 = new Binding("Text") { Source = this.textBox1 };
Binding b2 = new Binding("Text") { Source = this.textBox2 };
Binding b3 = new Binding("Text") { Source = this.textBox3 };
Binding b4 = new Binding("Text") { Source = this.textBox4 };
//准备MultiBinding
MultiBinding mb = new MultiBinding() { Mode = BindingMode.OneWay };
mb.Bindings.Add(b1);
mb.Bindings.Add(b2);
mb.Bindings.Add(b3);
mb.Bindings.Add(b4);
mb.Converter = new LogonMultiBindingConverter();
this.button1.SetBinding(Button.IsEnabledProperty,mb);//
}
}
第七章:
7.2 依赖属性:
一、CLR属性
CLR(Common Language Running)属性,本质是将一个类中的字段进行封装,以达到控制字段获取以及赋值的目的。
如下的Student类,作为一个学生,年龄显然不能为负数如果想要对age这一字段进行限制,我们可以采用CLR属性进行如下改写,
public class Student
{
public int id;
public string name;
public int age;
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
private int age;
public int Age
{
get { return age; }
set
{
if (value > 0)
{
age = value;
}
}
}
}
采用CLR属性,可以很方便的控制某一字段的读和写,其本质其实就是类方法,类似于C++中封装的控制类对象成员获取和设置的方法。值得注意的是,WPF的Binding中Path的必须是CLR属性。
二、依赖属性(Dependency Property)
依赖属性的含义是自己没有值,能通过Binding从数据源获得值(依赖在别人身上)的属性,用于依赖属性的对象被称为依赖对象。
2.1 依赖属性减少了内存的开销。
在传统的.Net开发中,每一个类对象一旦被创建,它的所有字段也就被分配了存储空间,以TextBox为例,其包含了138个字段,一旦一个TextBox被创建,其138个字段也就被分配了内存空间,而我们常用的字段也就Text一个,这就造成了内存空间的浪费。依赖属性的出现,就是为了解决这一问题。WPF允许对象在创建时,不包含各个字段所占用的空间,而在使用这个字段时通过其他对象的数据或者实时分配空间,这种对象就是依赖对象,而它这种实时获取数据能力就是依靠依赖属性来实现。
WPF中支持依赖对象的类,需要继承自DependevcyObject,依赖属性的类需要继承自DependevcyProperty。DependevcyObject具有GetValue和SetValue两个方法,其都是以DependevcyProperty对象为参数,用于读取和存储依赖属性对象的值:
public class Student:DependencyObject
{
public object GetValue(DependencyProperty dp)
{
...
}
public object SetValue(DependencyProperty dp,object value)
{
...
}
}
DependevcyObject是WPF中很底层的一个基类,所有的UI控件都是依赖对象。
2.2 依赖属性的使用
下面我们自已定义一个支持创建依赖对象的类Student,并设置其依赖属性:
public class Student:DependencyObject
{
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(Student));
}
从上面的类中我们应该注意到:
1、该类必须继承自DependencyObject。
2、NameProperty是依赖属性,Name是用于包装NameProperty这个依赖属性的包装器,依赖属性的命名规范是“包装器+Property”。
3、依赖属性前必须由 public static readonly DependencyProperty修饰,其生成时并非由New,而是用DependencyProperty.Register
4、DependencyProperty.Register的三个参数以此是用来存储依赖属性值的CLR属性、该CLR属性的类型、该DependencyObject对象的类型。
其实从上面的定义我们也可以看出,其实依赖属性的本质就是一个全局的静态的DependencyProperty对象,当我们在开发WPF程序时,系统会准备一个全局的Dictionnary存储了由和“Name”和“Student”类经过某算法生成的唯一键和DependencyProperty的对象组成键值对。而在DependencyObject基类中保存了一个可变的Dictionnary,保存所有和该DependencyObject相关依赖属性的键值以及该属性值,每个DependencyObject对象都有这样一个容器。当使用SetValue对依赖属性进行赋值时,需要先从自带的容器中找到该依赖属性键值,然后 对对应的属性值进行赋值,如果没有就增加一个键值对。当使用GetValue进行依赖属性值的获取时,DependencyObject对象会在其 Dictionnary中进行查找,找到该依赖属性的键值后,返回对应的值,若没找到,返回DependencyProperty对应的默认值(该值在DependencyProperty存储)。
另外,至此对于WPF的Bingding也应该具有了一个更深的认识,其需要绑定Target属性就是依赖属性,而这也是依赖属性最主要的使用方面。
对应的XMAL代码:
对应的按钮事件处理函数:
private void button1_Click(object sender, RoutedEventArgs e)
{
Student stu = new Student();
stu.SetValue(Student.NameProperty, textBox1.Text);
textBox2.Text=stu.GetValue(Student.NameProperty) as string;
}
程序实现的效果就是在第一个编辑框输入某一个值时,会在第一个编辑框显示该值,而后台实现时先给Student对象的依赖属性NameProperty赋值,然后再读取。
而在通常情况下,我们在使用依赖属性时,都需要用CLR属性将其进行包装,这也是目前所有UI控件对依赖属性的处理方式:
public class Student:DependencyObject
{
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register(“Name”, typeof(string), typeof(Student));
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
而按钮的点击事件就可改成:
private void button1_Click(object sender, RoutedEventArgs e)
{
Student stu = new Student();
stu.Name = textBox1.Text;
textBox2.Text = stu.Name;
}
这样我们就可以利用Student的CLR属性进行数据的绑定,作为Binding的path和目标,且虽然Student没有继承INotifyPropertyChanged接口,在CLR属性的值发生变化时,与之绑定的对象仍然可以得到通知,依赖属性默认带有这样的功能。
可以自己没有值,通过使用Binding在数据源身上获取值的属性。
Student.cs
using System.Windows;
namespace _7._2._2声明和使用依赖属性
{
public class Student:DependencyObject
{
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register(“Name”, typeof(string), typeof(Student));
}
}
MainWindows:
using System.Windows.Shapes;
namespace _7._2._2声明和使用依赖属性
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
Student stu;
public MainWindow()
{
InitializeComponent();
stu = new Student();
Binding binding = new Binding(“Text”) { Source = textBox1 };
BindingOperations.SetBinding(stu,Student.NameProperty,binding);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
stu.SetValue(Student.NameProperty, this.textBox1.Text);
textBox2.Text = (string)stu.GetValue(Student.NameProperty);
//MessageBox.Show(stu.GetValue(Student.NameProperty).ToString());
}
}
}
7.3 附加属性
附加属性:一个属性本来不属于某个对象,但由于某种需求而被后来附加上。也就是把对象放入一个特定环境后才具有的属性(表现出来就是被环境赋予的属性)就称为附加属性。
8.3深入浅出路由事件
8.3.2自定义路由事件
创建自定义路由事件大体分为三个步骤:
(1)声明并注册路由事件
(2)为路由事件添加CLR事件包装
(3)创建可以激发路由事件的方法
附加事件:
9.1命令系统的基本元素与关系
9.1.1 命令系统的基本元素
ResourceDictionary:
https://www.cnblogs.com/theroad/p/6178795.html
Mvvm框架:https://www.cnblogs.com/fly-bird/
进程线程:
当今计算机操作系统中最普遍使用的一类操作系统:分时操作系统(采用时间片轮转的方式同时为多个进程服务,进一步加强CPU的使用率)分为协作式和抢占式
虚拟内存:
进程调度:
进程上下文切换:
一个应用程序对应一个进程,操作系统为该进程指定虚拟内存,映射实际的物理内存,从而做到进程隔离。
.NET引入应用程序域,并将它设置在进程和线程之间,显然,每一个进程会包括至少一个应用程序域。.NET允许我们在一个进程中创建一个新的应用程序域来加载程序集。程序集无论是.dll还是.exe,都是一个Windows可以理解的PE文件。如果项目有多个第三方dll,那么应用程序域就变得十分有用
一个进程可以有至少一个前台线程和任意个后台线程,前台线程使得整个进程得以继续下去。一个进程的所有前台线程都结束了,进程也就结束了。当该进程得所有前台线程终止时,CLR将强制终止该进程得所有后台线程,这将导致finally可能没来得及执行,进而导致一些垃圾回收的问题。
虚拟内存:是计算机系统内存管理的一种技术,它使得应用程序认为它拥有连续的可用空间(一个连续完整的地址空间),而实际上,它通常对应多个物理内存碎片,还有部分暂时存储在磁盘上,在需要时进行数据交换。
虚拟内存是按需分配,如果需要更多则会在其他可用内存中进行分配。此时,程序占据的内存(物理上)就不是连续的了。不过,虚拟内存进行映射后,程序会认为自己占据的内存是连续的。
进程隔离:是为保护操作系统中进程互不干扰而设计的技术。进程得隔离实现使用了虚拟内存,进程A的虚拟地址和进程B的虚拟地址不同,这样就防止进程A将数据信息写入进程B。