包含复选框或单选按钮的 ListBox 控件

<Window x:Class="WpfApplication3.RadioListBoxWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="RadioListBoxWindow" Height="300" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <!--<ControlTemplate x:Key="ProductItemTemplate" TargetType="ListBoxItem">
            <RadioButton Focusable="False" IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource Mode=TemplatedParent},Mode=TwoWay}">
                <ContentPresenter></ContentPresenter>
            </RadioButton>
        </ControlTemplate>-->
        <Style x:Key="RadioButtonListStyle" TargetType="{x:Type ListBox}">
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="{x:Type ListBoxItem}" >
                        <Setter Property="Margin" Value="2" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                    <RadioButton 
                                            Focusable="False"
                                            IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
                                            >
                                        <ContentPresenter></ContentPresenter>
                                    </RadioButton>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <ListBox x:Name="lbProdcuts" Style="{StaticResource RadioButtonListStyle}" DisplayMemberPath="Name">

        </ListBox>
    </Grid>
</Window>

<Window x:Class="WpfApplication3.CheckBoxListWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="CheckBoxListWindow" Height="300" Width="300" Loaded="Window_Loaded">
    <Window.Resources>
        <Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListBox}">
            <Setter Property="SelectionMode" Value="Multiple" />
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="{x:Type ListBoxItem}" >
                        <Setter Property="Margin" Value="2" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                    <CheckBox 
                                            Focusable="False"
                                            IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
                                            >
                                        <ContentPresenter></ContentPresenter>
                                    </CheckBox>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <ListBox x:Name="lbProdcuts" Style="{StaticResource CheckBoxListStyle}" DisplayMemberPath="Name">

        </ListBox>
    </Grid>
</Window>

ListView 的 GridView及单元格模板

<Window x:Class="WpfApplication3.BasicListViewWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="BasicListViewWindow" Height="300" Width="300" Loaded="Window_Loaded">
    <Grid>
        <ListView Margin="5" x:Name="lvProducts">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="名称" DisplayMemberBinding="{Binding Name}" />
                    <GridViewColumn Header="单价" DisplayMemberBinding="{Binding UnitCost}" />
                    <GridViewColumn Header="照片">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Image Source="{Binding Cover}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

创建自定义视图

GridView 视图是第一个版本的 WPF 提供的唯一一个视图对象,但是可以创建自己的视图扩展 ListView 控件的功能。不过,这并不是很容易实现。

为了理解这一点,需要理解有关视图工作原理的更多细节。视图通过重写两个受保护的属性进行工作:DefaultStyleKey 属性和 ItemContainerKeyStyle 属性。这两个属性都返回一个被称为 ResourceKey 的特殊对象,该对象指向在 XAML 中定义的样式。DefaultStyleKey 属性指向将被用于配置整个 ListView 控件的样式,而 ItemContainer.DefaultStyleKey 属性指向将被用于配置 ListView 控件中每个 ListViewItem 元素的样式。尽管这些样式可以包含任意属性,但是它们通常通过替换用于 ListView 控件的 ControlTemplate 控件模板以及用于每个 ListViewItem 元素的 DataTemplate 数据模板进行工作。

这正是出现问题的地方。用于显示项的 DataTemplate 数据模板是在 XAML 标记中定义的。

在开始创建自定义视图之前,首先需要这样考虑:通过 ListBox 控件简单使用合适的 DataTemplate 数据模板,或者组合使用 ListView 控件与 GridView 视图,是否能够得到相同的结果。

  1. 视图类

  2. 视图样式

  3. 使用 ListView 控件

  4. 为视图传递信息

TreeView 控件

作为核心,TreeView 控件是宿主 TreeViewItem 对象的特殊 ItemsControl 控件。与 ListViewItem 对象不同,TreeViewItem 对象不是一个内容控件。而每个 TreeViewItem 对象是一个独立的 ItemsControl 控件,具有包含更多 TreeViewItem 对象的能力。通过这一灵活性可以创建更深层次的数据显示。

从技术上讲,TreeViewItem 类继承自 HeaderedItemsControl,HeaderedItemsControl 类又继承自 ItemsControl 类。HeaderedItemsControl 类添加了 Header 属性,该属性包含了希望为树中每个项显示的内容(通常是文本)。WPF 还提供了另外两个 HeaderedItemsControl 类:MenuItem 类和 ToolBar 类。

基本实例

<Window x:Class="WpfApplication3.TreeViewWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TreeViewWindow" Height="300" Width="300">
    <Grid>
        <TreeView>
            <TreeViewItem Header="水果">
                <TreeViewItem Header="橙子" />
                <TreeViewItem Header="香蕉" />
                <TreeViewItem Header="柚子" />
            </TreeViewItem>
            <TreeViewItem Header="蔬菜">
                <TreeViewItem Header="茄子" />
                <TreeViewItem Header="南瓜" />
                <TreeViewItem Header="菠菜" />
            </TreeViewItem>
        </TreeView>
    </Grid>
</Window>

使用数据绑定的 TreeView 控件

使用 HierarchicalDataTemplate 对象设置 TreeView.ItemTemplate 属性,而不是使用 DataTemplate 对象。HierarchicalDataTemplate 对象有一个额外的优点,它能够包装第二个模板。之后 HierarchicalDataTemplate 对象就可以从第一层次的数据中提取一个项集合,并将它作为第二层次的模板。可以简单的设置 ItemsSource 属性,指示该属性具有子项,并设置 ItemTemplate 属性,指示如何格式化每个对象。

<Window x:Class="WpfApplication3.TreeViewWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TreeViewWindow" Height="300" Width="300" Loaded="Window_Loaded">
    <Grid>
        <TreeView x:Name="tvCategories" Margin="5">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Path=Products}">
                    <TextBlock Text="{Binding Path=CategoryName}" />
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name}" />
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace WpfApplication3
{
    public class Product
    {
        public string Name { get; set; }
        public bool IsSelected { get; set; }
        public decimal UnitCost { get; set; }
        public string Cover { get; set; }
    }

    public class Category : INotifyPropertyChanged
    {
        private string categoryName;

        public string CategoryName
        {
            get
            {
                return categoryName;
            }
            set 
            {
                categoryName = value;
                OnPropertyChanged(new PropertyChangedEventArgs("CategoryName"));
            }
        }

        private ObservableCollection<Product> products;

        public ObservableCollection<Product> Products
        {
            get
            {
                return products;
            }
            set
            {
                products = value;
                OnPropertyChanged(new PropertyChangedEventArgs("Products"));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
        }

        public Category(string categoryName, ObservableCollection<Product> products)
        {
            CategoryName = categoryName;
            Products = products;
        }
    }
}


using System;
using System.Collections.Generic;
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.Shapes;
using System.Threading;
using System.Collections.ObjectModel;

namespace WpfApplication3
{
    /// <summary>
    /// TreeViewWindow.xaml 的交互逻辑
    /// </summary>
    public partial class TreeViewWindow : Window
    {
        public TreeViewWindow()
        {
            InitializeComponent();
        }

        private ObservableCollection<Category> categories { get; set; }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.Dispatcher.BeginInvoke((ThreadStart)delegate() {
                List<Category> list = new List<Category>();

                list.Add(new Category("Category1", new ObservableCollection<Product>(new List<Product> { new Product { Name = "Category1_model1", UnitCost = 998 } })));

                list.Add(new Category("Category2", new ObservableCollection<Product>(new List<Product> { new Product { Name = "Category2_model1", UnitCost = 3.25m } })));

                categories = new ObservableCollection<Category>(list);
                this.tvCategories.ItemsSource = categories;

            });
        }
    }
}

即时节点的创建

TreeView 控件经常被用于包含大量的数据。装满数据的 ListView 对象存在一个问题,因为 ListView 控件没有提供任何虚拟化功能或者分页功能。因此,所有的数据需要立即加载,并作为单独的位于内存中的对象交给 ListView 控件。对于这种情况,TreeView 控件显得稍微好些。这是因为 TreeView 控件的显示是能够折叠的。即使用户从顶部滚动到底部,也不需要显示全部信息。完全可以在 TreeView 控件中省略不显示的信息,从而降低开销(以及填充树需要的时间)。甚至更好的是,当展开每个 TreeViewItem 对象会引发一个 Expanded 事件,并在关闭时会引发一个 Collapsed 事件。可以通过处理这两个事件即时填充丢失的节点或丢弃不在需要的节点。这种技术被称为即时节点创建。

<Window x:Class="WpfApplication3.DirectoryTreeViewWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DirectoryTreeViewWindow" Height="300" Width="300" Loaded="Window_Loaded">
    <Grid>
        <TreeView x:Name="tvFileBrowser" TreeViewItem.Expanded="tvFileBrowser_Expanded">

        </TreeView>
    </Grid>
</Window>

using System;
using System.Collections.Generic;
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.Shapes;
using System.Threading;
using System.IO;

namespace WpfApplication3
{
    /// <summary>
    /// DirectoryTreeViewWindow.xaml 的交互逻辑
    /// </summary>
    public partial class DirectoryTreeViewWindow : Window
    {
        public DirectoryTreeViewWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.Dispatcher.BeginInvoke((ThreadStart)delegate() {
                foreach (DriveInfo drive in DriveInfo.GetDrives())
                {
                    TreeViewItem tvi = new TreeViewItem();
                    tvi.Tag = drive;
                    tvi.Header = drive.ToString();

                    tvi.Items.Add("*");
                    tvFileBrowser.Items.Add(tvi);
                }
            });
        }

        private void tvFileBrowser_Expanded(object sender, RoutedEventArgs e)
        {
            TreeViewItem tvi = (TreeViewItem)e.OriginalSource;

            tvi.Items.Clear();

            DirectoryInfo di;

            if(tvi.Tag is DriveInfo)
            {
                DriveInfo drive = (DriveInfo)tvi.Tag;
                di = drive.RootDirectory;
            }
            else
            {
                di = (DirectoryInfo)tvi.Tag;
            }

            try
            {
                foreach (DirectoryInfo subDir in di.GetDirectories())
                {
                    var stvi = new TreeViewItem { Tag = subDir, Header = subDir.ToString() };
                    stvi.Items.Add("*");
                    tvi.Items.Add(stvi);
                }
            }
            catch 
            {
                throw;
            }

        }
    }
}

菜单

WPF 提供了两个菜单控件:Menu 控件(用于主菜单)和 ContextMenu 控件(用于关联到其他元素的弹出式菜单)。和所有的 WPF 类一样,WPF 负责呈现 Menu 控件和 ContextMenu 控件。

Menu 类

<Window x:Class="WpfApplication3.MenuWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MenuWindow" Height="300" Width="300">
    <Window.Resources>
        <Image x:Key="file" Source="file.png" Height="24" Width="24" />
    </Window.Resources>
    <DockPanel LastChildFill="True">
        <Menu DockPanel.Dock="Top">
            <MenuItem StaysOpenOnClick="True" Icon="{StaticResource file}"  Header="文件">
                <MenuItem Header="新建" />
                <Separator />
                <MenuItem Header="打开" />
            </MenuItem>
            <MenuItem StaysOpenOnClick="True" Header="编辑">
                <MenuItem StaysOpenOnClick="True" Header="删除" IsCheckable="True" IsChecked="True" />
            </MenuItem>
            <MenuItem StaysOpenOnClick="True" Header="窗口">
                <MenuItem StaysOpenOnClick="True" Header="拆分" IsCheckable="True" IsChecked="True" />
                <Separator>
                    <Separator.Template>
                        <ControlTemplate>
                            <Border CornerRadius="2" Padding="5" Background="PaleGoldenrod" BorderBrush="Black" BorderThickness="1">
                                <TextBlock FontWeight="Bold" Text="分隔符" />
                            </Border>
                        </ControlTemplate>
                    </Separator.Template>
                </Separator>
                <MenuItem StaysOpenOnClick="True" Header="浮动" IsCheckable="True" IsChecked="True" />
            </MenuItem>
        </Menu>
    </DockPanel>
</Window>

工具条和状态栏

工具条和状态栏是 Windows 领域内的两个重要内容。这两个控件都是包含项集合的特殊包容器。通常,工具条包含按钮,而状态栏包含基本一致的文本和其他非交互的指示器(如进度条)。然后,工具条和状态栏都可以使用不同的控件。

在 Windows 窗体中,工具条和状态栏有它们自己的内容模型。尽管也能够使用包装器在工具条和状态栏中随意放置内容,但是这个过程不是无缝的。

Toolbar 控件

典型的 Toolbar 控件填满了 Button 对象、ComboBox 对象、CheckBox 对象、RadioButton 对象以及 Separator 对象。尽管这些元素都是内容控件(Separator 元素除外),所以可以在它们内部放置文本和图像。尽管可以使用其他元素,如 Label 对象和 Image 对象,在 Toolbar 控件中放置不能交互的元素,但是这通常会令人感到困惑。

<Window x:Class="WpfApplication3.ToolbarWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ToolbarWindow" Height="300" Width="300">
    <DockPanel LastChildFill="True">
        <ToolBar UseLayoutRounding="True" BandIndex="1" DockPanel.Dock="Top">
            <Button>
                <Button.Content>
                    <Image Source="images/download.png" Width="24" Height="24" />
                </Button.Content>
            </Button>
            <Separator />
            <ComboBox SelectedIndex="0">
                <ComboBoxItem>100%</ComboBoxItem>
                <ComboBoxItem>50%</ComboBoxItem>
                <ComboBoxItem>25%</ComboBoxItem>
            </ComboBox>
        </ToolBar>
        <ToolBar UseLayoutRounding="True" BandIndex="0"  DockPanel.Dock="Top">
            <Button>
                <Button.Content>
                    <Image Source="images/download.png" Width="24" Height="24" />
                </Button.Content>
            </Button>
            <Separator />
        </ToolBar>
        <Grid></Grid>
    </DockPanel>
</Window>

StatusBar 控件

相对于 Toolbar 类,StatusBar 类时一个魅力更小的控件类。与 ToolBar 控件一样,它可以包含任何内容(被隐式的包装到 StatusBarItem 对象中),并且重写了一些元素的默认样式,使它们更适合呈现。然而 StatusBar 控件不支持拖动式的重新排列,也不支持浮动菜单。它主要用于显示文本和图像指示器(并且有时用于显示进度条)。

如果使用状态栏,有一个提示值得注意。通常,StatusBar 控件使用一个水平的 StackPanel 面板,从左向右地放置它的子元素。然而,应用程序经常使用按比例设置尺寸的状态栏项,或者将某些项保持锁定在状态栏的右边。可以通过指示状态栏使用不同的面板实现这种设计。唯一的技巧是必须在 StatusBarItem 对象中包装子元素,从而能够正确的设置 Grid.Column 属性。

<Window x:Class="WpfApplication3.StatusBarWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="StatusBarWindow" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <StatusBar Grid.Row="1">
            <StatusBar.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </StatusBar.ItemsPanel>
            <TextBlock Text="Left Side" />
            <StatusBarItem Grid.Column="1">
                <TextBlock Text="Right Size" />
            </StatusBarItem>
        </StatusBar>
    </Grid>
</Window>

知识共享许可协议
《WPF 数据绑定及树、工具条和状态栏》 常伟华 创作。
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议 | 3.0 中国大陆许可协议进行许可。

站内公告

A PHP Error was encountered

Severity: Core Warning

Message: PHP Startup: zip: Unable to initialize module Module compiled with module API=20060613 PHP compiled with module API=20090626 These options need to match

Filename: Unknown

Line Number: 0

Backtrace: