Я уже пару раз писал про шаблоны. Вот здесь, о том, как поменять внешний вид кнопки. А вот здесь, как в зависимости от свойств компонента можно подгружать разные шаблоны. Сегодня предлагаю посмотреть натягивание шаблонов на более сложный элемент управления, а именно на TreeView.
Основная задача, которая может возникнуть при определении шаблона в TreeView, это переопределить элементы дерева так, чтобы они показывались по другому. Например, чтобы вместо стандартного значка сворачивания/разворачивания была наша коартинка, или задача убрать отступ у вложенных элементов. Вот такой пример и посмотрим.
Пусть у нас стоит задача, реализовать дерево для показа прогресса закачки файлов по разнвм типам: видео, музыка, программы, разное. Это будут ноды корня. Ну а дочерние ноды, будут конкретными файлами. На динамике и красивостях особо заморачиваться не буду. Поэтому для хранения дерева элементов буду использовать вот такой класс:
public string Name { get; set; }
public double Progress { get; set; }
public List<DownloadItem> Children { get; set; }
}
Для генерации тестовых данных добавлю вот такой метод:
Добавив в проект две картинки для отображения значка развернцтого и свернутого списка дочерних элементов, начинаем работать с шаблоном. Для этого у TreeView переопеределим ItemContainerStyle.
Для того, чтобы его сворачивать и разворачивать, необходимо в зависимости от состояния переключать его Visibility в Visible и и обратно в Collapsed. Правда в ToggleButton у нас это не получится, т.к. в визуальном дереве она лежит с ItemsPresenter на одном уровне. Но не беда, воспользуемся состояниями нашего TreeViewitem:
Теперь это выглядит вот так:
Практически то что надо, осталось только убрать иконку сворачивания-разворачивания, для файлов. Для этого добавим обработчик состояния NoItems нашего TreeView:
Вроде хорошо получилось.
Основная задача, которая может возникнуть при определении шаблона в TreeView, это переопределить элементы дерева так, чтобы они показывались по другому. Например, чтобы вместо стандартного значка сворачивания/разворачивания была наша коартинка, или задача убрать отступ у вложенных элементов. Вот такой пример и посмотрим.
Пусть у нас стоит задача, реализовать дерево для показа прогресса закачки файлов по разнвм типам: видео, музыка, программы, разное. Это будут ноды корня. Ну а дочерние ноды, будут конкретными файлами. На динамике и красивостях особо заморачиваться не буду. Поэтому для хранения дерева элементов буду использовать вот такой класс:
public class DownloadItem
{public string Name { get; set; }
public double Progress { get; set; }
public List<DownloadItem> Children { get; set; }
}
Для генерации тестовых данных добавлю вот такой метод:
public static IEnumerable<DownloadItem> GetTestData() { List<DownloadItem> result = new List<DownloadItem>(); result.Add( new DownloadItem() { Name = "Видео", Progress = 30, Children = new List<DownloadItem>( new DownloadItem[] { new DownloadItem() { Name = "Фильм.avi", Progress = 20 }, new DownloadItem() { Name = "Очень интересный фильм.mpeg", Progress = 60 }, new DownloadItem() { Name = "Ну ниче так фильм.mp4", Progress = 10 } }) } ); result.Add( new DownloadItem() { Name = "Музыка", Progress = 80, Children = new List<DownloadItem>( new DownloadItem[] { new DownloadItem() { Name = "Прикольная песня.mp3", Progress = 70 }, new DownloadItem() { Name = "Еще одна прикольная песня.mp3", Progress = 85 }, new DownloadItem() { Name = "Песня из мультика.mp3", Progress = 85 } }) } ); result.Add( new DownloadItem() { Name = "Программы", Progress = 30, Children = new List<DownloadItem>( new DownloadItem[] { new DownloadItem() { Name = "Visual Studio 2013 preview.iso", Progress = 30 } }) } ); return result; }Для удобаства, добавим DataTemplate, для показа имени и прогресса:
<HierarchicalDataTemplate x:Key="DownloadTemplate" ItemsSource="{Binding Children}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}" Margin="5" /> <ProgressBar Value="{Binding Progress}" Width="100" Margin="0,5,0,5" /> </StackPanel> </HierarchicalDataTemplate>Ну и собственно TreeView. Кидаем его на форму и при загрузке прописываем инициализацию.
<TreeView ItemTemplate="{StaticResource DownloadTemplate}" x:Name="tvMain" />
void MainWindow_Loaded(object sender, RoutedEventArgs e) { tvMain.ItemsSource = DownloadItem.GetTestData(); }После запуска, это все выглядит вот так:
Добавив в проект две картинки для отображения значка развернцтого и свернутого списка дочерних элементов, начинаем работать с шаблоном. Для этого у TreeView переопеределим ItemContainerStyle.
<TreeView.ItemContainerStyle> <Style TargetType="TreeViewItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TreeViewItem"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition MinWidth="19" Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <ToggleButton IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" x:Name="ToogleButton"/> <ToggleButton.Template> <ControlTemplate TargetType="ToggleButton"> <Grid Background="Transparent"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CheckStates"> <VisualState x:Name="Unchecked" /> <VisualState x:Name="Checked"> <Storyboard> <DoubleAnimation Storyboard.TargetName="UncheckedVisual" Storyboard.TargetProperty="Opacity" To="0" Duration="0" /> <DoubleAnimation Storyboard.TargetName="CheckedVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Image Source="expand.png" x:Name="UncheckedVisual" Height="30" Margin="3" /> <Image Source="collapse.png" x:Name="CheckedVisual" Height="30" Opacity="0" Margin="3" /> </Grid> </ControlTemplate> </ToggleButton.Template> </ToggleButton> <ContentControl Content="{TemplateBinding Header}" ContentTemplate="{StaticResource DownloadTemplate}" Grid.Column="1" /> <ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Visibility="Collapsed" Grid.ColumnSpan="100" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </TreeView.ItemContainerStyle>На что следует обратить внимание. Во-первых, мы здесь используем состояния нашей ToggleButton. Все состояния можно посмотреть здесь. По умолчанию, показывается непрозрачной одна картинка, при изменении состояния - другая. Во-вторых, свойство IsChecked мы привязываем к родительскому шаблону, т.е. состояние нашей кнопки, это состояние свернуто-развернуто нашего элемента дерева. В-третьих, я задал для ItemsPresenter-а с дочерними элементами состояние Collapsed, т.е. по умолчанию их не видно. Изменения состояния пока не определено, поэтому если запустить приложение и потыкать в картинки, то меняться они будут, а вот дочерних элементов мы не увидим:
Для того, чтобы его сворачивать и разворачивать, необходимо в зависимости от состояния переключать его Visibility в Visible и и обратно в Collapsed. Правда в ToggleButton у нас это не получится, т.к. в визуальном дереве она лежит с ItemsPresenter на одном уровне. Но не беда, воспользуемся состояниями нашего TreeViewitem:
<Style TargetType="TreeViewItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TreeViewItem"> <Grid> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="ExpansionStates"> <VisualState x:Name="Collapsed" /> <VisualState x:Name="Expanded"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ItemsHost" Storyboard.TargetProperty="(UIElement.Visibility)" Duration="0"> <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
Теперь это выглядит вот так:
Практически то что надо, осталось только убрать иконку сворачивания-разворачивания, для файлов. Для этого добавим обработчик состояния NoItems нашего TreeView:
<VisualState x:Name="NoItems"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ToogleButton" Storyboard.TargetProperty="(UIElement.Visibility)" Duration="0"> <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState>Смотрим:
Вроде хорошо получилось.
Комментариев нет:
Отправить комментарий