Silverlight TreeView ScrollViewer Issue - silverlight

Hey SO, got a question about the TreeView control in Silverlight.
I have an application which dynamically adds elements to a treeview. Some of the elements are long enough to require horizontal scrolling. When they are added to the treeview, my treeview remains correctly all the way scrolled left so you have to scroll to see the end of the item. However, if I click on one of my items (which the hides the treeview), and then use the 'back to results' button I implemented (note this only deals with visibility changes) the treeview becomes visible and it is automatically scrolled to the center.
Does anyone know how I can get the treeview to scroll all the way left when I hit back to results?
I've tried messing with the treeview template:
<Style TargetType="controls:TreeView" x:Name="SCREW">
<Setter Property="Background" Value="#FFFFFFFF" />
<Setter Property="Foreground" Value="#FF000000" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="Cursor" Value="Arrow" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="1" />
<Setter Property="BorderBrush" Value="#FF000000" />
<Setter Property="IsTabStop" Value="True" />
<Setter Property="TabNavigation" Value="Once" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:TreeView" x:Name="SCREWTEMPLATE">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed" />
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Unfocused" />
<VisualState x:Name="Focused" />
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid" />
<VisualState x:Name="InvalidUnfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Validation" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="InvalidFocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Validation" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationToolTip" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>True</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" Margin="1">
<ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="{x:Null}" BorderBrush="Transparent" BorderThickness="0" IsTabStop="False" TabNavigation="Once" Loaded="ScrollViewer_Loaded">
<ItemsPresenter Margin="5" />
</ScrollViewer>
</Border>
</Border>
<Border x:Name="Validation" Grid.Column="1" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="#FFDB000C" CornerRadius="2" Visibility="Collapsed">
<ToolTipService.ToolTip>
<ToolTip x:Name="ValidationToolTip" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" IsHitTestVisible="True" />
</ToolTipService.ToolTip>
<Grid Width="10" Height="10" HorizontalAlignment="Right" Margin="0,-4,-4,0" VerticalAlignment="Top" Background="Transparent">
<Path Margin="-1,3,0,0" Fill="#FFDC000C" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 Z" />
<Path Margin="-1,3,0,0" Fill="#FFFFFFFF" Data="M 0,0 L2,0 L 8,6 L8,8" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But the problem with that is I don't know how to access the ScrollViewer from the code behind... so I can't call ScrollView.setScrollOffset(0d,0d) or anything like that.
Any ideas? Thanks a million.
One last thing, I'd like to try to avoid implementing a new control that extends treeview. I'm really hoping there is a way to access/modify and use functions associated with the control template from c# codebehind.

I'd just set up an attached property for this and create the logic you want in there. Then you would decorate your treeview with the attached property. We do something similar with other controls that contain scrollviewers:
public class ScrollResetService
{
public static DependencyProperty IsScrollResetProperty = DependencyProperty.RegisterAttached("IsScrollReset",
typeof(bool),
typeof(ScrollResetService),
new PropertyMetadata(false,
OnIsScrollResetChanged));
public static void SetIsScrollReset(DependencyObject d, bool value)
{
d.SetValue(IsScrollResetProperty, value);
}
public static bool GetIsScrollReset(DependencyObject d)
{
return d.GetValue(IsScrollResetProperty) == null ? false : (bool)d.GetValue(IsScrollResetProperty);
}
private static void OnIsScrollResetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var treeView = d as TreeView;
bool isScrollReset;
if (e.NewValue!= null && bool.TryParse(e.NewValue.ToString(), out isScrollReset) && treeView != null)
{
treeView.SelectedItemChanged += (sender, args) =>
{
var scrolls =
treeView.GetAllLogicalChildrenOfType<IScrollInfo>();
scrolls.ForEach(i => i.SetVerticalOffset(0));
};
}
}
}
public static class Extensions
{
public static IEnumerable<T> GetAllLogicalChildrenOfType<T>(this FrameworkElement parent)
{
Debug.Assert(parent != null, "The parent cannot be null.");
return parent.GetVisualChildren().Flatten(item => item.GetVisualChildren()).OfType<T>();
}
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> childSelector)
{
if (items == null) return Enumerable.Empty<T>();
return items.Concat(items.SelectMany(i => childSelector(i).Flatten(childSelector)));
}
internal static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
for (int counter = 0; counter < childCount; counter++)
{
yield return VisualTreeHelper.GetChild(parent, counter);
}
}
}

I put this code into a behavior (would also work in code behind) after handling the SelectedItemChanged event of the TreeView:
var offset = this.HorizontalScrollOffsetAfterSelect; // would be 0 if you don't want to make that adjustable
if (!Double.IsNaN(offset))
{
var scrollViewer = trv.GetVisualDescendants().OfType<ScrollViewer>().FirstOrDefault();
if (scrollViewer != null)
{
scrollViewer.ScrollToHorizontalOffset(offset);
// and because that wasn't enough because of timing issues:
var scrollBar = scrollViewer.GetVisualDescendants().OfType<ScrollBar>().Where(cur => cur.Orientation == Orientation.Horizontal).FirstOrDefault();
if (scrollBar != null)
{
RoutedPropertyChangedEventHandler<double> handler = null;
handler = (sender, e) =>
{
scrollBar.ValueChanged -= handler;
scrollViewer.ScrollToHorizontalOffset(offset);
};
scrollBar.ValueChanged += handler;
}
}
}
Requires using System.Linq; and using System.Windows.Controls.Primitives; and the Toolkit ofc.
Works well enough for me so far.

Related

InvalidOperationException Ellipse not Found after Binding "IsChecked"

I am using a Togglebutton Style from here after Binding the IsChecked Property to my ViewModel the Program crashes with a System.InvalidOperationException Ellipse not found in "System.Windows.Controls.ControlTemplate".
The Style looks like this:
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Viewbox>
<Border x:Name="Border"
CornerRadius="10"
Background="#FFE2E2E2"
Width="40"
Height="20">
<Border.Effect>
<DropShadowEffect ShadowDepth="0.5"
Direction="0"
Opacity="0.3" />
</Border.Effect>
<Ellipse x:Name="Ellipse"
Fill="#FF909090"
Stretch="Uniform"
Margin="-8 -4"
Stroke="Gray"
StrokeThickness="0.2"
HorizontalAlignment="Stretch">
<Ellipse.Effect>
<DropShadowEffect BlurRadius="10"
ShadowDepth="1"
Opacity="0.3"
Direction="260" />
</Ellipse.Effect>
</Ellipse>
</Border>
</Viewbox>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Checked">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="Ellipse"
Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
To="#00facc"
Duration="0:0:0.05"
AccelerationRatio="0.7"
DecelerationRatio="0.3" />
<ThicknessAnimation Storyboard.TargetName="Ellipse"
Storyboard.TargetProperty="Margin"
To="20 -4 -8 -4"
Duration="0:0:0.15"
AccelerationRatio="0.7"
DecelerationRatio="0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Unchecked">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="Ellipse"
Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
To="#00facc"
Duration="0:0:0.05"
AccelerationRatio="0.7"
DecelerationRatio="0.3" />
<ThicknessAnimation
Storyboard.TargetName="Ellipse"
Storyboard.TargetProperty="Margin"
To="-8 -4"
Duration="0:0:0.15"
AccelerationRatio="0.7"
DecelerationRatio="0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And my usage like this:
<ToggleButton Grid.Column="0"
Grid.Row="2"
IsChecked="{Binding IsAutostop}"/>
I'd like to use the style AND be able to use the "IsChecked" Property.
EDIT
Thanks for the comments.
I open the window with a Service bound to a command in a taskbaricon.
The Service:
class WindowService
{
public void OpenOptionsView(MainViewModel mvm)
{
OptionsView optionsView = new OptionsView { DataContext = mvm };
optionsView.Show();
}
}
The important parts of the MainviewModel:
public ICommand OpenOptionsViewCommand { get; private set; }
public bool IsAutostop { get { return Settings.Default.Autostop; } set { Settings.Default.Autostop = value; Settings.Default.Save(); OnPropertyChanged(); } }
public MainViewModel()
{
OpenOptionsViewCommand = new RelayCommand(OpenOptionsView, param => true);
}
private void OpenOptionsView(object o)
{
WindowService ws = new WindowService();
ws.OpenOptionsView(this);
}
I stored my TaskbarIcon in a seperated View:
<tb:TaskbarIcon>
<tb:TaskbarIcon.ContextMenu>
<ContextMenu Background="#1e1e1e">
<MenuItem Header="Programm öffnen"
Foreground="White"
Command="{Binding OpenOptionsViewCommand}"/>
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
EDIT II:
I just found out, that the problem occurs when the bound value is True and the window should be opened with a checked Togglebutton.

Best way to "group" items "virtually"?

I'm working on a custom items based WPF control. For the "normal" layout, think vertical StackPanel, so my xaml would look something like:
<mycontrol>
<item1 />
<item2 />
<item3 />
</mycontrol>
In this case, its simple, 3 item containers are created and all is good. The control will look like:
[item1]
[item2]
[item3]
Use case #2 is, I need to support horizontal "grouping"... ideally my xaml would look like:
<mycontrol>
<item1 />
<stackpanel orientation=horizontal>
<item2a />
<item2b />
<item2c />
</stackpanel>
<item3 />
</mycontrol>
and in this case, I would render as:
[item1]
[item2a] [item2b] [item2c]
[item3]
So, what I'm going for is 5 item containers being generated. I've already worked out a custom layout panel and that part works.
The issue is, if I use a stackpanel, I'll only get 3 item containers which makes sense, duh, but it breaks the keyboard interface of the control. I could do something hacky where I intercept all the keyboard and mouse stuff and "re-route" it in this case, but that seems hacky and difficult to get to work in a generic way.
Is there something obscure built into WPF to deal with this? The "sub items" getting generated as thier own containers?
The way I'm currently heading is to do something like:
<item1 />
<item2a isGrouped=true />
<item2b isGrouped=true />
<item2c isGrouped=true />
<item3 />
So, when I hit the first isGrouped=true, it'll start grouping until it hits a false, but I'm not crazy about that either because I'll have to make isGrouped a 3 state enum so I can have one group right below another group. Also, the hierarchy is not clear in the xaml.
Any suggestions?
I was able to get more or less the look you are going for using a HierarchicalDataTemplate within a TreeView that uses a custom TreeViewItem control template. For the control template, I simply copied the example template and made a few modifications. All of the containers are in the right place, but the keyboard navigation doesn't work on the nested items (because TreeView is not expecting that layout I guess). Here is what I came up with:
<TreeView
ItemsSource="{Binding Items}">
<TreeView.Resources>
<Color x:Key="SelectedBackgroundColor">#FFC5CBF9</Color>
<Color x:Key="SelectedUnfocusedColor">#FFDDDDDD</Color>
<Style
TargetType="{x:Type TreeViewItem}">
<Setter
Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter
Property="IsExpanded"
Value="True" />
<Setter
Property="Template">
<Setter.Value>
<!-- This template came from the example template and has just a few modifications.
Example is at: https://msdn.microsoft.com/en-us/library/ms752048.aspx -->
<ControlTemplate
TargetType="{x:Type TreeViewItem}">
<Grid>
<!-- Changed the grid configuration -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- The entire VisualStateGroups section is a direct copy+paste from the example template -->
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimationUsingKeyFrames
Storyboard.TargetName="Bd"
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame
KeyTime="0"
Value="{StaticResource SelectedBackgroundColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unselected" />
<VisualState x:Name="SelectedInactive">
<Storyboard>
<ColorAnimationUsingKeyFrames
Storyboard.TargetName="Bd"
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame
KeyTime="0"
Value="{StaticResource SelectedUnfocusedColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ExpansionStates">
<VisualState x:Name="Expanded">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="ItemsHost">
<DiscreteObjectKeyFrame
KeyTime="0"
Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Collapsed" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- Removed the ToggleButton -->
<!-- Tweaked the placement of items in the grid -->
<Border
x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ContentPresenter
x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
<ItemsPresenter
x:Name="ItemsHost"
Grid.Column="1" />
</Grid>
<ControlTemplate.Triggers>
<!-- Removed the IsExpanded trigger -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader" Value="false" />
<Condition Property="Width" Value="Auto" />
</MultiTrigger.Conditions>
<Setter
TargetName="PART_Header"
Property="MinWidth"
Value="75" />
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader" Value="false" />
<Condition Property="Height" Value="Auto" />
</MultiTrigger.Conditions>
<Setter
TargetName="PART_Header"
Property="MinHeight"
Value="19" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:MyItem}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
For reference, my ItemsSource property is bound to a collection containing items that look like this:
internal class MyItem
{
public string Name { get; private set; }
public List<MyItem> Children { get; private set; }
public MyItem(string name = null)
{
Name = name;
Children = new List<MyItem>();
}
}
This is done without creating a custom control, but hopefully it gives you an idea of what you can do. The combination of a HeirarchicalDataTemplate and a specialized container type (in my case TreeViewItem with a custom control template) is key to the way I did it.
Here is some data that I tested with and the result:
public IEnumerable<MyItem> Items { get; private set; }
...
var items = new MyItem[]
{
new MyItem("[First]"),
new MyItem(),
new MyItem("[Third]")
};
items[1].Children.Add(new MyItem("[Second_0]"));
items[1].Children.Add(new MyItem("[Second_1]"));
items[1].Children.Add(new MyItem("[Second_2]"));
Items = items;
I am sure the visualization could be improved. I just threw this together in under 10 minutes.

Setting Border background with a template binding

Value="{TemplateBinding HeaderColor}"I've created my own control, and I'm wondering if I can bind a Border.Background to a template property. Currently i'm setting it with a StaticResource like the following:
<Color x:Key="ControlMouseOverColor">green</Color>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="headerLayout">
<EasingColorKeyFrame KeyTime="0:0:6" Value="{StaticResource ControlMouseOverColor}" />
</ColorAnimationUsingKeyFrames>
I'd like it be a property on my control, and be able to set it as a template binding
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="headerLayout">
<EasingColorKeyFrame KeyTime="0:0:6" Value="{TemplateBinding HeaderColor}" />
</ColorAnimationUsingKeyFrames>
MainPage.xaml
<ctrl:Selection Grid.Column="0" HeaderColor="Red" HeaderText="Header Text" />
my class:
public static readonly DependencyProperty HeaderColorProperty =
DependencyProperty.Register("HeaderColor", typeof(System.Windows.Media.Color), typeof(Selection), new PropertyMetadata(System.Windows.Media.Colors.Red));
public System.Windows.Media.Color HeaderColor {
get { return (System.Windows.Media.Color)GetValue(HeaderColorProperty); }
set { SetValue(HeaderColorProperty, value); }
}
This 2nd option does not work, should I be able to do this? I don't get an error, it just does not change to the color I set.
Comment left by AngelWPF asked for more code, pasting below, I'm in the beginning stages of learning to create a control, wanted to note that, because there is a lot I have not got to yet, one piece at a time :)
generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:SelectionControl.Library"
xmlns:ctrl="clr-namespace:SelectionControl.Library;assembly=SelectionControl">
<LinearGradientBrush x:Key="HeaderBackground" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="Gray" Offset="1" />
</LinearGradientBrush>
<Color x:Key="ControlMouseEnterColor">aliceblue</Color>
<Color x:Key="ControlMouseLeaveColor">Gray</Color>
<Color x:Key="ControlLeftMouseUpColor">Red</Color>
<Style TargetType="ctrl:Selection">
<Setter Property="Width" Value="Auto" />
<Setter Property="Height" Value="Auto" />
<Setter Property="FontSize" Value="12" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="AliceBlue" />
<Setter Property="Margin" Value="2,2,2,2" />
<Setter Property="Background" Value="{StaticResource ResourceKey=HeaderBackground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ctrl:Selection">
<Grid x:Name="RootElement" Margin="{TemplateBinding Margin}">
<!-- Visual States -->
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="MouseEnter">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="headerLayout">
<EasingColorKeyFrame KeyTime="0:0:.5" Value="{TemplateBinding HeaderColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseLeave">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="headerLayout">
<EasingColorKeyFrame KeyTime="0:0:1" Value="{StaticResource ControlMouseLeaveColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseLeftUp">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="headerLayout">
<EasingColorKeyFrame KeyTime="0:0:1" Value="{StaticResource ControlLeftMouseUpColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- End Visual States-->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Header -->
<Border x:Name="headerLayout" Background="{TemplateBinding Background}" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="2,2,2,2" BorderBrush="Black" BorderThickness="1">
<StackPanel>
<ToggleButton ></ToggleButton>
<TextBlock Foreground="{TemplateBinding Foreground}" Text="{TemplateBinding HeaderText}" FontWeight="{TemplateBinding FontWeight}" FontSize="{TemplateBinding FontSize}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
</Border>
<!-- Body Content -->
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Selection.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace SelectionControl.Library {
[TemplateVisualState(Name = Selection.MouseEnterStateName, GroupName = Selection.CommonStatesGroupName)]
[TemplateVisualState(Name = Selection.MouseLeaveStateName, GroupName = Selection.CommonStatesGroupName)]
[TemplateVisualState(Name = Selection.MouseLeftUpStateName, GroupName = Selection.CommonStatesGroupName)]
public class Selection : ContentControl {
public const string CommonStatesGroupName = "CommonStates";
public const string MouseEnterStateName = "MouseEnter";
public const string MouseLeaveStateName = "MouseLeave";
public const string MouseLeftUpStateName = "MouseLeftUp";
public Selection() {
this.DefaultStyleKey = typeof(Selection);
this.MouseEnter += new MouseEventHandler(OnMouseEnter);
this.MouseLeave += new MouseEventHandler(OnMouseLeave);
this.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseLeftButtonUp);
}
void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
this.GoToState(Selection.MouseLeftUpStateName, true);
}
void OnMouseLeave(object sender, MouseEventArgs e) {
this.GoToState(Selection.MouseLeaveStateName, true);
}
void OnMouseEnter(object sender, MouseEventArgs e) {
this.GoToState(Selection.MouseEnterStateName, true);
}
private void GoToState(string stateName, bool useTransitions) {
VisualStateManager.GoToState(this, stateName, useTransitions);
}
public static readonly DependencyProperty HeaderTextProperty =
DependencyProperty.Register("HeaderText", typeof(string), typeof(Selection), new PropertyMetadata(""));
public string HeaderText {
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
public static readonly DependencyProperty HeaderColorProperty =
DependencyProperty.Register("HeaderColor", typeof(System.Windows.Media.Color), typeof(Selection), new PropertyMetadata(System.Windows.Media.Colors.Red));
public System.Windows.Media.Color HeaderColor {
get { return (System.Windows.Media.Color)GetValue(HeaderColorProperty); }
set { SetValue(HeaderColorProperty, value); }
}
}}
I have had mixed results using TemplateBinding on custom dependency properties. Because of this, I have used RelativeSource TemplatedParent which seems to work in every situation.
<EasingColorKeyFrame KeyTime="0:0:.5" Value="{Binding HeaderColor, RelativeSource={RelativeSource TemplatedParent}}" />

Silverlight: access control inside the default template of a child control (derived from a templated control)

I am trying to implement the following hierarchy of controls:
RoundedHandleBase:
- this will have a round border around a content presenter that will host a path defined in the template of the child controls.
- it will also provide 2 states: MouseOver and Normal (it displays a rectangle over the path when mouse is over)
public class RoundedHandleBase : ContentControl
{
public RoundedHandleBase()
: base()
{
base.DefaultStyleKey = typeof(RoundedHandleBase);
this.MouseEnter += new MouseEventHandler(RoundedHandleBase_MouseEnter);
this.MouseLeave += new MouseEventHandler(RoundedHandleBase_MouseLeave);
}
void RoundedHandleBase_MouseEnter(object sender, MouseEventArgs e)
{
bool bRet = VisualStateManager.GoToState((Control)sender, "MouseOver", true);
}
void RoundedHandleBase_MouseLeave(object sender, MouseEventArgs e)
{
bool bRet = VisualStateManager.GoToState((Control)sender, "Normal", true);
}
}
FlipHandle:
- this defines a Path as the template (that is the actual icon of the handle):
public class FlipHandle : RoundedHandleBase
{
private bool bFlipPath;
public bool FlipPath
{
get { return bFlipPath; }
set
{
bFlipPath = value;
}
}
public FlipHandle()
: base()
{
base.DefaultStyleKey = typeof(FlipHandle);
bFlipPath = false;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (bFlipPath)
{
ContentPresenter content = this.GetTemplateChild("content") as ContentPresenter;
Path arrowPath = this.GetTemplateChild("Path_FlipHandle") as Path;
}
}
}
generic.xaml
*generic.xaml*
<!--ROUNDED HANDLES -->
<ControlTemplate x:Key="RoundedHandleBaseTemplate" TargetType="local:RoundedHandleBase">
<Viewbox>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangleOver" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.6"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="0.3"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="rectangleOver" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.15"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border CornerRadius="2,2,2,2">
<Grid Height="Auto" Width="Auto">
<Border Margin="0,0,0,0" BorderBrush="#FFFFFFFF" BorderThickness="2,2,2,2" CornerRadius="1,1,1,1" Width="20" Height="20">
<Grid>
<Viewbox Width="14" Height="14">
<ContentPresenter x:Name="content" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="Center" VerticalAlignment="Center"></ContentPresenter>
</Viewbox>
<Rectangle x:Name="rectangleOver" Fill="#FFFFFFFF" RadiusX="2" RadiusY="2" Margin="1,1,1,1" Opacity="0"/>
</Grid>
</Border>
</Grid>
</Border>
</Viewbox>
</ControlTemplate>
<Style TargetType="local:RoundedHandleBase" x:Key="RoundedHandleStyle">
<Setter Property="Template" Value="{StaticResource RoundedHandleBaseTemplate}">
</Setter>
</Style>
<Style TargetType="local:FlipHandle" BasedOn="{StaticResource RoundedHandleStyle}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Path x:Name="Path_FlipHandle" Width="47.5646" Height="49.2324" Canvas.Left="472.184" Canvas.Top="584.534" Stretch="Fill" Fill="#FFFFFFFF" Data="M 494.718,633.76C 508.77,634.067 520.133,622.527 519.738,608.563C 519.534,601.327 516.378,595.502 512.286,591.527C 508.354,587.711 502.225,583.955 493.832,584.608C 482.414,585.496 475.704,591.912 472.184,601.111L 501.638,601.111C 501.668,599.118 501.62,595.698 501.638,593.124C 505.862,596.766 509.822,601.13 513.884,605.191C 514.376,605.684 517.992,608.638 517.965,609.272C 517.962,609.336 516.478,610.758 516.013,611.224C 513.753,613.483 511.846,615.214 509.802,617.258C 506.906,620.154 504.43,623.172 501.638,625.066C 501.618,622.551 501.669,619.192 501.638,617.258L 472.184,617.258C 475.668,626.822 483.53,633.515 494.718,633.76 Z ">
<Path.RenderTransform>
<ScaleTransform CenterX="23.7823" CenterY="24.6162" ScaleX="1" ScaleY="1"></ScaleTransform>
</Path.RenderTransform>
</Path>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<!--/ROUNDED HANDLES -->
My problem is:
- I cannot access the Path in the FlipHandle in order to modify its ScaleTransform in OnApplyTemplate.
Path arrowPath = this.GetTemplateChild("Path_FlipHandle") as Path;
-> arrowPath is allways null.
On the other hand, i can access the "content" content presenter:
ContentPresenter content = this.GetTemplateChild("content") as ContentPresenter;
-> the above works.
Please advice.
Thank you.
You can't access path with GetTemplateChild if it is not part of the template. You could make it part of your template or you could define some visual states on your flip handle and use GoToState method.

TreeView inside AutoCompleteBox's popup

Inspired by this post, I'm trying to use a TreeView inside the AutoCompleteBox's popup in order to show hierarchical data.
Here's what i did:
I Made my own TreeView that implements ISelectionAdapter (not sure it's perfect)
Edit AutoCompleteBox template and change the selector to my treeview
Try Bind my ViewModel to it
Unfortunately this is not working.
i checked my new TreeView with the same binding to the same object and it works prefectly when it's outside of a AutoCompleteBox template.
Any idea? any special way to implement ISelectionAdapter for treeview? did i missed something?
couldn't find any example for it on the web...
Thanks.
I managed to write it eventually.
Made a custom control for it. it still need to be refined, but it works good:
Generic.xaml :
<ControlTemplate x:Key="CommonValidationToolTipTemplate" TargetType="ToolTip">
<Grid x:Name="Root" Margin="5,0" Opacity="0" RenderTransformOrigin="0,0">
<Grid.RenderTransform>
<TranslateTransform x:Name="Translation" X="-25"/>
</Grid.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="OpenStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
<VisualTransition GeneratedDuration="0:0:0.2" To="Open">
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" To="0" Storyboard.TargetProperty="X" Storyboard.TargetName="Translation">
<DoubleAnimation.EasingFunction>
<BackEase Amplitude=".3" EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Duration="0:0:0.2" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Closed">
<Storyboard>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Open">
<Storyboard>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="X" Storyboard.TargetName="Translation"/>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Background="#052A2E31" CornerRadius="5" Margin="4,4,-4,-4"/>
<Border Background="#152A2E31" CornerRadius="4" Margin="3,3,-3,-3"/>
<Border Background="#252A2E31" CornerRadius="3" Margin="2,2,-2,-2"/>
<Border Background="#352A2E31" CornerRadius="2" Margin="1,1,-1,-1"/>
<Border Background="#FFDC000C" CornerRadius="2">
<TextBlock Foreground="White" MaxWidth="250" Margin="8,4,8,4" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}" UseLayoutRounding="false"/>
</Border>
</Grid>
</ControlTemplate>
<Style TargetType="local:AutoCompleteTreeView"
xmlns:System="clr-namespace:System;assembly=mscorlib">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA3AEB9" Offset="0"/>
<GradientStop Color="#FF8399A9" Offset="0.375"/>
<GradientStop Color="#FF718597" Offset="0.375"/>
<GradientStop Color="#FF617584" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="#FFFFFFFF"/>
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="MinWidth" Value="45"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:AutoCompleteTreeView">
<Grid Opacity="{TemplateBinding Opacity}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PopupStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.1" To="PopupOpened"/>
<VisualTransition GeneratedDuration="0:0:0.2" To="PopupClosed"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="PopupOpened">
<Storyboard>
<DoubleAnimation To="1.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PopupBorder"/>
</Storyboard>
</VisualState>
<VisualState x:Name="PopupClosed">
<Storyboard>
<DoubleAnimation To="0.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PopupBorder"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid"/>
<VisualState x:Name="InvalidUnfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="InvalidFocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" Storyboard.TargetName="validationTooltip">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>True</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBox x:Name="Text" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" IsTabStop="True" Margin="0" Padding="{TemplateBinding Padding}" Style="{TemplateBinding TextBoxStyle}"/>
<Border x:Name="ValidationErrorElement" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1" Visibility="Collapsed">
<ToolTipService.ToolTip>
<ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource CommonValidationToolTipTemplate}">
<ToolTip.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="validationTooltip">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Boolean>true</System:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToolTip.Triggers>
</ToolTip>
</ToolTipService.ToolTip>
<Grid Background="Transparent" HorizontalAlignment="Right" Height="12" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12">
<Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C" Margin="1,3,0,0"/>
<Path Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff" Margin="1,3,0,0"/>
</Grid>
</Border>
<Popup x:Name="Popup">
<Grid Opacity="{TemplateBinding Opacity}">
<Border x:Name="PopupBorder" BorderThickness="0" Background="#11000000" HorizontalAlignment="Stretch" Opacity="0">
<Border.RenderTransform>
<TranslateTransform X="1" Y="1"/>
</Border.RenderTransform>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="0" HorizontalAlignment="Stretch" Opacity="1.0" Padding="0">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFDDDDDD" Offset="0"/>
<GradientStop Color="#AADDDDDD" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Border.RenderTransform>
<TransformGroup>
<TranslateTransform X="-1" Y="-1"/>
</TransformGroup>
</Border.RenderTransform>
<local:TreeViewSelectionAdapter x:Name="SelectionAdapter" BorderThickness="0" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" ScrollViewer.HorizontalScrollBarVisibility="Auto" ItemTemplate="{TemplateBinding ItemTemplate}" ItemContainerStyle="{TemplateBinding ItemContainerStyle}" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
</Border>
</Border>
</Grid>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
AutoCompleteTreeView.cs:
public class AutoCompleteTreeView : AutoCompleteBox
{
public AutoCompleteTreeView()
{
DefaultStyleKey = typeof(AutoCompleteTreeView);
}
}
TreeViewSelectionAdapter.cs:
public class TreeViewSelectionAdapter : TreeView, ISelectionAdapter
{
private object lastSelectedItem = null;
/// <summary>
///This prevents the text box of the AutoCompleteBox control from being updated continuously.
/// </summary>
private bool IgnoreAnySelection { get; set; }
/// <summary>
/// An event that indicates that a selection is complete and has been
/// made, effectively a commit action.
/// </summary>
public event RoutedEventHandler Commit;
/// <summary>
/// An event that indicates that the selection operation has been
/// canceled.
/// </summary>
public event RoutedEventHandler Cancel;
/// <summary>
/// Initializes a new instance of the SelectorSelectionAdapter class.
/// </summary>
public TreeViewSelectionAdapter()
{
base.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(OnSelectionChanged);
MouseLeftButtonUp += OnSelectorMouseLeftButtonUp;
}
void OnSelectionChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (IgnoreAnySelection)
{
return;
}
SelectionChangedEventHandler handler = this.SelectionChanged;
if (handler != null)
{
IList oldSelectedItem = new List<object>();
if (lastSelectedItem != null)
oldSelectedItem.Add(lastSelectedItem);
if (SelectionChanged != null)
handler(this, new SelectionChangedEventArgs(oldSelectedItem, new List<object> { this.SelectedItem }));
lastSelectedItem = this.SelectedItem;
}
}
public new object SelectedItem
{
get
{
return base.SelectedItem;
}
set
{
this.SelectItem(value);
}
}
/// <summary>
/// Handles the mouse left button up event on the selector control.
/// </summary>
private void OnSelectorMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
IgnoreAnySelection = false;
OnSelectionChanged(this, null);
OnCommit(this, new RoutedEventArgs());
}
public event SelectionChangedEventHandler SelectionChanged;
/// <summary>
/// Gets or sets the items source.
/// </summary>
public new IEnumerable ItemsSource
{
get { return base.ItemsSource; }
set
{
if (base.ItemsSource != null)
{
INotifyCollectionChanged notify = base.ItemsSource as INotifyCollectionChanged;
if (notify != null)
{
notify.CollectionChanged -= OnCollectionChanged;
}
}
base.ItemsSource = value;
if (base.ItemsSource != null)
{
INotifyCollectionChanged notify = base.ItemsSource as INotifyCollectionChanged;
if (notify != null)
{
notify.CollectionChanged += OnCollectionChanged;
}
}
}
}
/// <summary>
/// Handles the CollectionChanged event, resetting the selection
/// ignore flag.
/// </summary>
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
IgnoreAnySelection = true;
}
/// <summary>
/// Process a key down event.
/// </summary>
public void HandleKeyDown(KeyEventArgs e)
{
switch (e.Key)
{
case Key.Enter:
IgnoreAnySelection = false;
OnCommit(this, e);
e.Handled = true;
break;
case Key.Down:
if ((ModifierKeys.Alt & Keyboard.Modifiers) == ModifierKeys.None)
{
IgnoreAnySelection = true;
//SelectedIndexIncrement();
TreeView tv = this.FindName("SelectionAdapter") as TreeView;
tv.KeyDown += new KeyEventHandler(tv_KeyDown);
tv.Focus();
List<TreeViewItem> ls = new List<TreeViewItem>(tv.GetContainers());
ls[0].Focus();
e.Handled = true;
}
break;
case Key.Escape:
OnCancel(this, e);
e.Handled = true;
break;
default:
break;
}
}
void tv_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Enter:
IgnoreAnySelection = false;
OnSelectionChanged(this, null);
OnCommit(this, new RoutedEventArgs());
break;
}
}
/// <summary>
/// Fires the Commit event.
/// </summary>
private void OnCommit(object sender, RoutedEventArgs e)
{
RoutedEventHandler handler = Commit;
if (handler != null)
{
handler(sender, e);
}
AfterAdapterAction();
}
/// <summary>
/// Fires the Cancel event.
/// </summary>
private void OnCancel(object sender, RoutedEventArgs e)
{
RoutedEventHandler handler = Cancel;
if (handler != null)
{
handler(sender, e);
}
AfterAdapterAction();
}
/// <summary>
/// Change the selection after the actions are complete.
/// </summary>
private void AfterAdapterAction()
{
this.SetSelectedContainer(null);
}
/// <summary>
/// Initializes a new instance of a DataGridAutomationPeer.
/// </summary>
public AutomationPeer CreateAutomationPeer()
{
return new TreeViewAutomationPeer(this);
}
}
Using the control:
<ccontrols:AutoCompleteTreeView x:Name="textbox" MinimumPrefixLength="1" IsEnabled="True" ItemsSource="{Binding MyNodes}" >
<ccontrols:AutoCompleteTreeView.ItemTemplate>
<hirarch:HierarchicalDataTemplate ItemsSource="{Binding MyNodes}" >
<TextBlock Text="{Binding Name}" />
</hirarch:HierarchicalDataTemplate>
</ccontrols:AutoCompleteTreeView.ItemTemplate>
</ccontrols:AutoCompleteTreeView>
The hierarchy objects:
public class Node
{
public string Name { get; set; }
public List<Node> MyNodes { get; set; }
public override string ToString()
{
return Name;
}
}

Resources