WPF入门基础

WPF(Windows Presentation Foundation)

  • 英 /ˌpreznˈteɪʃn/ Presentationn. (颁奖等)仪式,典礼;授予,颁发;(在仪式上接受的)奖项,赠予物;展示会,介绍会,发布会;陈述,报告,说明;(事物的)呈现方式;(证件等的)出示;(证物的)提交,提供;(戏剧的)演出;(胎儿的)先露位置,产位;(尤指在帝王前)正式引见,引……朝见;<史>(圣职等的)推荐

XAML是eXtensible Application Markup Language的英文缩写,相应的中文名称为可扩展应用程序标记语言.

<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp11"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>

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<DictionaryEntry>().ToList();
            dictionaryEntries.ForEach(arg =>
            {
                Baml2006Reader reader = new Baml2006Reader((Stream)arg.Value);
                var win = XamlReader.Load(reader) as Window;
                Console.WriteLine(win.Name);
            });

1.为什么有一些元素是Content显示内容,而一些元素是Text显示内容? 
凡是继承于ContentControl的控件, 他们的定义内容用Content,  除了TextBlock使用的是Text,
2.为什么有一些元素有Padding,而一些元素并没有? 
大部分都是Content设置其显示内容。在继承于Control下的大部分控件具备这个Padding属性, TextBlock则单独实现了Padding 属性。
3.Magin和Padding的区别是…?
Margin: 外边距  Padding: 内边距
4.关于Content…
在继承于Control下的大部分控件具备这个Padding属性, TextBlock则单独实现了Padding 属性。Content由于是object类型, 所以对于常用的, Button, CheckBox 等等类型控件来讲, 不仅仅可以设置字符串类型, 也可以设置各种复杂的对象类型
例如:
         <Button>
            <Button.Content>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="♥"/>
                    <TextBlock Text="Hello"/>
                </StackPanel>
            </Button.Content>
        </Button>

样式介绍:
WPF中的各类控件元素, 都可以自由的设置其样式。 诸如:
字体(FontFamily)
字体大小(FontSize)
背景颜色(Background)
字体颜色(Foreground)
边距(Margin)
水平位置(HorizontalAlignment)
垂直位置(VerticalAlignment) 等等。

而样式则是组织和重用以上的重要工具。不是使用重复的标记填充XAML, 通过Styles创建一系列封装所有这些细节的样式。然后通过元素的Style属性设定其样式。

触发器介绍:
触发器可以理解为, 当达到了触发的条件, 那么就执行预期内的响应, 可以是样式、数据变化、动画等。触发器通过 Style.Triggers集合连接到样式中, 每个样式都可以有任意多个触发器, 并且每个触发器都是 System.Windows.TriggerBase的派生类实例, 以下是触发器的类型:

Trigger : 监测依赖属性的变化、触发器生效
MultiTrigger : 通过多个条件的设置、达到满足条件、触发器生效
DataTrigger : 通过数据的变化、触发器生效
MultiDataTrigger : 多个数据条件的触发器
EventTrigger : 事件触发器, 触发了某类事件时, 触发器生效。

控件模板介绍:
控件模板(ControlTemplate)不仅是用于来定义控件的外观、样式, 还可通过控件模板的触发器(ControlTemplate.Triggers)修改控件的行为、响应动画等。

案例 

<Window x:Class="CrazyElephant.Client.MainWindow" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="{Binding Restaurant.Name, StringFormat=\{0\}-在线订餐}" Height="600" Width="1000"
        WindowStartupLocation="CenterScreen">
    <Border BorderBrush="Orange" BorderThickness="3" CornerRadius="6" Background="Yellow">
        <Grid x:Name="Root" Margin="4">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Border BorderBrush="Orange" BorderThickness="1" CornerRadius="6" Padding="4">
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <StackPanel.Effect>
                            <DropShadowEffect Color="LightGray" />
                        </StackPanel.Effect>
                        <TextBlock Text="欢迎光临-" FontSize="60" FontFamily="LiShu" />
                        <TextBlock Text="{Binding Restaurant.Name}" FontSize="60" FontFamily="LiShu" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="小店地址:" FontSize="24" FontFamily="LiShu" />
                        <TextBlock Text="{Binding Restaurant.Address}" FontSize="24" FontFamily="LiShu" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="订餐电话:" FontSize="24" FontFamily="LiShu" />
                        <TextBlock Text="{Binding Restaurant.PhoneNumber}" FontSize="24" FontFamily="LiShu" />
                    </StackPanel>
                </StackPanel>
            </Border>
            <DataGrid AutoGenerateColumns="False" GridLinesVisibility="None" CanUserDeleteRows="False"
                    CanUserAddRows="False" Margin="0,4" Grid.Row="1" FontSize="16" ItemsSource="{Binding DishMenu}">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="菜品" Binding="{Binding Dish.Name}" Width="120" />
                    <DataGridTextColumn Header="种类" Binding="{Binding Dish.Category}" Width="120" />
                    <DataGridTextColumn Header="点评" Binding="{Binding Dish.Comment ,UpdateSourceTrigger=PropertyChanged}" Width="120" />
                    <DataGridTextColumn Header="推荐分数" Binding="{Binding Dish.Score}" Width="120" />
                    <DataGridTemplateColumn Header="选中" SortMemberPath="IsSelected" Width="120">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding Path=IsSelected,  UpdateSourceTrigger=PropertyChanged}"
                                        VerticalAlignment="Center" HorizontalAlignment="Center"
                                        Command="{Binding Path=DataContext.SelectMenuItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="2">
                <TextBlock Text="共计" VerticalAlignment="Center" />
                <TextBox IsReadOnly="True" TextAlignment="Center" Width="120" Text="{Binding Count}" Margin="4,0" />
                <Button Content="Order" Height="24" Width="120" Command="{Binding PlaceOrderCommand}" />
            </StackPanel>
        </Grid>
    </Border>
</Window>
namespace CrazyElephant.Client
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    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<DishMenuItemViewModel> dishMenu;

        public List<DishMenuItemViewModel> 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<DishMenuItemViewModel>();
            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);
        }
    }
}

XAML 介绍

可以说是用WPF创建应用程序必不可少的最基本的知识就是XAML 。理解它是 HTML的标记符号,用户定义WPF程序页面

xmlns 标记

“xmlns:mc=”为例,“mc”为前缀。,前缀名称 = 的右侧指定命名空间。前缀名称(类似于使用别名)可以为前缀指定任何名称。
在XAML主体中使用xmlns中指定的前缀时,使用“mc:Ignorable”=“ prefix:class name(property) ”的格式。

<Window 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">

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描述

<Point X="1" Y="2"
  xmlns="clr-namespace:Sample;assembly=SampleLibrary" />

相反,要从 XAML 代码反序列化为 CLR 对象,请使用 XamlReader.Load 方法

**XAML中元素对应CLR **

创建用户定义类“Point”( XAML 代码中的 元素)的实例(= CLR 对象)的示例。

<Point X="1" Y="2"
  xmlns="clr-namespace:Sample;assembly=SampleLibrary" />
↑ 项目自定义的C#类 Sample.Point 与XAML标记关系 X,y 传递参数
var obj = new Sample.Point{
  X = 1,
  Y = 2
}

   XML 命名空间(xmlns 属性)= CLR 命名空间 + 程序集信息
   XML 元素名称 = CLR 类名称
   XML 属性 = CLR 对象属性值设置(属性语法)

System.Uri 类的实例化示例

Uri 类(系统命名空间)(来自 XAML 代码中的 元素)的实例(= CLR 对象)的示例。

< Uri xmlns = "clr-namespace: System ; assembly = System">
  http://www.atmarkit.co.jp/ </Uri>
var converter = new System.UriTypeConverter();
var obj = converter.ConvertFrom("http://www.atmarkit.co.jp/");
// ↑System.Uri类(=<Uri>元素)包含
// [TypeConverter(typeof(UriTypeConverter))]带属性

XAML 代码中每个元素的字符串根据分配到的CLR 类中的 TypeConverter 属性值进行传递参数。例如,根据给定的TypeConverter属性的“UriTypeConverter”,将XAML代码中元素的字符串转换为C#代码中Uri类的一个实例(=CLR对象)到 Uri 类.
 

 TreeView 

简单的方式
<Window x:Class="WpfTutorialSamples.TreeView_control.TreeViewSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TreeViewSample" Height="200" Width="250">
    <Grid Margin="10">
		<TreeView>
			<TreeViewItem Header="Level 1" IsExpanded="True">
				<TreeViewItem Header="Level 2.1" />
				<TreeViewItem Header="Level 2.2" IsExpanded="True">
					<TreeViewItem Header="Level 3.1" />
					<TreeViewItem Header="Level 3.2" />
				</TreeViewItem>
				<TreeViewItem Header="Level 2.3" />
			</TreeViewItem>
		</TreeView>
	</Grid>
</Window>

我们只是直接在 XAML 中声明 TreeViewItem 对象,在我们想要显示它们的相同结构中,其中第一个标记是 TreeView 控件的子项,其子对象也是其父对象的子标记。为了指定我们想要为每个节点显示的文本,我们使用Header属性。默认情况下,TreeViewItem 不会展开,但为了向您展示示例的结构,我使用了 IsExpanded属性来展开两个父项。

带有图像和其他控件的 TreeViewItem

不过,Header是一个有趣的属性。正如你所看到的,我可以只指定一个文本字符串,然后直接渲染它而不做任何其他事情,但这是 WPF 对我们很好 - 在内部,它将文本包装在 TextBlock 控件中,而不是强迫你做它。这向我们表明,我们可以将几乎任何我们想要的内容填充到 Header 属性中,而不仅仅是一个字符串,然后让 TreeView 呈现它 - 一个很好的例子,说明了为什么自定义 WPF 控件的外观如此容易。

来自 WinForms 甚至其他 UI 库的人们的常见要求之一是能够在 TreeView 项的文本标签旁边显示图像。使用 WinForms 很容易做到这一点,因为 TreeView 正是为此场景构建的。使用 WPF 树视图,它有点复杂,但您获得的灵活性比从 WinForms 树视图中获得的要多得多。这是一个例子:

<Window x:Class="WpfTutorialSamples.TreeView_control.TreeViewCustomItemsSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TreeViewCustomItemsSample" Height="200" Width="250">
	<Grid Margin="10">
		<TreeView>
			<TreeViewItem IsExpanded="True">
				<TreeViewItem.Header>
					<StackPanel Orientation="Horizontal">
						<Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
						<TextBlock Text="Level 1 (Blue)" />
					</StackPanel>
				</TreeViewItem.Header>
				<TreeViewItem>
					<TreeViewItem.Header>
						<StackPanel Orientation="Horizontal">
							<TextBlock Text="Level 2.1" Foreground="Blue" />
						</StackPanel>
					</TreeViewItem.Header>
				</TreeViewItem>
				<TreeViewItem IsExpanded="True">
					<TreeViewItem.Header>
						<StackPanel Orientation="Horizontal">
							<Image Source="/WpfTutorialSamples;component/Images/bullet_green.png" />
							<TextBlock Text="Level 2.2 (Green)" Foreground="Blue" />
						</StackPanel>
					</TreeViewItem.Header>
					<TreeViewItem>
						<TreeViewItem.Header>
							<TextBlock Text="Level 3.1" Foreground="Green" />
						</TreeViewItem.Header>
					</TreeViewItem>
					<TreeViewItem>
						<TreeViewItem.Header>
							<TextBlock Text="Level 3.2" Foreground="Green" />
						</TreeViewItem.Header>
					</TreeViewItem>
				</TreeViewItem>
				<TreeViewItem>
					<TreeViewItem.Header>
						<TextBlock Text="Level 2.3" Foreground="Blue" />
					</TreeViewItem.Header>
				</TreeViewItem>
			</TreeViewItem>
		</TreeView>
	</Grid>
</Window>

我在这里做了很多事情,只是为了向您展示您获得的灵活性:我为子项着色,并为父项添加了图像甚至按钮。因为我们使用简单的标记来定义整个事物,所以您几乎可以做任何事情,但是正如您从示例代码中看到的那样,它确实有代价:大量的 XAML 代码,对于总共只有六个节点的树!

TreeView、数据绑定和多个模板 

WPF TreeView 支持数据绑定,就像几乎所有其他 WPF 控件一样,但是因为 TreeView 本质上是分层的,所以普通的 DataTemplate 通常是不够的。相反,我们使用 HierarchicalDataTemplate,它允许我们对树节点本身进行模板化,同时控制将哪个属性用作节点子项的源。

一个基本的数据绑定 TreeView
<Window x:Class="WpfTutorialSamples.TreeView_control.TreeViewDataBindingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:self="clr-namespace:WpfTutorialSamples.TreeView_control"
        Title="TreeViewDataBindingSample" Height="150" Width="200">
    <Grid Margin="10">
		<TreeView Name="trvMenu">
			<TreeView.ItemTemplate>
				<HierarchicalDataTemplate DataType="{x:Type self:MenuItem}" ItemsSource="{Binding Items}">
					<TextBlock Text="{Binding Title}" />
				</HierarchicalDataTemplate>
			</TreeView.ItemTemplate>
		</TreeView>
	</Grid>
</Window>
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<MenuItem>();
		}

		public string Title { get; set; }

		public ObservableCollection<MenuItem> Items { get; set; }
	}

}

在 XAML 标记中,我为TreeView的ItemTemplate指定了一个 HierarchicalDataTemplate 。我指示它使用 Items属性来查找子项,方法是设置模板的ItemsSource属性,并在其中定义实际模板,现在它只包含一个绑定到Title属性的 TextBlock 。

第一个示例非常简单,实际上非常简单,我们不妨手动添加 TreeView 项,而不是生成一组对象然后绑定到它们。然而,一旦事情变得更加复杂,使用数据绑定的优势就会变得更加明显。

不同类型的多个模板

在下一个示例中,我采用了一个稍微复杂一些的案例,我想在其中展示一棵家庭及其成员的树。一个家庭应该以一种方式表示,而它的每个成员都应该以另一种方式表示。我通过创建两个不同的模板并将它们指定为树(或窗口或应用程序 - 这真的取决于您)的资源来实现这一点,然后允许 TreeView 根据基础数据类型选择正确的模板。

这是代码 - 它的解释将紧随其后:

<Window x:Class="WpfTutorialSamples.TreeView_control.TreeViewMultipleTemplatesSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:self="clr-namespace:WpfTutorialSamples.TreeView_control"
        Title="TreeViewMultipleTemplatesSample" Height="200" Width="250">
	<Grid Margin="10">
		<TreeView Name="trvFamilies">
			<TreeView.Resources>
				<HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
					<StackPanel Orientation="Horizontal">
						<Image Source="/WpfTutorialSamples;component/Images/group.png" Margin="0,0,5,0" />
						<TextBlock Text="{Binding Name}" />
						<TextBlock Text=" [" Foreground="Blue" />
						<TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
						<TextBlock Text="]" Foreground="Blue" />
					</StackPanel>
				</HierarchicalDataTemplate>

                <HierarchicalDataTemplate DataType="{x:Type local:FamilyMember}" ItemsSource="{Binding Members}">
                    <StackPanel Orientation="Horizontal">

                        <TextBlock Text="{Binding Name}" />
                        <TextBlock Text=" [" Foreground="Blue" />
                        <TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
                        <TextBlock Text="]" Foreground="Blue" />
                    </StackPanel>
                </HierarchicalDataTemplate>

				<DataTemplate DataType="{x:Type self:FamilyMember}">
					<StackPanel Orientation="Horizontal">
						<Image Source="/WpfTutorialSamples;component/Images/user.png" Margin="0,0,5,0" />
						<TextBlock Text="{Binding Name}" />
						<TextBlock Text=" (" Foreground="Green" />
						<TextBlock Text="{Binding Age}" Foreground="Green" />
						<TextBlock Text=" years)" Foreground="Green" />
					</StackPanel>
				</DataTemplate>
			</TreeView.Resources>
		</TreeView>
	</Grid>
</Window>
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<Family> families = new List<Family>();

			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<FamilyMember>();
		}

		public string Name { get; set; }

		public ObservableCollection<FamilyMember> 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 的项目源。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值