二、帶復選框的TreeView
說(shuō)明:在TreeView中設置復選框是十分常見(jiàn)的,這有助于我們對于同組數據的一次性選取或取消。本文就將為你介紹怎樣在Silverlight中實(shí)現帶有Checkbox的TreeView。
①最初的步驟:
※ObjectCollection
這是Silverlight Toolkit 提供的一個(gè)對象集合,用以提供靜態(tài)的對象資源綁定。注意:使用時(shí)一定要添加System.Windows.Controls.Toolkit的引用。在Skysigal上有一篇介紹靜態(tài)資源數據綁定的好文章[鏈接],推薦給大家。
※HierarchicalDataTemplate
這是用于處理層次狀數據而設置的數據模板,其主要用于具有HeaderedItemsControl的組件,比如說(shuō)TreeViewItem。詳細內容請參考這里。
※INotifyPropertyChanged
向客戶(hù)端發(fā)出某一屬性值已更改的通知。主要用于實(shí)現數據的雙向綁定。詳細內容請參考這里。
②實(shí)現業(yè)務(wù)對象Feature:
通過(guò)實(shí)現該業(yè)務(wù)對象,將能使其與TreeView進(jìn)行交互。構建起這一對象的步驟主要有下述幾步:
第一,聲明可在XAML文件中顯示的內容屬性,添加屬性標簽[ContentProperty("SubComponents")]。
第二,使Feature對象繼承接口INotifyPropertyChanged。
第三,設定Feature對象的屬性。
第四,添加實(shí)現Checkbox效果的重要屬性HasSubcomponents和ShouldInstall。
第五,實(shí)現接口INotifyPropertyChanged定義的函數。
具體代碼請見(jiàn)下文。
③具體部署組件:
在MainPage.xaml文件中添加Feature對象的ObjectCollection資源,添加代表Feature對象Item的模板,以及添加有關(guān)數據對象的資源綁定。在MainPage.xaml.cs文件中添加對于TreeView組件的事件處理函數。具體代碼請見(jiàn)下文。
實(shí)例:
效果圖:
代碼段:
Feature業(yè)務(wù)對象代碼(Feature.cs):
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Markup;
namespace SilverlightClient
{
[ContentProperty("Subcomponents")] //聲明可在XAML文件中顯示的內容屬性
public class Feature : INotifyPropertyChanged //繼承接口INotifyPropertyChanged用于雙向數據綁定
{
//Feature對象的屬性
public string FeatureName { get; set; }
public string Description { get; set; }
//聲明全局變量
public Collection<Feature> Subcomponents { get; private set; }
private bool? _shouldInstall;
//是否有子組件
public bool HasSubcomponents
{
get
{
return Subcomponents.Count > 0;
}
}
//是否允許Feature進(jìn)行安置
public bool? ShouldInstall
{
get
{
return _shouldInstall;
}
set
{
if (value != _shouldInstall)
{
_shouldInstall = value;
OnPropertyChanged("ShouldInstall");
}
}
}
//構造函數
public Feature()
{
Subcomponents = new Collection<Feature>();
ShouldInstall = true;
}
//事件委托
public event PropertyChangedEventHandler PropertyChanged;
//實(shí)現接口INotifyPropertyChanged定義函數
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
MainPage.xaml代碼:
<UserControl
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"
xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:common="clr-namespace:System.Windows;assembly=System.Windows.Controls"
xmlns:samplesCommon="clr-namespace:SilverlightClient"
mc:Ignorable="d" x:Class="SilverlightClient.MainPage"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" Background="White" Width="640" Height="480">
<StackPanel>
<StackPanel.Resources>
<!-- 用于安置的示例Features -->
<toolkit:ObjectCollection x:Key="CorporationFeatures">
<samplesCommon:Feature FeatureName="公司部門(mén)" Description="公司各部門(mén)的結構">
<samplesCommon:Feature FeatureName="建筑部" Description="負責公司的工程項目">
<samplesCommon:Feature FeatureName="設計科" Description="負責項目的設計" />
<samplesCommon:Feature FeatureName="工程科" Description="負責項目的具體實(shí)施" />
</samplesCommon:Feature>
<samplesCommon:Feature FeatureName="管理部" Description="負責管理公司的財務(wù)與人事">
<samplesCommon:Feature FeatureName="財務(wù)科" Description="負責公司的對內對外的財務(wù)事宜" />
<samplesCommon:Feature FeatureName="總務(wù)人事科" Description="負責公司日常事務(wù)及員工招聘" />
</samplesCommon:Feature>
</samplesCommon:Feature>
</toolkit:ObjectCollection>
<!-- 代表一個(gè)Feature項的模板 -->
<common:HierarchicalDataTemplate x:Key="NodeTemplate" ItemsSource="{Binding Subcomponents}">
<StackPanel Orientation="Horizontal" ToolTipService.ToolTip="{Binding Description}">
<CheckBox
IsTabStop="False"
IsThreeState="{Binding HasSubcomponents}"
IsChecked="{Binding ShouldInstall, Mode=TwoWay}"
Click="ItemCheckbox_Click"
/>
<ContentPresenter Content="{Binding FeatureName}" />
</StackPanel>
</common:HierarchicalDataTemplate>
</StackPanel.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<controls:TreeView
Grid.Column="0"
ItemTemplate="{StaticResource NodeTemplate}"
ItemsSource="{StaticResource CorporationFeatures}" FontSize="14">
<!-- 用來(lái)一次展開(kāi)TreeView所有結點(diǎn) -->
<controls:TreeView.ItemContainerStyle>
<Style TargetType="controls:TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</controls:TreeView.ItemContainerStyle>
</controls:TreeView>
</Grid>
</StackPanel>
</Grid>
</UserControl>
MainPage.xaml.cs代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SilverlightClient
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
//處理Checkbox點(diǎn)擊事件
private void ItemCheckbox_Click(object sender, RoutedEventArgs e)
{
TreeViewItem item = GetParentTreeViewItem((DependencyObject)sender);
if (item != null)
{
Feature feature = item.DataContext as Feature;
if (feature != null)
{
UpdateChildrenCheckedState(feature);//更新子組件選中狀態(tài)
UpdateParentCheckedState(item);//更新父組件選中狀態(tài)
}
}
}
//靜態(tài)方法:獲取父級TreeViewItem
private static TreeViewItem GetParentTreeViewItem(DependencyObject item)
{
if (item != null)
{
DependencyObject parent = VisualTreeHelper.GetParent(item);//獲取依賴(lài)的父級對象
TreeViewItem parentTreeViewItem = parent as TreeViewItem;//對象轉換
return (parentTreeViewItem != null) ? parentTreeViewItem : GetParentTreeViewItem(parent);//如果父級TreeViewItem存在則返回,否則就遞歸尋找
}
//找不到父對象,返回父對象不存在
return null;
}
//靜態(tài)方法:更新父級TreeViewItem選中狀態(tài)
private static void UpdateParentCheckedState(TreeViewItem item)
{
TreeViewItem parent = GetParentTreeViewItem(item);//獲取父級TreeViewItem
if (parent != null)//如果父對象不為空,為空則退出遞歸尋找
{
Feature feature = parent.DataContext as Feature;//對象轉換
if (feature != null)//如果對象不為空
{
//更新子組件的選中狀態(tài)
bool? childrenCheckedState = feature.Subcomponents.First<Feature>().ShouldInstall;//得到第一個(gè)子組件的選中狀態(tài)
for (int i = 1; i < feature.Subcomponents.Count(); i++)
{
if (childrenCheckedState != feature.Subcomponents[i].ShouldInstall)
{
childrenCheckedState = null;
break;
}
}
//將父組件的選中狀態(tài)與子組件置為相同
feature.ShouldInstall = childrenCheckedState;
//繼續遞歸搜索.
UpdateParentCheckedState(parent);
}
}
}
//用遞歸更新子組件的選中狀態(tài)
private static void UpdateChildrenCheckedState(Feature feature)
{
if (feature.ShouldInstall.HasValue)
{
foreach (Feature childFeature in feature.Subcomponents)
{
childFeature.ShouldInstall = feature.ShouldInstall;
if (childFeature.Subcomponents.Count() > 0)
{
UpdateChildrenCheckedState(childFeature);
}
}
}
}
}

