国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Advanced Custom TreeView Layout in WPF

Introduction

In a previous article here on the CodeProject I demonstrated one way of redefining how a WPF TreeView can be displayed. In that article we examined how to make a TreeViewlook like an Org Chart by using some XAML magic. In this article wewill explore a more complex and interactive customization, which makesthe TreeView present its items as a set of "nested buckets."

Show me the money

Let‘s take look at what the customized TreeView lookslike. Later we will see how this custom layout was implemented. Thescreenshot seen below is of a demo application, which is available fordownload via a link at the top of this page.

The top portion of the user interface is the customized TreeView.The innermost items provide links to Wikipedia pages containinginformation about the cities named in those items. The bottom portionof the UI is a Frame element, which loads and displays the Wikipedia pages chosen in the TreeView.

If we did not apply a layout customization to the TreeView seen above, it would look like this:

What is a layout customization?

Take another quick glance again at the non-customized TreeViewscreenshot above. Notice that the leaf items are rendered ashyperlinks, even though a layout customization is not in effect. Theway a TreeViewItem‘s content renders is affected by datatemplates. What I refer to as a "layout customization" does not dealwith rendering an item‘s content, rather, it explains how to renderthat which contains an item‘s content and how those containers arepositioned relative to one another. The item content is irrelevant forour purposes.

TreeView and TreeViewItem both derive from ItemsControl. An ItemsControlcontains item containers, which can be thought of as ‘boxes‘ that holdarbitrary content. The content of those boxes is the data which theuser consumes (i.e. the stuff that the user cares about and pays themost attention to). In a TreeView those boxes are represented by TreeViewItem objects. A layout customization organizes the TreeViewItems – explaining how they should be positioned, how they should be rendered, if they should be shown or hidden, etc.

How it works

Data Format

Before delving into the code which implements our custom layout,let‘s take a moment to review the data being displayed. In the demoapplication a TreeView is bound to XML data, which is in this simple format:

<?xml version="1.0" encoding="utf-8" ?>
<Countries>
<Country CountryName="USA">
<Region RegionName="California">
<City
CityName="Los Angeles"
Uri="http://en.wikipedia.org/wiki/Los_Angeles" />

<!-- More City elements... -->

</Region>

<!-- More Region elements... -->

</Country>

<!-- More Country elements... -->

</Countries>

The TreeView displays the Country, Region, and City elements as TreeViewItems.It renders Country and Region items as collapsible groups, whosecaption is the CountryName or RegionName attribute value, and the innerlist of items is taken from the element‘s set of nested child elements(a country contains regions, and a region contains cities).

TreeViewItem Style

Below is an abridged version of the Style which contains most of the custom layout implementation:

Collapse
<Style TargetType="TreeViewItem">
<Style.Resources>
<!-- Resources omitted for clarity... -->
</Style.Resources>

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid Margin="8,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<!-- This Border contains elements which display
the content and child items of the TreeViewItem. -->
<Border Name="Bd"
Background="{StaticResource ItemAreaBrush}"
BorderBrush="{StaticResource ItemBorderBrush}"
BorderThickness="0.6"
CornerRadius="8"
Padding="6"
SnapsToDevicePixels="True"
>
<Grid>
<!-- Items with children are shown in an Expander. -->
<Expander Name="Exp"
IsExpanded="{TemplateBinding TreeViewItem.IsExpanded}">
<Expander.Header>
<!-- Displays the item‘s header in the Expander. -->
<ContentPresenter ContentSource="Header" />
</Expander.Header>
<!-- Displays the item‘s children. -->
<ItemsPresenter />
</Expander>

<!--Items without children are shown in a ContentPresenter.-->
<ContentPresenter Name="CntPres"
ContentSource="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="Collapsed"
/>
</Grid>
</Border>
</Grid>

<ControlTemplate.Triggers>
<!-- If the TreeViewItem has child items,
show it in an Expander. Otherwise
hide the Expander and show the hidden
ContentPresenter. -->
<Trigger Property="TreeViewItem.HasItems" Value="false">
<Setter
TargetName="Exp"
Property="Visibility"
Value="Collapsed" />
<Setter
TargetName="CntPres"
Property="Visibility"
Value="Visible" />
</Trigger>

<!--When the item is selected in the TreeView, use the
"selected" colors and give it a drop shadow. -->
<Trigger Property="IsSelected" Value="true">
<!-- Setters omitted for clarity... -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>

<!-- Make each TreeViewItem show it‘s children
in a StackPanel. If it is a root item then
the Orientation will be ‘Horizontal‘, else
‘Vertical‘. -->
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<ItemsPanelTemplate.Resources>
<local:ItemsPanelOrientationConverter x:Key="conv" />
</ItemsPanelTemplate.Resources>
<StackPanel
IsItemsHost="True"
Orientation="{Binding
RelativeSource={x:Static RelativeSource.TemplatedParent},
Converter={StaticResource conv}}"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>

Choosing the right visuals

There are a couple aspects of the XAML seen above worth pointing out. The Style sets the Template property of TreeViewItem to a ControlTemplate. That template has the job of explaining how a TreeViewIteminstance should be rendered. Items that represent a Country or RegionXML element must render as collapsible groups, but City elements shouldnot. Let‘s take a closer look at how that is achieved. I stripped awaysome unimportant settings, so that we can focus on the essentialinformation:

<Border>
<Grid>
<!-- Items with children are shown in an Expander. -->
<Expander Name="Exp">
<Expander.Header>
<!-- Displays the item‘s header in the Expander. -->
<ContentPresenter ContentSource="Header" />
</Expander.Header>
<!-- Displays the item‘s children. -->
<ItemsPresenter />
</Expander>

<!-- Items without children are shown in a ContentPresenter. -->
<ContentPresenter Name="CntPres"
ContentSource="Header"
Visibility="Collapsed" />
</Grid>
</Border>

The XAML above creates a Border element which contains a Grid panel. That Grid has one row and one column (i.e. one "cell"). That cell contains an Expander and a ContentPresenter, but only one of those two elements will ever be visible at any given moment. The Expander is there in case the TreeViewItem has child items. The ContentPresenter will be shown if the item does not have any children, in this case, if it represents a City element in the XML data.

The control template has a Trigger to determine which element should be used to render the TreeViewItem. That Trigger is seen below:

<Trigger Property="TreeViewItem.HasItems" Value="false">
<Setter
TargetName="Exp"
Property="Visibility"
Value="Collapsed" />
<Setter
TargetName="CntPres"
Property="Visibility"
Value="Visible" />
</Trigger>

Item layout direction

Another tricky aspect to the layout seen in the screenshot at the top of this article has to do with the direction in which TreeViewItemsare arranged. The items representing Country and Region elements arearranged in a horizontal row, but the City items are in a verticallist.

Arranging the root items (the Country items) in a horizontal row requires the TreeView‘s ItemsPanel property to be set to a StackPanel with a horizontal orientation. Here is some XAML from the demo app‘s main Window which configures the TreeView:

<TreeView Name="tree" 
DataContext="{StaticResource countriesXml}"
ItemsSource="{Binding}"
>
<!-- Import the resource file with the
new TreeViewItem style. -->
<TreeView.Resources>
<ResourceDictionary
Source="GroupedTreeViewItemStyle.xaml" />
</TreeView.Resources>

<!-- Arrange the root items horizontally. -->
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
IsItemsHost="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
</TreeView>

The next piece of the puzzle requires a little more trickery than just setting a property. Since the TreeViewItemsthat represent Region elements must be arranged horizontally but Cityitems must be listed vertically, we need to use a value converter todetermine at runtime what orientation a TreeViewItem‘s ItemsPanel should use. Here‘s the XAML from the Style seen previously which sets the ItemsPanel for a TreeViewItem:

<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<ItemsPanelTemplate.Resources>
<local:ItemsPanelOrientationConverter x:Key="conv" />
</ItemsPanelTemplate.Resources>
<StackPanel
IsItemsHost="True"
Orientation="{Binding
RelativeSource={x:Static RelativeSource.TemplatedParent},
Converter={StaticResource conv}}"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>

The StackPanel used as the ItemsPanel template has its Orientation property bound. The binding uses a value converter to determine whether the StackPanel should have a horizontal or vertical orientation. Here‘s the code for the value converter:

Collapse
[ValueConversion( typeof( ItemsPresenter ), typeof( Orientation ) )]
public class ItemsPanelOrientationConverter : IValueConverter
{
// Returns ‘Horizontal‘ for root TreeViewItems
// and ‘Vertical‘ for all other items.
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture )
{
// The ‘value‘ argument should reference
// an ItemsPresenter.
ItemsPresenter itemsPresenter = value as ItemsPresenter;
if( itemsPresenter == null )
return Binding.DoNothing;

// The ItemsPresenter‘s templated parent
// should be a TreeViewItem.
TreeViewItem item = itemsPresenter.TemplatedParent as TreeViewItem;
if( item == null )
return Binding.DoNothing;

// If the item is contained in a TreeView then it is
// a root item. Otherwise it is contained in another
// TreeViewItem, in which case it is not a root.
bool isRoot =
ItemsControl.ItemsControlFromItemContainer( item ) is TreeView;

// The children of root items are layed out
// in a horizontal row. The grandchild items
// (i.e. cities) are layed out vertically.
return
isRoot ?
Orientation.Horizontal :
Orientation.Vertical;
}

public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture )
{
throw new NotSupportedException( "Cannot convert back." );
}
}
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
使用Silverlight Toolkit TreeView樹形控件
TreeView控件研究(WPF篇)(創(chuàng)建+數(shù)據(jù)綁定+整行選中)
使用Vue實現(xiàn)一個樹組件
WPF快速入門系列(7)
WPF的Presenter(ContentPresenter)
【silverlight】Silverlight TreeViw默認(rèn)展開節(jié)點
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服