ListBox with ItemTemplate (and ScrollBar!) - wpf

I have a databound and itemtemplated ListBox:
<ListBox x:Name="lbLista"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Deleteable, Mode=TwoWay}" />
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ites show fine and they come from an ObservableCollection.
The problem is the scrollbar which appears but is not usable - it does not have a handle to grab. I've tried setting some ScrollView attached properties on ListBox, but they do not affect the situation.

I pasted your code into test project, added about 20 items and I get usable scroll bars, no problem, and they work as expected. When I only add a couple items (such that scrolling is unnecessary) I get no usable scrollbar. Could this be the case? that you are not adding enough items?
If you remove the ScrollViewer.VerticalScrollBarVisibility="Visible" then the scroll bars only appear when you have need of them.

ListBox will try to expand in height that is available.. When you set the Height property of ListBox you get a scrollviewer that actually works...
If you wish your ListBox to accodate the height available, you might want to try to regulate the Height from your parent controls.. In a Grid for example, setting the Height to Auto in your RowDefinition might do the trick...
HTH

I have never had any luck with any scrollable content placed inside a stackpanel (anything derived from ScrollableContainer. The stackpanel has an odd layout mechanism that confuses child controls when the measure operation is completed and I found the vertical size ends up infinite, therefore not constrained - so it goes beyond the boundaries of the container and ends up clipped. The scrollbar doesn't show because the control thinks it has all the space in the world when it doesn't.
You should always place scrollable content inside a container that can resolve to a known height during its layout operation at runtime so that the scrollbars size appropriately. The parent container up in the visual tree must be able to resolve to an actual height, and this happens in the grid if you set the height of the RowDefinition o to auto or fixed.
This also happens in Silverlight.
-em-

Thnaks for answer. I tried it myself too to an Empty Project and - lo behold allmighty creator of heaven and seven seas - it worked. I originally had ListBox inside which was inside of root . For some reason ListBox doesn't like being inside of StackPanel, at all! =)
-pom-

Related

Max Height for a dynamically sized WPF Textbox

I'd like to make a TextBox in XAML that is dynamically sized to the content, but that has a max height that keeps it from growing forever if that text is very long. If that max height is reached, the TextBox should stop growing and instead show a scroll bar. Ideally, that scrollbar does not exist when the text fits. How would I go about that?
I gain the dynamic resizing property by simply not setting an explicit Height on the TextBox (and possibly turning on text wrapping). But achieving the max height and scroll bar is a mystery to me.
Currently I have a setup that always shows a scroll bar and that grows forever. How would I change this?
<ScrollViewer>
<TextBox Text="{Binding Path=Selection.SummeryDescription, UpdateSourceTrigger=PropertyChanged}" />
</ScrollViewer>
You can do that just by setting the ScrollViewer.VerticalScrollBarVisibility to Auto and the MaxHeight. See example below:
<TextBox Text="..." TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="500" />
This will only show a vertical scrollbar when needed.
Please notice that in my example the ScrollViewer element is omitted as I enable the ScrollViewer via the ScrollViewer.VerticalScrollBarVisibility property instead.

TextTrimming in panel in ScrollViewer

I am trying to set up an element within a WPF application, but cannot get the TextTrimming on the TextBlocks to work properly. This is within a ScrollViewer. I am currently using a DockPanel as the inner container.
I have searched quite a bit, but found no questions addressing this issue.
The XAML for the container:
<Grid>
<ScrollViewer>
<DockPanel Name="listedCharacters" LastChildFill="False"></DockPanel>
</ScrollViewer>
</Grid>
The XAML for the child elements (added by code):
<UserControl …>
<Grid DockPanel.Dock="Top" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding FullName}" TextTrimming="CharacterEllipsis" />
</Grid>
</UserControl>
The first problem is that whether I use the DockPanel or a StackPanel, as the inner container, the child element's width appears to be dictated by its content (the TextBlocks) rather than constrained by the parent ScrollViewer.
The effect I want is for the ellipsis to truncate each TextBlock's content when the Window's grid column (not shown in code) is narrower than the bound text. Basically, a list that scrolls vertically when needed, and trims horizontally (which I thought would have been sufficiently common that the answer would be out there; alas, no).
I believe I need to use my own UserControl for this, as there is a lot more going on than shown her; right-click menus on the item in the list, etc.
The secondary issue, iff the optimal panel to use is the DockPanel, how to apply the DockPanel.Dock="Top" through code (C#) when the elements are dynamically added? Again, I cannot find anything that appears to explain this. (I know it is probably in the wrong place in the sample code above.)

WPF ContentControl width grows but doesn't shrink when wrapped in a ScrollViewer

I'm trying to figure out how to make my ContentControl to correctly scroll Horizontally (Vertically its fine at the moment). By correctly i mean i would like to see the content to stretch (expand infinitely) while having minimum sizes to which a scrollbar would appear in order for the content not to overflow behind the ContentControl's area, so here's a quick introduction:
The main window is structured in this way:
Grid (2 columns of .3* and .7*)
Border
Grid (7 rows, one set to * where ContentControl is)
ScrollViewer with StackPanel (purely for test) wrapping a ContentControl that has Auto Width
ContentControl's Template:
Grid (Width set to UserControl's ActualWidth, 6 rows with one set to Auto where ItemsControl go
ItemsControl that describes an ItemTemplate of a type DataTemplate which contains a Grid inside of which i have a DataGrid
The actual problem is that the ContentControl grows as you resize the window, but does not shrink with window resize.
Main View XAML (truncated for clarity):
<ScrollViewer Grid.Row="5" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ContentControl Grid.Row="5" Background="Transparent" Focusable="False" Margin="0,5,0,0"
Content="{Binding CurrentSection}" ContentTemplateSelector="{StaticResource templateSelector}/>
</ScrollViewer>
Tempate XAML (truncated for clarity):
<Grid>
...
<ItemsControl Grid.Row="4" ItemsSource="{Binding Data.QualifyingDistributionsDividends}" x:Name="QualifyingItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="DTLayoutGrid">
...
<Grid Grid.Row="1" x:Name="DataLayout" Width="{Binding ElementName=DTLayoutGrid, Path=ActualWidth}" HorizontalAlignment="Stretch">
...
<DataGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="8" HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Payments}" Style="{StaticResource DataGridStyle}" CellStyle="{StaticResource DataGridNormalCellStyle}">
</DataGrid>
</Grid>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
So what happens? Datagrid assumes width of the entire DataTemplate (well its underlying controls that are set to be DataTemplates size, then * column assumes all empty space. When you try to resize the entire window that holds this code it will grow correctly, expanding the * column but it seems shrinking is not "registered" and it keeps the size you expanded it to, applies a scrollbar over that and forgets about it.
What i've tried so far was to set widths for ItemsControl, its underlying parents like Grid etc, also setting size to ContentControl, StackPanel, ScrollViewer and parent Grid of that.
I've also tried using scrollviewers directly on the Datagrid which produces an epileptic "1 million resizes a second" scenario. I've also played around with HorizontalAlignments
Under certain situations i DID managed to get the horizontal scrollbar to appear correctly but unfortunately that makes my DataGrid's * column to assume Auto Width rather then Star so DataGrid starts having an empty area to the right (unacceptable unfortunately...)
I understand that in order for horizontal scrollbar to work the parent or child of the scrollviewer needs Width set, i guess i can't work out where exactly do i need to restrict it. DataGrids NEED to infinitely expand with the main window while having first column fill all the available space.
Do let me now if you need more information on this and I will gladly answer.
It seems to me that this is just another case of the dreaded StackPanel layout problem. This problem comes up again and again and I confess that I had the very same problem when I started learning WPF. The StackPanel does not take the available size of its parent into consideration whereas other Panels such as a DockPanel or a Grid (yes, that's actually a Panel too) do.
It's explained in the How to: Choose Between StackPanel and DockPanel page on MSDN:
Although you can use either DockPanel or StackPanel to stack child elements, the two controls do not always produce the same results. For example, the order that you place child elements can affect the size of child elements in a DockPanel but not in a StackPanel. This different behavior occurs because StackPanel measures in the direction of stacking at Double.PositiveInfinity; however, DockPanel measures only the available size.
The StackPanel should only really be used to align a number of items, such as Buttons or other controls in a straight line where available space is not a concern. So anyway, the solution should be simple... just remove the StackPanel from the ScrollViewer. It doesn't appear to serve any purpose there anyway.
UPDATE >>>
After looking again, it seems as though you're saying that the problem is inside the DataTemplate, right? You might be able to fix that by setting the ItemsControl.HorizontalContentAlignment property to Stretch. That would ensure that each item remains within the boundary of the ItemsControl.
I'd also remove your Binding on the Grid.Width as you don't need it... a child Grid will take up the full space of a parent Grid by default. If these ideas don't work, just simplify your problem. Seriously, if you follow the advise in the linked page from the Help Center that I gave you in the comments, then you'll either fix the problem, or be able to come back here and provide a complete, but concise example that we could test.
I've found the behavior I was looking for by using a UniformGrid as the ItemsPanel, with its rows bound to the count of the ItemsSource model:
<ScrollViewer>
<ItemsControl ItemsSource="{Binding MyCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="{Binding MyCollection.Count}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
As #Sheridan pointed out above, it seems the StackPanel is causing trouble. Also, credit to https://stackoverflow.com/a/23375262/385273 for pointing out the UniformGrid option.

WPF ListBox scrolls to top when I change status message or show wait screen

I'm developing an opensource application named Media Assistant. I used a ListBox to show the library. ItemsSource is bound to a list of LibraryItem. Here is the XALM.
<ListBox Name="Tree" DockPanel.Dock="Top"
ItemsSource="{Binding DataSource.OrderedLibraryItems}"
Background="{StaticResource LibraryBackground}"
Width="220" HorizontalAlignment="Left"
BorderThickness="0"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
ScrollViewer.IsDeferredScrollingEnabled="True"
ItemTemplate="{StaticResource ListLibraryItemTemplate}"
SelectionMode="Single"
MouseDoubleClick="HandleMouseDoubleClick"
/>
The problem is when I show any status message at the bottom of my window from a thread by using Dispatcher.
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,new ParameterizedThreadStart(action), state);
The ListBox scrolls to at the top. If I don't show any status message then it works just fine. The datacontext or list items or focus has not been changed. I could not found any reason why it's doing that. It happens when I display any wait screen which is a non modal window. I could not recreate it in a different project. Here is the source code of Media Assistant.
You can easily re-create it by un-commenting the return statement of method SetStatusMessage at BackgroundScanner class.
I found the reason behind this, so the solution.
I used a DockPanel to layout my UI. I put my status bar at the bottom, the ListBox on the Left and other items are on middle and top. There is a TextBlock in my StatusBar which has width and Height set to Auto. So, when I changed text of my StatusBar TextBlock it's width and height gets recalculated and It's parent's recalculates it's layout. Hence the ListBox gets invoked to Measures and Arrange. Even though it's size does not gets changed it resets it's scroll position to top. It happens only if I use ScrollViewer.CanContentScroll="True" at the ListBox. By default it is True. So, even though I did not set this value It was resetting the scroll position. If I disable it by using ScrollViewer.CanContentScroll="False" then it works fine.
<ListBox Name="Tree" DockPanel.Dock="Top"
ItemsSource="{Binding DataSource.OrderedLibraryItems}"
Background="{StaticResource LibraryBackground}"
Width="220" HorizontalAlignment="Left"
BorderThickness="0"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
ScrollViewer.IsDeferredScrollingEnabled="True"
ScrollViewer.CanContentScroll="False"
ItemTemplate="{StaticResource ListLibraryItemTemplate}"
SelectionMode="Single"
MouseDoubleClick="HandleMouseDoubleClick"
/>
But setting ScrollViewer.CanContentScroll="False" disables virtualization and I want to use virtualization to my ListBox so I set fixed Height and Width to the TextBlock. So, the DockPanel does not re-arrange it's children if I change the status message.
May be it's a bug at ScrollViewer. It should not change the scroll position if the size has not changed.
In reply to #user904627's answer, here is an enhanced version of his workaround. The issue with just fixing Width and Height is the ListBox keeps the same position even if the user resizes the Window. This is not acceptable.
This is why I created this tiny behavior which fixes Width and Height but listens to the parent element's SizeChanged event to let the ListBox resize itself when the container's size changes.
The code is here: VirtualizedListBoxFixBehavior
When the parent element is resized, I restore Width and Height to double.NaN (so the control can resize itself) and I queue the bit of code which fixes the size properties to actual values in the Dispatcher for later execution.
But this still is an ugly working workaround...
Try this:
listBox1.ScrollIntoView(listBox1.Items.GetItemAt(listBox1.Items.Count - 1));
Since the layout is being reset, it is expected that the list box to select the first item (0).
Can you try to set the selected item to the number of existing items in the list box:
Tree.SelectedIndex = Tree.Items.Count;
I did not test this solution on your code but I have used it in another project of mine where I had a similar problem.
Hope it helps.

Bing Maps in a Listbox boundary issue

Normally the silverlight controls know where they are in terms of who is in front of or behind. An example is putting an image inside a listbox and when you scroll up and down in the listbox, the image will disappear/hide inside the listbox boundaries.
I have put a bing map object(the one that comes with the windows phone 7 sdk) inside a listbox. When I scroll to where the map is in the listbox, it is acting like I have some flag set to "Always on Top". I can't seem to find a property that is setting this or if it's inherent in the way the maps are designed.
I haven't tried this yet, but I'm curious if I add layers with pushpins in them if they too would act "Always on Top". I've included an image to explain. As you can see below, the map is outside of the listbox's bounding area and is even overlapping a button outside of the listbox.
Link to Map Image
<ListBox Height="590">
<TextBlock IsHitTestVisible="False" Foreground="#F80046" Style="{StaticResource PhoneTextExtraLargeStyle}" TextAlignment="Center" Text="Map"></TextBlock>
<my:Map Width="445" x:Name="EventMap" Margin="0,0,0,20" LogoVisibility="Collapsed" CopyrightVisibility="Collapsed">
<my:Map.CredentialsProvider>
<my:ApplicationIdCredentialsProvider ApplicationId="OMITED"></my:ApplicationIdCredentialsProvider>
</my:Map.CredentialsProvider>
</my:Map>
</ListBox>
I'm not sure what you're trying to accomplish, but this seems like more of a usage of ScrollViewer
<ScrollViewer>
<StackPanel>
<my:Map>
</StackPanel>
</ScrollViewer>
rather than ListBox. But anyway, I couldn't reproduce the problem. Does that button have some custom margins that could be doing that?
Here's the solution file I created to see the problem you have in the image. Note that I wasn't able to reproduce it in the solution.
http://dl.dropbox.com/u/129101/WindowsPhoneApplication1.zip

Resources