I have an ItemsControl which binds two ObservableCollections called NetworkMain.Conduits and NetworkMain.Nodes as lines and ellipses.
The following XAML is working well, but there are a couple of issues I want to address.
<ItemsControl x:Name="MainPanel">
<ItemsControl.Resources>
<CollectionViewSource x:Key="ConduitLines" Source="{Binding Path=NetworkMain.Conduits}"></CollectionViewSource>
<CollectionViewSource x:Key="NodeCircles" Source="{Binding Path=NetworkMain.Nodes}"></CollectionViewSource>
<DataTemplate DataType="{x:Type cad:Conduits}">
<Line Stroke="Yellow"
StrokeThickness="2"
X1="{Binding X1, Mode=OneWay, Converter={StaticResource XcoordinatesCoverter}}"
X2="{Binding X2, Mode=OneWay, Converter={StaticResource XcoordinatesCoverter}}"
Y1="{Binding Y1, Mode=OneWay, Converter={StaticResource YcoordinatesCoverter}}"
Y2="{Binding Y2, Mode=OneWay, Converter={StaticResource YcoordinatesCoverter}}">
</Line>
</DataTemplate>
<DataTemplate DataType="{x:Type cad:Nodes}">
<Ellipse
Width="20"
Height="20"
Stroke="Blue"
Fill="Blue"
StrokeThickness="4">
</Ellipse>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource ConduitLines}}"></CollectionContainer>
<CollectionContainer Collection="{Binding Source={StaticResource NodeCircles}}"></CollectionContainer>
</CompositeCollection>
</ItemsControl.ItemsSource>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas
MinHeight="800"
Background="Black"
ClipToBounds="true">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter
Property="Canvas.Left" Value="{Binding Path=X, Mode=OneWay, Converter={StaticResource XcoordinatesCoverter}, ConverterParameter=20}" />
<Setter
Property="Canvas.Top" Value="{Binding Path=Y, Mode=OneWay, Converter={StaticResource YcoordinatesCoverter}, ConverterParameter=20}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
I don't really understand the <ItemsControl.ItemContainerStyle> section which binds the Ellipse coordinates. Unfortunately it fires for both the conduits and nodes, which means I get a load of binding errors as conduits don't contain variables X and Y, as they use X1, X2, Y1, Y2.
I want to remove <ItemsControl.ItemContainerStyle> and update my Ellipse DataTemplate to this.
<ItemsControl.Resources>
<CollectionViewSource x:Key="ConduitLines" Source="{Binding Path=NetworkMain.Conduits}"></CollectionViewSource>
<CollectionViewSource x:Key="NodeCircles" Source="{Binding Path=NetworkMain.Nodes}"></CollectionViewSource>
<DataTemplate DataType="{x:Type cad:Conduits}">
<!-- Conduits code removed from clarity -->
</DataTemplate>
<DataTemplate DataType="{x:Type cad:Nodes}">
<Ellipse
Width="10"
Height="10"
Stroke="Blue"
StrokeThickness="4"
Canvas.Left ="{Binding Path=X, Mode=OneWay, Converter={StaticResource XcoordinatesCoverter}}"
Canvas.Top ="{Binding Path=Y, Mode=OneWay, Converter={StaticResource YcoordinatesCoverter}}">
</Ellipse>
</DataTemplate>
</ItemsControl.Resources>
When I do this, all the Nodes bind with coordinates of 0,0.
I don't get any binding errors.
I'm surprised this doesn't work. Any ideas why not?
Related
in my current Project I have a nested data-structure. The root is an ObservableCollection ( terrainModel.terrainElements). Each of the elements in the collection host another ObservableCollection(drawElements) that consists of data to draw primitives in a canvas. Depending on the primitive I provide a DataTemplate so that it is rendered in the Canvas.
Here is the actual XAML:
<ItemsControl ItemsSource="{Binding terrainModel.terrainElements}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1000" Height="500" Background="Aquamarine"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding drawElements}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type drawtype:LineElement}">
<Line Stroke="Black" X1="{Binding startPoint.X}" Y1="{Binding startPoint.Y}" X2="{Binding endPoint.X}" Y2="{Binding endPoint.Y}" />
</DataTemplate>
<DataTemplate DataType="{x:Type drawtype:CircleElement}">
<Ellipse Stroke="Black" Width="{Binding radius}" Height="{Binding radius}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type drawtype:RectangleElement}">
<Rectangle Stroke="Blue" Width="{Binding width}" Height="{Binding height}" Canvas.Left="{Binding position.X}" Canvas.Top="{Binding position.Y}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The problem is however that I am not able to set the position of the primitives correct in the XAML, as Canvas.Left="{Binding position.X}" for example does not work as it is in the inner ItemsControl and not in the outer ItemsControl.
I also tried to transform the primitives like this:
<Rectangle.RenderTransform><TranslateTransform x:Name="myTransform2" X="{Binding position.X}" Y="{Binding position.Y}" /></Rectangle.RenderTransform>
This worked but ruined the position of following elements to draw. Of course I could draw everything in code of the view, but I would like to know if it's also possible to do in xaml.
The solution by Clemens worked:
Set a Canvas as the ItemsPanel for the inner ItemsControl the same way
you did for the outer one. Then use the RenderTransform solution, as
setting Canvas.Left and Canvas.Top would still not work because the
Rectangle will not become a direct child of the Canvas. You would need
to set an ItemsContainerStyle for the Left and Top bindings.
The outer Canvas should still (also) have a Canvas as its ItemsPanel.
Otherwise you will have problems with more than one terrainElement.
I thought that a new Canvas would be generated in the inner ItemsControl for every outer ItemsControl.
Here is the XAML if somebody is interested:
<ItemsControl ItemsSource="{Binding terrainModel.terrainElements}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding drawElements}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1000" Height="500" Background="Aquamarine"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type drawtype:LineElement}">
<Line Stroke="Black" X1="{Binding startPoint.X}" Y1="{Binding startPoint.Y}" X2="{Binding endPoint.X}" Y2="{Binding endPoint.Y}" />
</DataTemplate>
<DataTemplate DataType="{x:Type drawtype:CircleElement}">
<Ellipse Stroke="Black" Width="{Binding radius}" Height="{Binding radius}">
<Ellipse.RenderTransform>
<TranslateTransform x:Name="myTransform" X="{Binding position.X}" Y="{Binding position.Y}" />
</Ellipse.RenderTransform>
</Ellipse>
</DataTemplate>
<DataTemplate DataType="{x:Type drawtype:RectangleElement}">
<Rectangle Stroke="Blue" Width="{Binding width}" Height="{Binding height}">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="myTransform2" X="{Binding position.X}" Y="{Binding position.Y}" />
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I'm working on a WPF project that has a ListBox bound to a collection. This ListBox shows the elements in a canvas since these elements are ellipses and lines.
I am developing an application where I can draw entities (ellipses) and connectors (lines). That's already working.
I also want to be able to select either a ellipse or a line which I can already do it, since this capability is provided by the ListBox. However, when I select a line, the selected line looks like an ugly big blue square!
Is there a way to select only the pure line?
Here is how my current ListBox implementation looks like:
<ListBox ItemsSource="{Binding Elements}" >
<ItemsControl.ItemContainerStyle>
<Style>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
</Style.Resources>
<Setter Property="Canvas.Left" Value="{Binding CanvasLeft}" />
<Setter Property="Canvas.Top" Value="{Binding CanvasTop}" />
<Setter Property="Panel.ZIndex" Value="{Binding ZIndex, Mode=TwoWay}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type vm:EntityViewModel}" >
<Viewbox>
<Grid>
<Ellipse myb:DraggableBehavior.IsDraggingEnabled="True"
Stroke="Black"
Width="{Binding ShapeWidth, Mode=TwoWay}"
Height="{Binding ShapeHeight, Mode=TwoWay}"
Canvas.Left="{Binding X1, Mode=TwoWay}"
Canvas.Top="{Binding Y1, Mode=TwoWay}"
Fill="{Binding Background}"
Panel.ZIndex="{Binding ZIndex, Mode=TwoWay}" >
</Ellipse>
<TextBlock Text="{Binding StatePropertiesViewModel.Name}"
HorizontalAlignment="Center"
TextAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Viewbox>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:LineViewModel}" >
<Line X1="{Binding X1, Mode=TwoWay}"
Y1="{Binding Y1, Mode=TwoWay}"
X2="{Binding X2, Mode=TwoWay}"
Y2="{Binding Y2, Mode=TwoWay}"
Stroke="{Binding Color, Mode=TwoWay}"
StrokeThickness="{Binding Thickness, Mode=TwoWay}"
Panel.ZIndex="{Binding ZIndex, Mode=TwoWay}"
/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
Update
After following #AbhinavSharma's advice:
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
</Style.Resources>
It seemed that the blue square selection area is not visible now, it looks better. However, it is still there because, for example, if I have a Line shape over an Ellipse, I cannot select the ellipse since the invisible selected Line area is still there. i.e. what I get selected when clicking on it is the line, not the ellipse. So, is there a way to reduce the selection area just to the area of the shape itself?
Thanks in advance.
I'm having difficulties in accessing the controls that are in each node of a TreeView. I can access the tree view nodes, but not the controls in them (the expander button and it's messages).The TreeView is bound to a ValidationResult array (the code is below).
I uploaded a picture of the control here: http://s30.postimg.org/j2vszwc01/Results_form.jpg
The border highlight is from the Coded UI Test Builder. It doesn't see anything that is below the TreeViewItem node level.
The results viewer control is defined like this:
<UserControl x:Class="ResultsForm.ValidationResultsViewerUI"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ResultsForm"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="400">
<UserControl.Resources>
<local:TreeViewDimensionConvertor x:Key="treeViewWidthConvertor"/>
<DataTemplate x:Key="errorTemplate">
<Grid Name="resultsGrid" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeView}}, Path=ActualWidth, Converter={StaticResource treeViewWidthConvertor}}">
<Grid.Resources>
<Style TargetType="Border" x:Key="errorBorderStyle" >
<Style.Resources>
<LinearGradientBrush x:Key="errorBackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#EF3132" Offset="0.1" />
<GradientStop Color="#D62B2B" Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource errorBackBrush}"/>
</Style>
</Grid.Resources>
<Expander Name="expanderTest">
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Border Style="{StaticResource errorBorderStyle}" Grid.Column="0">
<TextBlock Text="{Binding Message}"
FontSize="12"
TextWrapping="Wrap"
Foreground="White">
</TextBlock>
</Border>
<TextBlock Grid.Column="2" HorizontalAlignment="Left">
<Hyperlink Tag="{Binding UrlSuffix}">Details</Hyperlink>
</TextBlock>
</Grid>
</Expander.Header>
<TextBox Text="{Binding Description}"
AcceptsReturn="True"
IsReadOnly="True"
TextWrapping="Wrap"
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeView}}, Path=ActualWidth, Converter={StaticResource treeViewWidthConvertor}}">
</TextBox>
</Expander>
</Grid>
</DataTemplate>
<DataTemplate x:Key="warningTemplate">
<Grid Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeView}}, Path=ActualWidth, Converter={StaticResource treeViewWidthConvertor}}">
<Grid.Resources>
<Style TargetType="Border" x:Key="warningBorderStyle" >
<Style.Resources>
<LinearGradientBrush x:Key="warningBackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FFFDE203" Offset="0.1" />
<GradientStop Color="#FFFDDC00" Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource warningBackBrush}"/>
</Style>
</Grid.Resources>
<Expander Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2">
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Style="{StaticResource warningBorderStyle}">
<TextBlock Text="{Binding Message}" FontSize="12" TextWrapping="Wrap" Foreground="Black" />
</Border>
<TextBlock Grid.Column="2" HorizontalAlignment="Left">
<Hyperlink Tag="{Binding UrlSuffix}">Details</Hyperlink>
</TextBlock>
</Grid>
</Expander.Header>
<TextBox Text="{Binding Description}"
AcceptsReturn="True"
IsReadOnly="True"
TextWrapping="Wrap"
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeView}}, Path=ActualWidth, Converter={StaticResource treeViewWidthConvertor}}" />
</Expander>
</Grid>
</DataTemplate>
<local:TemplateSelector x:Key="warningOrErrorTemplateSelector"
ErrorTemplate="{StaticResource errorTemplate}"
WarningTemplate="{StaticResource warningTemplate}"/>
<DataTemplate x:Key="childTemplate">
<TreeViewItem Header="child"
ItemTemplateSelector="{StaticResource warningOrErrorTemplateSelector}"
IsExpanded="True"
Focusable="False"
IsSelected="False">
<TreeViewItem.Resources>
<CollectionViewSource x:Key="childErrors" Source="{Binding Errors}"/>
<CollectionViewSource x:Key="childWarnings" Source="{Binding Warnings}"/>
</TreeViewItem.Resources>
<TreeViewItem.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource childErrors}}"/>
<CollectionContainer Collection="{Binding Source={StaticResource childWarnings}}"/>
</CompositeCollection>
</TreeViewItem.ItemsSource>
</TreeViewItem>
</DataTemplate>
<local:TemplateSelector x:Key="templateSelector"
ErrorTemplate="{StaticResource errorTemplate}"
WarningTemplate="{StaticResource warningTemplate}"
VerificationResultTemplate="{StaticResource childTemplate}"/>
<DataTemplate x:Key="parentTemplate">
<TreeViewItem Header="{Binding Description}"
ItemTemplateSelector="{StaticResource templateSelector}"
IsExpanded="True"
Focusable="False"
IsSelected="False">
<TreeViewItem.Resources>
<CollectionViewSource x:Key="errors" Source="{Binding Errors}"/>
<CollectionViewSource x:Key="warnings" Source="{Binding Warnings}"/>
<CollectionViewSource x:Key="children" Source="{Binding ChildrenResults}"/>
</TreeViewItem.Resources>
<TreeViewItem.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource errors}}"/>
<CollectionContainer Collection="{Binding Source={StaticResource warnings}}"/>
<CollectionContainer Collection="{Binding Source={StaticResource children}}"/>
</CompositeCollection>
</TreeViewItem.ItemsSource>
</TreeViewItem>
</DataTemplate>
</UserControl.Resources>
<Grid>
<Border>
<TreeView Name="resultTree" ItemTemplate="{StaticResource parentTemplate}" Focusable="False" ItemsSource="{Binding}">
<TreeView.Resources>
<SolidColorBrush Color="White" x:Key="{x:Static SystemColors.HighlightBrushKey}"/>
</TreeView.Resources>
</TreeView>
</Border>
</Grid>
</UserControl>
The validation result class is defined like this (VerificationError and VerificationWarning each contain a Description and Message string fields):
[DataContract]
public class ValidationResult
{
[DataMember(Order = 1)]
public VerificationError[] Errors { get; set; }
[DataMember(Order = 2)]
public VerificationWarning[] Warnings { get; set; }
[DataMember(Order = 3)]
public virtual string Description { get; set; }
}
What do I need to do in order to access the messages in the TreeView?
Edit: I have tried accessing the items programatically and settings the AutomationId of each TreeViewItem, but neither approach worked. I have also created a similar hierarchy without data binding the tree view and I didn;t have any problems in accessing the controls. I'm guessing the problem is with the data binding (http://blogs.msdn.com/b/tapas_sahoos_blog/archive/2011/12/13/verifying-wpf-data-bound-item-controls-in-coded-ui-test-recording-context.aspx), but I don't know how to correct it.
Have you tried to access the tree programmatically? Eg:
UITestControl myControl = treeObjectPath;
foreach (UITestControl x in myControl.GetChildren())
{
DoSomething...
}
Sometimes objects cannot be captured using the Coded UI Test Spy.
I have a ListView which layout looks like a Windows Explorer view (icon + some details), bound to a list somewhere in the ViewModel.
My aim here is to be able to switch between explorer view or classic view whenever we want.
I could define an ItemsPanelTemplate doing exactly the work to display correctly the layout, directly in the ListView.ItemsPanel field. Now, I'd like to define it in the resources so that I'll be able to use it in different views, and especially in one control, the user should have the choice between Explorer view or classic list view (the default rendering for a list)
How'd you do that? I cannot define any ItemsPanelTemplate in my ResourceDictionary, and if I define a DataTemplate it is not compatible (while I thought that following pure logic, ItemsPanelTemplate should inherit from DataTemplate, but it actually doesn't look like so).
Code snippet for the actual list:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
Width="{Binding (FrameworkElement.ActualWidth),
RelativeSource={RelativeSource
AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth,
RelativeSource={RelativeSource AncestorType=ListView}}"
ItemHeight="{Binding (ListView.View).ItemHeight,
RelativeSource={RelativeSource AncestorType=ListView}}"
/>
<!--
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
-->
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal"
Height="Auto"
Width="150" >
<Image
Source="{Binding Path=Appli.AppType, Converter={StaticResource TypeToIconConverter}}"
Margin="5"
Height="50"
Width="50" />
<StackPanel
VerticalAlignment="Center"
Width="90" >
<TextBlock
Text="{Binding Path=Appli.AppName}"
FontSize="13"
HorizontalAlignment="Left"
TextWrapping="WrapWithOverflow"
Margin="0,0,0,1" />
<TextBlock
Text="{Binding Path=Appli.AppType}"
FontSize="9"
HorizontalAlignment="Left"
Margin="0,0,0,1" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Keeping the ItemTemplate in a static resource was easy to do, but now I can't do anything with the ItemsPanelTemplate...
Any ideas? I'm using MVVM so I'm trying ideally not to use code-behind if possible
You would use a style for the whole ListView for that. So you would do:
<Grid.Resources>
<Style x:Key="ListViewStyle" TargetType="ListView">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel
Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}" />
<!--
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
-->
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ListView
SelectionMode="Single"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Bottom"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding ListUserApps, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding SelectedUserApp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Background="White"
Style="{StaticResource ListViewStyle}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal"
Height="Auto"
Width="150">
<Image
Source="{Binding Path=Appli.AppType, Converter={StaticResource TypeToIconConverter}}"
Margin="5"
Height="50"
Width="50"/>
<StackPanel
VerticalAlignment="Center"
Width="90">
<TextBlock
Text="{Binding Path=Appli.AppName}"
FontSize="13"
HorizontalAlignment="Left"
TextWrapping="WrapWithOverflow"
Margin="0,0,0,1" />
<TextBlock
Text="{Binding Path=Appli.AppType}"
FontSize="9"
HorizontalAlignment="Left"
Margin="0,0,0,1" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want the user then be able to switch between explorer and classic view, just define a second Style and switch the style of the listview. This can be done for example with some VisualStates and a 'DataStateBehavior'.
Alternatively you could create a style with some DataTriggers and Setters for the individual ItemsPanels.
Okay, here is my pretty simple problem.
I have a ListView that I styled so as to make it look like the Windows Explorer.
Now, I'd like to group the items inside. Therefore, I defined a GroupStyle with an Expander to group it. The grouping is now fine.
What I don't like is that now, my ListView displays each group on a separate line, while I'd like to have some expander-wrapping in order to display many groups on the same line.
An image is better than some text I guess.
Here is what I have:
Here is what I want:
I cannot find which property should I have to style in order to make the GroupItems fit in a WrapPanel, just like I did for the items.
Here is my ListView style:
<ResourceDictionary>
<!-- Explorer-style layout-->
<DataTemplate x:Key="ExplorerView">
<StackPanel Orientation="Horizontal" Height="Auto" Width="150">
<Image Source="{Binding Path=Value.AppConfig.Appli.AppType, Converter={StaticResource TypeToIconConverter}}" Margin="5"
Height="50" Width="50"/>
<StackPanel VerticalAlignment="Center" Width="90">
<TextBlock Text="{Binding Path=Value.AppConfig.Appli.AppName}"
FontSize="13" HorizontalAlignment="Left" TextWrapping="WrapWithOverflow"
Margin="0,0,0,1" />
<TextBlock Text="{Binding Path=Value.AppConfig.Appli.AppType}" FontSize="9"
HorizontalAlignment="Left" Margin="0,0,0,1" />
</StackPanel>
</StackPanel>
</DataTemplate>
<!-- Group header style-->
<Style x:Key="GroupHeaderStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp" IsExpanded="True" Width="310"
BorderBrush="CornflowerBlue">
<Expander.Header>
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="CornflowerBlue" x:Name="expContent"
Width="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType={x:Type Expander}},
Path=Width}"
Height="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType={x:Type ToggleButton}},
Path=ActualHeight}">
<CheckBox IsChecked="False" DockPanel.Dock="Right"/>
<TextBlock Text="{Binding Path=Name}" Foreground="White"
FontWeight="Bold" HorizontalAlignment="Center" />
</DockPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<!-- (...) -->
<ListView ItemsSource="{Binding GroupedConfig, Mode=TwoWay}"
ItemTemplate="{StaticResource ExplorerView}">
<ListView.ItemsPanel>
<ItemsPanelTemplate >
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth),
RelativeSource={RelativeSource
AncestorType=Expander}}"
ItemWidth="{Binding (ListView.View).ItemWidth,
RelativeSource={RelativeSource AncestorType=ListView}}"
ItemHeight="{Binding (ListView.View).ItemHeight,
RelativeSource={RelativeSource AncestorType=ListView}}" />
<!--MinWidth="{Binding ItemWidth,
RelativeSource={RelativeSource Self}}"-->
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}" />
</ListView.GroupStyle>
</ListView>
Any ideas?
I'm trying to insert some appropriate Setter in the style defined for GroupItem, but I'm starting to think that this is not the right way to do.
Thanks!
I finally found the right property to edit after many tries.
I guess it could be useful to post it here if anybody would need to do something with the same behavior:
So we actually have a property Panel in the GroupStyle in which we can add this so needed WrapPanel :
<ListView.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel Width="800" />
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
In case anyone is here like I am trying to make a the ListBox Items Wrap but based on an unknown amount of items so you cannot set a Width like the above answer, this is how I did it.
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" MaxHeight="{Binding Converter={StaticResource ListBoxHeightToItemsPanelHeightConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=ActualHeight}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
In my converter I simply subtract 30 to account for the height of the header.
Here is the complete code:
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Margin="8" FontSize="18" TextAlignment="Center" FontWeight="Bold" Foreground="White" >
<TextBlock.Text>
<Binding Path="Name"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" MaxHeight="{Binding Converter={StaticResource ListBoxHeightToGroupStyleHeightConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=ActualHeight}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Template>
<ControlTemplate>
<!-- Your template here. -->
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate >
<!-- Your template here. -->
</DataTemplate>
</ListBox.ItemTemplate>
Hope this helps save someone some time!