WPF Treeview с горизонтально завернутыми элементами листа
Новичок в WPF Я пытаюсь справиться с TreeView, и я невероятно впечатлен гибкостью, которую он предлагает.
До сих пор в каждом из моих элементов в виде дерева реализован Expander, который является отличным способом отображения более подробной информации о каждом элементе, а в заголовке отображается только сводная информация. Однако это не подходит для конечных элементов и приводит к расточительству места на экране - каждый конечный элемент (которых много) в моем древовидном представлении показывает сравнительно небольшой объем данных.
То, что я хочу реализовать для конечных элементов, это горизонтальная переноска, а не вертикальная распечатка. Представьте, что отображается сетка или (стековая панель), и каждый элемент отображается в своей собственной ячейке / области, переносясь в строку ниже, в зависимости от доступного горизонтального пространства.
например
Level 1
Level 2
Level 3
Leaf 1 | leaf 2 | leaf 3 | leaf 4
Leaf 5 | leaf 6 | leaf 7 | leaf 8
Leaf 9 .....
Level 1
Level 2
Level 3
Leaf 1 | leaf 2| ....
Я искал целую вечность для этого - я читал о TreeGrid (это то, что я уже независимо реализовал), а также отличную http://www.codeproject.com/Articles/17025/Custom-TreeView-Layout-in-WPF Пример в WPF. Это идет как-то, чтобы выполнить то, что я хочу, но не реализует упаковку элементов.
Мой опыт сильно зависит от WinForms, и я ограничен в своем отсутствии опыта WPF (мне нравится то, что я изучил до сих пор). Это то, что я хочу сделать, выполнимо?
Я не спрашиваю о шаблонном решении "вырезать и вставить", просто некоторые указатели / ресурсы / мнения, которые я могу исследовать.
Кстати, из-за природы моей компании, любое решение должно быть бесплатным.
Спасибо.
Вот xaml для TreeView:
<TreeView Name="tvMonitoredAlarms" Margin="10,10,10,10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Auto" >
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Expanded}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<!-- *****************************************************************************************************************
Server TreeView item
***************************************************************************************************************** -->
<HierarchicalDataTemplate DataType="{x:Type PAM:MonitoredServer}" ItemsSource="{Binding PLCs}">
<Border Margin="0" BorderBrush="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}"
Background="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=OPCAlarmTriggered}" Value="True">
<Setter Property="BorderThickness" Value="5"/>
<Setter Property="CornerRadius" Value="3,3,3,3"/>
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="FlashBorderSERVER" Storyboard="{StaticResource FlashBorder}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="FlashBorderSERVER"/>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=OPCAlarmTriggered}" Value="False">
<Setter Property="BorderThickness" Value="1"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Expander Template="{StaticResource RevealExpanderTemp}"
OverridesDefaultStyle="True"
Header="{Binding ServerName}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
>
<Grid Margin="0,0,0,0" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Margin="4,0,4,0" Grid.Row="0" Grid.Column="0">PLCs</Label>
<Label Margin="4,0,4,0" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding NumberOfPLCs}" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" />
<Label Margin="4,0,4,0" Grid.Row="1" Grid.Column="0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}">Monitored Alarms</Label>
<Label Margin="4,0,4,0" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding NumberOfMonitoredAlarms}" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" />
<Label Margin="4,0,4,0" Grid.Row="2" Grid.Column="0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}">Address</Label>
<Label Margin="4,0,4,0" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding IPAddress}" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}"/>
<Label Margin="4,0,4,0" Grid.Row="3" Grid.Column="0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}">Comment</Label>
<Label Margin="4,0,4,0" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding Comment}" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}"/>
</Grid>
</Expander>
</Border>
</HierarchicalDataTemplate>
<!-- *****************************************************************************************************************
PLC TreeView item
***************************************************************************************************************** -->
<HierarchicalDataTemplate DataType="{x:Type PAM:MonitoredPLC}" ItemsSource="{Binding Areas}">
<Border Margin="0" BorderBrush="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}"
Background="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}"
>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=OPCAlarmTriggered}" Value="True">
<Setter Property="BorderThickness" Value="5"/>
<Setter Property="CornerRadius" Value="3,3,3,3"/>
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="FlashBorderPLC" Storyboard="{StaticResource FlashBorder}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="FlashBorderPLC"/>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=OPCAlarmTriggered}" Value="False">
<Setter Property="BorderThickness" Value="1"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Expander Template="{StaticResource RevealExpanderTemp}"
OverridesDefaultStyle="True"
Header="{Binding Name}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
>
<Expander.Background>
<LinearGradientBrush ColorInterpolationMode="ScRgbLinearInterpolation" StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Color="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}" Offset="0"/>
<GradientStop Color="DarkKhaki" Offset="0.75"/>
</LinearGradientBrush>
</Expander.Background>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="0" Grid.Column="0">Areas</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding NumberOfAreas}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="1" Grid.Column="0">Monitored Alarms</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding NumberOfMonitoredAlarms}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="2" Grid.Column="0">Device</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding Device}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="3" Grid.Column="0">Model</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding Model}" />
</Grid>
</Expander>
</Border>
</HierarchicalDataTemplate>
<!-- *****************************************************************************************************************
Area TreeView item
***************************************************************************************************************** -->
<HierarchicalDataTemplate DataType="{x:Type PAM:MonitoredArea}" ItemsSource="{Binding Alarms}">
<Border Margin="0" BorderBrush="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}"
Background="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=OPCAlarmTriggered}" Value="True">
<Setter Property="BorderThickness" Value="5"/>
<Setter Property="CornerRadius" Value="3,3,3,3"/>
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="FlashBorderAREA" Storyboard="{StaticResource FlashBorder}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="FlashBorderAREA"/>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=OPCAlarmTriggered}" Value="False">
<Setter Property="BorderThickness" Value="1"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Expander Template="{StaticResource RevealExpanderTemp}"
OverridesDefaultStyle="True"
Header="{Binding OPCArea.DBArea.fldDescription}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="0" Grid.Column="0">Monitored Alarms</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding NumberOfMonitoredAlarms}" />
</Grid>
</Expander>
</Border>
</HierarchicalDataTemplate>
<!-- *****************************************************************************************************************
Alarm TreeView item
***************************************************************************************************************** -->
<DataTemplate DataType="{x:Type PAM:MonitoredAlarm}">
<Border Name="AlarmBorder"
BorderBrush="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}"
Background="{Binding AlarmPriority.BackColour, Converter={StaticResource PriorityBrush}}"
>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=OPCAlarmTriggered}" Value="True">
<Setter Property="BorderThickness" Value="5"/>
<Setter Property="CornerRadius" Value="3,3,3,3"/>
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="FlashBorder" Storyboard="{StaticResource FlashBorder}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="FlashBorder"/>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding Path=OPCAlarmTriggered}" Value="False">
<Setter Property="BorderThickness" Value="1"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Expander Template="{StaticResource RevealExpanderTemp}"
OverridesDefaultStyle="True"
Header="{Binding Description}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="400"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="0" Grid.Column="0">Current Value</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding OPCAlarm.OPCValue}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="1" Grid.Column="0">Priority</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding AlarmPriority.DBPriority.fldName}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding TagName}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="4" Grid.Column="0">Address</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding Address}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="5" Grid.Column="0">Scan rate</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="5" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding ScanRate}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding LastActive}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="3" Grid.Column="0">Activity</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding Activity}" />
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="4" Grid.Column="0">Comment</Label>
<Label Margin="4,0,4,0" Foreground="{Binding AlarmPriority.ForeColour, Converter={StaticResource PriorityBrush}}" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Right" Content="{Binding DBMonitoredAlarm.fldComment}" />
</Grid>
</Expander>
</Border>
</DataTemplate>
</TreeView.Resources>
</TreeView>
1 ответ
Хорошо, я решил эту проблему, реструктурировав данные, отображаемые в триодах, в набор "строк", где каждая строка может вместить до 10 значений. Затем эта коллекция связывается с сеткой данных (которая может иметь до 10 столбцов), которая отображается в шаблоне данных в древовидном представлении.
Не совсем идеально, но это соответствует моим потребностям.