ListView with nested Expander not collapsing - wpf

This question is the same as this other unanswered question.
When the Expander is expanded the outer ListView grows to make space for the expanders contents, but when the Expander is then collapsed the view does not force the ListView to resize.
Reduced code, with notes after:
<!--<StackPanel>-->
<ItemsControl>
<!-- ParameterGroupView -->
<Border BorderBrush="Brown" BorderThickness="1" CornerRadius="4" Padding="4">
<ListView HorizontalContentAlignment="Stretch">
<Expander Header="Expander A" IsExpanded="False">
<ListView HorizontalContentAlignment="Stretch">
<!-- TextView -->
<TextBlock >Content A</TextBlock>
<TextBlock >Content B</TextBlock>
</ListView>
</Expander>
</ListView>
</Border>
</ItemsControl>
<!--</StackPanel>-->
I have the ParameterGroupView in a ItemsControl or StackPanel because there is actually many ParameterGroupView entries. Swapping to a StackPanel does not change the behaviour.
Removing the Boarder does not affect the behaviour, but having it helps show the behaviour with only a single ParameterGroupView.
There can be many Expander sections in the outer ListView, and the Expander can have many entities inside the inner ListView.
The outer ListView and Expander is to replace a TreeView, that was used to have a list of collapsible nodes, but the TreeView's internal use of grids, means the TextView items were squashed horizonatlly, the same as if you remove ether HorizontalContentAlignment="Stretch" attributes.
So if there is another way to wrap/wire all this up, I'll be also happy.
This is a problem because our TextView blocks are large and there are many Expanders.
Edit: TextView is used as the code is data-bound, and thus dynamically put together. So any replacement for ListView would need some form of ItemsSource

Found the solution, and it details where in the question already.
ItemControl accepts an ItemsSource and auto resizes. So replacing the two ListViews with ItemControls gets the nested collapsing.
But there is no scrolling, so wrapping the outer ItemControl with a ScrollViewer, reproduces the complete desired effect.
<ScrollViewer
VerticalScrollBarVisibility="Auto">
<ItemsControl>
<!-- ParameterGroupView -->
<Border
BorderBrush="Brown"
BorderThickness="1"
CornerRadius="4"
Padding="4"
Height="Auto">
<ItemsControl
HorizontalContentAlignment="Stretch">
<Expander
Header="Expander A"
IsExpanded="False">
<ItemsControl
HorizontalContentAlignment="Stretch">
<!-- TextView -->
<TextBlock>Content A</TextBlock>
<TextBlock>Content B</TextBlock>
</ItemsControl>
</Expander>
</ItemsControl>
</Border>
</ItemsControl>
</ScrollViewer>
I was testing with double Expanders in the Border and double Border sections.

The most obvious thing to do here is to put the expanders in a container other than a listview:
<Border BorderBrush="Brown" BorderThickness="1" CornerRadius="4" Padding="4">
<StackPanel>
<Expander Header="Expander A" IsExpanded="False">
<ListView HorizontalContentAlignment="Stretch" MinWidth="100">
<ListBox Name="listb"></ListBox>
<!-- TextView -->
<TextBlock >Content A</TextBlock>
<TextBlock>Content B</TextBlock>
</ListView>
</Expander>
</StackPanel>
</Border>
The container resizes around content just fine.
If you absolutely must have it in a ListView (which is possible) then I don't know of a way to get a ListView to resize easily once it's grown (beyond setting explicit sizes of the whole thing, which is clumsy and not useful). If thats the case then it's possible that you'll have to use a controllable listbox to display all the open expanders, or display the content in a different way (like in a popup, maybe?) if you want to be able to see it all at a glance.

Related

How do you make a vertically scrolling scrollviewer inside a tabControl?

I want to make it so that my TabControl is vertically scrollable, but I can't seem to do it. The following sample acts as though there was no scrollviewer at all. I even tried putting the TabControl inside the scrollviewer, or putting it all in a grid and constraining the height of the grid, but nothing works.
<DataTemplate x:Key="tabControlTemplate">
<TabControl ItemsSource="{Binding guiItems}" DisplayMemberPath="Title" Height="Auto" Template="{StaticResource mainTabControlTemplateEx}">
<TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" CanContentScroll="True">
<StackPanel Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl ItemsSource="{Binding guiItems }" ItemTemplateSelector="{DynamicResource templateSelector}"/>
</StackPanel>
</ScrollViewer>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</DataTemplate>
The actual problem is not clear from the question.
You are not getting to see scrollviewer and the content inside is clipped? PLease confirm if that is the case.
If the problem is you are getting to see entire content taking up all the available space, and you would like to control that using scroll viewer, then you would need to set 'MaxHeight' property on Scroll Viewer. This would limit the height of your DataTemplate and would make verticall scroll bar visible if the inner content goes beyond the MaxHeight.
Hope that helps.

Is there a way to apply a scrollviewer to only part of a treeview item?

I have a treeview where Each treeview item contains both a signal and a signalname.
I want to have a namepanel inside the treeview item that expands, but i want a scrollviewer to only scroll across the right portion of the panel that contains the signals of the treeviewitem.
Is the only answer to create a custom treeview class? My original idea was to apply two style templates to each of the treeviews so that the expander button is just on one treeview and just make the expansion of one result in the expansion of the other. but ultimately I couldn't figure out a a way to link the items in the two treeviews using triggers so that when one item expands, its counterpart expands.
i also played around with customizing it using just the controltemplate, but I feel like the scrollviewer needs to be placed around only part of the treeviewitem, but there is no way to split the item going to itemspresenter while inside the treeview control template.
Is my best option to just create a custom treeview?
edit:
creating an itemtemplate like suggested will create a separate scrollviewer for part of each item, so I would end up with 6 small scrollviewers. But what I'm trying to do is apply a scrollviewer over the right column of all the items. However, to apply a scrollviewer to all items, I think I would need to place it within the treeview controltemplate. But in the treeviewcontroltemplate you only have access to itemspresenter:
<ControlTemplate TargetType="TreeView">
<ScrollViewer
Focusable="False"
CanContentScroll="False"
Padding="4">
<StackPanel>
<wpfExp:SignalGraphAxis
PenColor="{Binding ElementName=GraphColorPicker, Path=SelectedColor, Mode=OneWay}"
Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}"
PenWidth="{Binding ElementName=graph_viewer, Path=GraphPenWidth, Mode=OneWay}"
X_Scale="{Binding ElementName=graph_viewer, Path=X_Scale, Mode=OneWay}"
MaxTimeValue="{Binding ElementName=graph_viewer, Path=_SignalDataViewModel.MaxTimeValue, Mode=OneWay}"
/>
<ItemsPresenter />
</StackPanel>
</ScrollViewer>
</ControlTemplate>
You can do it using Itemtemplate. here is an example
<TreeView>
<TreeView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0">
<!-- Your Signal data binding -->
</ScrollViewer>
<!-- Your Signalname data binding -->
</Grid>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Keyboard navigation in WPF Grids

Suppose you have a StackPanel, which contains a ScrollViewer which contains another StackPanel with an ItemsControl with a bound ItemsSource. This ItemsSource is bound to a collection of Grids created at runtime. Each Grid contains a label and a textbox/combobox/a few checkboxes that all have a unique TabIndex value within the StackPanel.
Here is the xaml:
<ScrollViewer Name="scrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Name="stackPanel" MinWidth="500" Width="Auto">
<ItemsControl Name="itemsControl" ItemsSource="{Binding ElementName=SomeWindow, Path=GridsCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</ScrollViewer>
I want to simply tab from one control to the next, but only within the controls in the grids in the grids collection. So far I've tried different KeyboardNavigation.TabNavigation settings but without any luck. What is the best way to do this?
Set TabNavigation to KeyboardNavigationMode.Cycle for each container you want to behave like that, so the focus won't escape it as long as you use Tab and Shift+Tab:
KeyboardNavigation.SetTabNavigation(grid1, KeyboardNavigationMode.Cycle);
If you want to change Ctrl+Tab behaviour, use KeyboardNavigation.SetControlTabNavigation.
You can apply an implicit style that disables tabbing for every Control, then you re-enable it for just what you want to be tab-able:
<ScrollViewer Name="scrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<Style TargetType="Control">
<Setter Property="IsTabStop" Value="False" />
</Style>
</ScrollViewer.Resources>
<StackPanel Name="stackPanel" MinWidth="500" Width="Auto">
<ItemsControl Name="itemsControl"
ItemsSource="{Binding ElementName=SomeWindow, Path=GridsCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</ScrollViewer>
Don't forget to set IsTabStop on your dynamically generated Grids to True

Prevent WPF control from expanding beyond viewable area

I have an ItemsControl in my user control with a scroll viewer around it for when it gets too big (Too big being content is larger than the viewable area of the UserControl). The problem is that the grid that it is all in just keeps expanding so that the scroll viewer never kicks in (unless I specify an exact height for the grid). See code below and thanks in advance.
<UserControl x:Class="BusinessObjectCreationWizard.View.TableSelectionPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<GroupBox FontWeight="Bold" Height="300px"
Header="Tables"
Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal"
ItemsSource="{Binding Path=AvailableTables}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=DisplayName}"
IsChecked="{Binding Path=IsSelected}"
Margin="2,3.5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</UserControl>
This user control is loaded here
<Border Background="White" Grid.Column="1" Grid.Row="0">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"
Header="{Binding Path=CurrentPage.DisplayName}" />
</Border>
I would like to not specify the height.
If you remove the Height from your GroupBox (which, as far as I understand, is what you want to do), then it will fill its container, unless there's a panel upstream that imposes its own sizing rules.
I used this simplified version of your XAML. I removed the template and the binding, and hard-coded some items, to make this stand alone; those changes won't affect the way layout is done.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</Window>
Run it, and you'll see that the content does indeed size to fit the window, and the scrollbar only enables when the window gets too small to see all three items. I believe this is what you want.
So the problem is most likely one of the parent panels, one you're not showing in your sample XAML. The problem you describe could occur if your GroupBox appears inside a StackPanel:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<StackPanel>
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Window>
Now the GroupBox appears at the top of the Window, sized to exactly fit its contents. If you shrink the Window enough, the GroupBox will be cut off -- because it's sized to fit its content, not its container. This sounds like the problem you're describing.
The reason is that StackPanel asks its children what their ideal height is (based on their content), and uses that height. Without StackPanel (or something similar), the default is to respect the control's VerticalAlignment, and if that's set to the default value of Stretch, then the control is stretched to fill its parent. This means it won't be taller than its parent, which sounds like what you want.
Solution: remove the StackPanel (or whatever else is causing you problems) and use something else. Depending on what you're trying to accomplish, you might have better luck with a DockPanel or a Grid. Hard to tell without knowing more about your layout.
Edit: Okay, it looks like the problem is indeed the HeaderedContentControl parent -- but not directly. HeaderedContentControl isn't a panel, so it doesn't do any layout of its own (and its descendant, GroupBox, doesn't have this same problem). The problem is its default template -- which includes a StackPanel. The good news is, you're free to use a different template, let's say one with a DockPanel instead:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<HeaderedContentControl>
<HeaderedContentControl.Style>
<Style TargetType="{x:Type HeaderedContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<DockPanel>
<ContentPresenter ContentSource="Header" DockPanel.Dock="Top"/>
<ContentPresenter/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</HeaderedContentControl.Style>
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</HeaderedContentControl>
</Window>
If you leave off the <HeaderedContentControl.Style> part, this reproduces your problem; but with the style in place, it allows the GroupBox to fill its container, so the ScrollViewer will get a scrollbar when you want it to.
If the previous answer doesn't fix the problem, you could also try binding the Width, Height of your grid to the ActualWidth, ActualHeight of your parent UserControl. Something like:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication.UserControl1"
x:Name="UserControl">
<Grid Height="{Binding ElementName=UserControl, Path=ActualHeight}"
Width="{Binding ElementName=UserControl, Path=ActualWidth}" />
In this case you aren't setting an explicit width and height but you are limiting the Grids width/height to the constraints of the UserControl it sits in.
I had the same issue, after reading this response I replaced all StackPanels with Grids in UserControl. It resolved the Scrollbar issue.
Try removing the grid entirely and setting the HorizontalAlignment and VerticalAlignment directly on the GroupBox. If a layoutpanel has only one child, it's often redundant... this migth be true in your case.
If that doesn't work... what's the parent of your grid control?
Why not just use a listbox instead of an itemscontrol, that has a built in scrollviewer.
They are different. If you do not want to have the items selectable, then don't use a ListBox. It is going to be heavier, and will also have the deselect a selection everytime the user clicks on an entry. Just put the ItemsControl in a ScrollViewer
I had the same problema with ListBox, it wasn't expanding and the scroll viewer didn't appear. I solved it as follows:
<UserControl x:Class="TesteView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid MaxHeight="710">
....
....
<StackPanel>
<ListBox MaxHeight="515"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Path=Teste,Mode=TwoWay}">
....
....
</ListBox>
</StackPanel>
</Grid>
</UserControl>

Getting UI virtualization working with ItemsControl in Silverlight

I'm trying to create a scrolling list of fairly large textblocks. I want there to be a vertical scrollbar to show them all, and if they overflow a certain size I want them to display an ellipsis. I actually have all this working pretty good.
I have the following Silverlight XAML:
<Grid x:Name="LayoutRoot" MaxWidth="500" MinWidth="100"
MaxHeight="500" MinHeight="100">
<Grid.DataContext>
<app:MainPageViewModel/>
</Grid.DataContext>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding TextItems}" Margin="0,20,0,20">
<ItemsControl.ItemTemplate><DataTemplate>
<Border MaxHeight="175" Margin="0,0,0,18" CornerRadius="5">
<TextBlock Margin="2" TextTrimming="WordEllipsis"
TextWrapping="Wrap" Text="{Binding}"/>
</Border>
</DataTemplate></ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
My problem is that this layout does not use UI virtualization, such as with a VirtualizingStackPanel. So it is pretty slow. What is the best way to get UI virtualization into this layout? I've tried about a half dozen different ways and nothing has worked out all that well.
I managed to get this working in a ListBox because it seems to support virtualization out of the box. However, I'd prefer to use ItemsControl as I don't want these things to be selectable, and I don't want the styling that comes along with a ListBox.
This in Silverlight 4.
There are a few things you need to do to make this work.
Set the ItemsPanelTemplate for
your ItemsControl to a
VirtualizingStackPanel.
Incorporate the ScrollViewer inside
a ControlTemplate for your
ItemsControl instead of just
wrapping it around the outside.
Make sure the ItemsControl has a fixed height so the layout system can work out how many items it needs to fill the viewport. (It looks like you are already doing this by putting the ItemsControl in a Grid - that will allow the layout system to determine the alloted height for the control)
Here's the simplest example I could come up with of this working:
<ItemsControl ItemsSource="{Binding TextItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border>
<ScrollViewer>
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>

Resources