記得原來做Winfrom通過Item的Bounds可以獲得整行的地區,但是在WPF中進行了幾個布局方式都沒能成功!
VS中的解決方案效果布局如下:
這樣在第一行第二列方式Border控制項,此為選中地區。而如果要實現右側系統的效果,必須要在選中地區的時候補齊第二行第一列的寬度,但是如果在TreeViewItem的Template不設定子節點列表縮排的話,將無法定位子節點列表縮排!
對比了Winform的TreeNode類型有兩個關鍵的屬性:FullPath和Level。只要知道一個就可以根據Indent算出相應的縮排寬度,從這樣的思路上,這個Item布局結構就要更改如下了:
代碼結構如下:
<StackPanel>
<Border x:Name=”itemBorder”>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinitions Width=”19” />
<ColumnDefinitions Width=”*” />
</Grid.ColumnDefinitions>
<Path x:Name=”TreeArrow” Grid.Column=”0” />
<ContentPresenter ContentSource=”Header” Grid.Column=”1” />
</Grid>
</Border>
<ItemsPresenter x:Name="ItemsHost" />
</StackPanel>
可是此時的子節點列表沒有縮排怎麼辦?
做幾個預備動作,在TreeViewItem裡面有一個TreeNode的特殊特性,叫做Level,獲得節點所在的層級,在這裡我做了個Extensions類:
public static class TreeViewItemExtensions
{
public static int GetDepth(this TreeViewItem item)
{
FrameworkElement elem = item;
while (elem.Parent != null)
{
var tvi = elem.Parent as TreeViewItem;
if (null != tvi)
return tvi.GetDepth() + 1;
elem = elem.Parent as FrameworkElement;
}
return 0;
}
}
用來獲得TreeViewItem在TreeView裡面的層級深度。然後就是怎麼將縮排的綁定到它該在的地方了!
在上面那段TreeViewItem的Template代碼裡面的itemBorder就是節點項的整體範圍了,如果我們想讓選中邊框始終保持Aero樣式中滿行選中的狀態就只能在itemBorder中的Grid上做手腳了,上面擴充了TreeViewItem對象,可以擷取的到層級深度,而縮排值則等於節點層級和縮排值的乘積,而應用縮排值需要實現一個縮排的轉換類型方法:
public class IndentConverter:IValueConverter
{
public double Indent{ get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var item = value as TreeViewItem;
if (null == item)
return new Thickness(0);
return new Thickness(Indent* item.GetDepth(), 0, 0, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
然後就是改造上一段模板內容。完整代碼如下:
<Style TargetType="{x:Type TreeViewItem}" x:Key="aaa">
<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<ControlTemplate.Resources>
<o2ds:IndentConverter Indent="19" x:Key="indentConverter" />
</ControlTemplate.Resources>
<StackPanel>
<Border Name="itemBackground" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<Grid Margin="{Binding Converter={StaticResource lengthConverter},RelativeSource={RelativeSource TemplatedParent}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ToggleButton Grid.Column="0" x:Name="ArrowButton" Style="{StaticResource TreeViewArrowButtonStyle}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press" />
<ContentPresenter Grid.Column="1" x:Name="PART_Header" ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
</Grid>
</Border>
<ItemsPresenter x:Name="ItemsHost" />
</StackPanel>
<ControlTemplate.Triggers>
Trigger something…
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Grid Margin="{Binding Converter={StaticResource indentConverter},RelativeSource={RelativeSource TemplatedParent}}">
indentConverter對象承擔了擷取深度以及轉換邊距的任務。由此我們就完成了實現整行選中TreeViewItem的任務,至於Aero樣式的效果細節,後面放出。