XAML是eXtensible Application Markup Language的英文缩写,相应的中文名称为可扩展应用程序标记语言.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp11"
StartupUri="MainWindow.xaml">
StartupUri: 该项目为项目启动
Application.Resources: 设置引用程序资源字典, MergedDictionaries用于定义各种资源字典的字典集合
.xaml文档最终被转换成BAML作为资源得形式嵌入到程序集当中。当应用程序工作得时候,会从构造函数的实现方法当中提取BAML资源,并用它来构建用户界面,通过解析BAML,会创建每个控件对象, 设置属性,关联事件等等…
WPF应用程序编译时 首先把XAML文件编译为BAML, (编过的过程当中, VisualStudio会在项目的文件夹当中生成一些临时文件,主要包含控件的相关内容)
编译结束后,编译过的代码会将所有BAML作为资源的形式嵌入到到应用程序或者DLL程序集当中,
以BAML资源的形式存在的优点:
BAML为XAML的二进制表达形式, 不仅小, 而且本身是经过优化的, 在运行时读取BAML比XAML的速度快
资源已经随着程序集嵌入其中, 不必担心丢失导致问题。
也提供程序内部进行读取。
XAML与WPF:
两者相互补充, 你可以完全使用C#来构建你的用户界面, 也可以使用未编译过的XAML文件。
不适用XAML则无法发挥WPF的优势, 但是两者之间是相互独立的。
读取XAML文件作为内容显示:
string xamlfile=“”;
DependencyObject dependency;
using (FileStream fs = new FileStream(xamlfile, FileMode.Open))
{
dependency = (DependencyObject)XamlReader.Load(fs);
}
this.Content = dependency;
读取程序集当中的BAML资源:
var resourcesName = this.GetType().Assembly.GetName().Name + ".g";
var manager = new ResourceManager(resourcesName, this.GetType().Assembly);
var resourceSet = manager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
var dictionaryEntries = resourceSet.OfType().ToList();
dictionaryEntries.ForEach(arg =>
{
Baml2006Reader reader = new Baml2006Reader((Stream)arg.Value);
var win = XamlReader.Load(reader) as Window;
Console.WriteLine(win.Name);
});
样式介绍:
WPF中的各类控件元素, 都可以自由的设置其样式。 诸如:
字体(FontFamily)
字体大小(FontSize)
背景颜色(Background)
字体颜色(Foreground)
边距(Margin)
水平位置(HorizontalAlignment)
垂直位置(VerticalAlignment) 等等。
而样式则是组织和重用以上的重要工具。不是使用重复的标记填充XAML, 通过Styles创建一系列封装所有这些细节的样式。然后通过元素的Style属性设定其样式。
触发器介绍:
触发器可以理解为, 当达到了触发的条件, 那么就执行预期内的响应, 可以是样式、数据变化、动画等。触发器通过 Style.Triggers集合连接到样式中, 每个样式都可以有任意多个触发器, 并且每个触发器都是 System.Windows.TriggerBase的派生类实例, 以下是触发器的类型:
Trigger : 监测依赖属性的变化、触发器生效
MultiTrigger : 通过多个条件的设置、达到满足条件、触发器生效
DataTrigger : 通过数据的变化、触发器生效
MultiDataTrigger : 多个数据条件的触发器
EventTrigger : 事件触发器, 触发了某类事件时, 触发器生效。
控件模板介绍:
控件模板(ControlTemplate)不仅是用于来定义控件的外观、样式, 还可通过控件模板的触发器(ControlTemplate.Triggers)修改控件的行为、响应动画等。
namespace CrazyElephant.Client
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Prism.ViewModel;
using CrazyElephant.Client.Models;
using CrazyElephant.Client.Services;
using Microsoft.Practices.Prism.Commands;
using System.Windows;
namespace CrazyElephant.Client.ViewModels
{
class MainWindowViewModel : NotificationObject
{
public DelegateCommand PlaceOrderCommand { get; set; }
public DelegateCommand SelectMenuItemCommand { get; set; }
private int count;
public int Count
{
get { return count; }
set
{
count = value;
this.RaisePropertyChanged("Count");
}
}
private Restaurant restaurant;
public Restaurant Restaurant
{
get { return restaurant; }
set
{
restaurant = value;
this.RaisePropertyChanged("Restaurent");
}
}
private List dishMenu;
public List DishMenu
{
get { return dishMenu; }
set
{
dishMenu = value;
this.RaisePropertyChanged("DishMenu");
}
}
public MainWindowViewModel()
{
this.LoadRestaurant();
this.LoadDishMenu();
this.PlaceOrderCommand = new DelegateCommand(new Action(this.PlaceOrderCommandExecute));
this.SelectMenuItemCommand = new DelegateCommand(new Action(this.SelectMenuItemExecute));
}
private void LoadRestaurant()
{
this.Restaurant = new Restaurant();
this.Restaurant.Name = "Crazy大象";
this.Restaurant.Address = "北京市海淀区万泉河路紫金庄园1号楼 1层 113室(亲们:这地儿特难找!)";
this.Restaurant.PhoneNumber = "15210365423 or 82650336";
}
private void LoadDishMenu()
{
IDataService ds = new XmlDataService();
var dishes = ds.GetAllDishes();
this.DishMenu = new List();
foreach (var dish in dishes)
{
DishMenuItemViewModel item = new DishMenuItemViewModel();
item.Dish = dish;
this.DishMenu.Add(item);
}
}
private void PlaceOrderCommandExecute()
{
var selectedDishes = this.DishMenu.Where(i => i.IsSelected == true).Select(i => i.Dish.Name).ToList();
IOrderService orderService = new MockOrderService();
orderService.PlaceOrder(selectedDishes);
MessageBox.Show("订餐成功!");
}
private void SelectMenuItemExecute()
{
this.Count = this.DishMenu.Count(i => i.IsSelected == true);
}
}
}
可以说是用WPF创建应用程序必不可少的最基本的知识就是XAML 。理解它是 HTML的标记符号,用户定义WPF程序页面
“xmlns:mc=”为例,“mc”为前缀。,前缀名称 = 的右侧指定命名空间。前缀名称(类似于使用别名)可以为前缀指定任何名称。
在XAML主体中使用xmlns中指定的前缀时,使用“mc:Ignorable”=“ prefix:class name(property) ”的格式。
XAML = “CLR 对象的实例化”
XAML 摸板中的元素 ,都是(CLR 对象)标记语言创建一个对象C#实例,因此使用XAML进行页面元素的描述来构建PWF页面内容。
定义一个CLR 类
using System.IO;
using System.Windows.Markup;
namespace Sample{
public class Point{
public int X { get; set; }
public int Y { get; set; }
}
}
class Program{
static void Main(string[] args){
var x = new Sample.Point { X = 1, Y = 2 };
using (var stream = File.Open("test.xaml", FileMode.Create))
XamlWriter.Save(x, stream);
}
}
xaml引入这个CLR描述
相反,要从 XAML 代码反序列化为 CLR 对象,请使用 XamlReader.Load 方法
**XAML中元素对应CLR **
创建用户定义类“Point”( XAML 代码中的 元素)的实例(= CLR 对象)的示例。
↑ 项目自定义的C#类 Sample.Point 与XAML标记关系 X,y 传递参数
var obj = new Sample.Point{
X = 1,
Y = 2
}
XML 命名空间(xmlns 属性)= CLR 命名空间 + 程序集信息
XML 元素名称 = CLR 类名称
XML 属性 = CLR 对象属性值设置(属性语法)
Uri 类(系统命名空间)(来自 XAML 代码中的 元素)的实例(= CLR 对象)的示例。
< Uri xmlns = "clr-namespace: System ; assembly = System">
http://www.atmarkit.co.jp/
var converter = new System.UriTypeConverter();
var obj = converter.ConvertFrom("http://www.atmarkit.co.jp/");
// ↑System.Uri类(=元素)包含
// [TypeConverter(typeof(UriTypeConverter))]带属性
XAML 代码中每个元素的字符串根据分配到的CLR 类中的 TypeConverter 属性值进行传递参数。例如,根据给定的TypeConverter属性的“UriTypeConverter”,将XAML代码中元素的字符串转换为C#代码中Uri类的一个实例(=CLR对象)到 Uri 类.
我们只是直接在 XAML 中声明 TreeViewItem 对象,在我们想要显示它们的相同结构中,其中第一个标记是 TreeView 控件的子项,其子对象也是其父对象的子标记。为了指定我们想要为每个节点显示的文本,我们使用Header属性。默认情况下,TreeViewItem 不会展开,但为了向您展示示例的结构,我使用了 IsExpanded属性来展开两个父项。
不过,Header是一个有趣的属性。正如你所看到的,我可以只指定一个文本字符串,然后直接渲染它而不做任何其他事情,但这是 WPF 对我们很好 - 在内部,它将文本包装在 TextBlock 控件中,而不是强迫你做它。这向我们表明,我们可以将几乎任何我们想要的内容填充到 Header 属性中,而不仅仅是一个字符串,然后让 TreeView 呈现它 - 一个很好的例子,说明了为什么自定义 WPF 控件的外观如此容易。
来自 WinForms 甚至其他 UI 库的人们的常见要求之一是能够在 TreeView 项的文本标签旁边显示图像。使用 WinForms 很容易做到这一点,因为 TreeView 正是为此场景构建的。使用 WPF 树视图,它有点复杂,但您获得的灵活性比从 WinForms 树视图中获得的要多得多。这是一个例子:
我在这里做了很多事情,只是为了向您展示您获得的灵活性:我为子项着色,并为父项添加了图像甚至按钮。因为我们使用简单的标记来定义整个事物,所以您几乎可以做任何事情,但是正如您从示例代码中看到的那样,它确实有代价:大量的 XAML 代码,对于总共只有六个节点的树!
WPF TreeView 支持数据绑定,就像几乎所有其他 WPF 控件一样,但是因为 TreeView 本质上是分层的,所以普通的 DataTemplate 通常是不够的。相反,我们使用 HierarchicalDataTemplate,它允许我们对树节点本身进行模板化,同时控制将哪个属性用作节点子项的源。
using System;
using System.Collections.Generic;
using System.Windows;
using System.IO;
using System.Collections.ObjectModel;
namespace WpfTutorialSamples.TreeView_control
{
public partial class TreeViewDataBindingSample : Window
{
public TreeViewDataBindingSample()
{
InitializeComponent();
MenuItem root = new MenuItem() { Title = "Menu" };
MenuItem childItem1 = new MenuItem() { Title = "Child item #1" };
childItem1.Items.Add(new MenuItem() { Title = "Child item #1.1" });
childItem1.Items.Add(new MenuItem() { Title = "Child item #1.2" });
root.Items.Add(childItem1);
root.Items.Add(new MenuItem() { Title = "Child item #2" });
trvMenu.Items.Add(root);
}
}
public class MenuItem
{
public MenuItem()
{
this.Items = new ObservableCollection
在 XAML 标记中,我为TreeView的ItemTemplate指定了一个 HierarchicalDataTemplate 。我指示它使用 Items属性来查找子项,方法是设置模板的ItemsSource属性,并在其中定义实际模板,现在它只包含一个绑定到Title属性的 TextBlock 。
第一个示例非常简单,实际上非常简单,我们不妨手动添加 TreeView 项,而不是生成一组对象然后绑定到它们。然而,一旦事情变得更加复杂,使用数据绑定的优势就会变得更加明显。
在下一个示例中,我采用了一个稍微复杂一些的案例,我想在其中展示一棵家庭及其成员的树。一个家庭应该以一种方式表示,而它的每个成员都应该以另一种方式表示。我通过创建两个不同的模板并将它们指定为树(或窗口或应用程序 - 这真的取决于您)的资源来实现这一点,然后允许 TreeView 根据基础数据类型选择正确的模板。
这是代码 - 它的解释将紧随其后:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Collections.ObjectModel;
namespace WpfTutorialSamples.TreeView_control
{
public partial class TreeViewMultipleTemplatesSample : Window
{
public TreeViewMultipleTemplatesSample()
{
InitializeComponent();
List families = new List();
Family family1 = new Family() { Name = "The Doe's" };
family1.Members.Add(new FamilyMember() { Name = "John Doe", Age = 42 });
family1.Members.Add(new FamilyMember() { Name = "Jane Doe", Age = 39 });
family1.Members.Add(new FamilyMember() { Name = "Sammy Doe", Age = 13 });
families.Add(family1);
Family family2 = new Family() { Name = "The Moe's" };
family2.Members.Add(new FamilyMember() { Name = "Mark Moe", Age = 31 });
family2.Members.Add(new FamilyMember() { Name = "Norma Moe", Age = 28 });
families.Add(family2);
trvFamilies.ItemsSource = families;
}
}
public class Family
{
public Family()
{
this.Members = new ObservableCollection();
}
public string Name { get; set; }
public ObservableCollection Members { get; set; }
}
public class FamilyMember
{
public string Name { get; set; }
public int Age { get; set; }
}
}
如前所述,这两个模板被声明为 TreeView 资源的一部分,允许 TreeView 根据即将显示的数据类型选择适当的模板。为Family类型定义的模板是一个分层模板,使用Members 属性来显示其家庭成员。
为FamilyMember类型定义的模板是常规 DataTemplate,因为此类型没有任何子成员。但是,如果我们希望每个 FamilyMember 都保留他们的孩子以及他们孩子的孩子的集合,那么我们将改用分层模板。
在这两个模板中,我们使用代表家庭或家庭成员的图像,然后我们还展示了一些关于它的有趣数据,例如家庭成员的数量或人的年龄。
在代码隐藏中,我们简单地创建两个 Family 实例,用一组成员填充每个实例,然后将每个系列添加到一个列表中,然后将其用作 TreeView 的项目源。