ListBox scrolling with variable height - silverlight

I have a problem with ListBox scrolling. Everytime I have more items than can be shown on screen and I try to scroll through the list it automatically snaps back to the top. I found this can be fixed by setting a fixed height to the ListBox.
However, I need to add items to the list dynamically and hence the height changes.
The Listbox is in the second row of a grid nested in a PivotItem.
How can I either set the height of the Listbox dynamically with XAML or achieve a scrolling effect that does not always snap to the top and hence prevents me from reading/editing items on the bottom?
Here is the XAML:
<Grid x:Name="LayoutRoot">
...
<controls:Pivot Title="Malts">
<controls:PivotItem Header="Gravity" Name="GravityPivot">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid> some more controls in row 0</Grid>
<Grid
Grid.Row="1">
<ListBox
VerticalAlignment="Stretch"
ItemsSource="{Binding ItemList}"
Padding="10,10,0,10"
ItemContainerStyle="{StaticResource ListBoxItemStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock
Style="{StaticResource PhoneTextExtraLargeStyle}"
Text="{Binding Name}"
VerticalAlignment="Bottom"/>
<StackPanel>
<StackPanel
Orientation="Horizontal" >
<TextBlock
Text="{Binding Amount, Converter={StaticResource WeightConverter},StringFormat=' {0:f2}'}"
Margin="10,0,5,0"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock
Text="{Binding ., Converter={StaticResource UnitExtension}, ConverterParameter='Weight'}"
Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
</StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem
Header="Edit"
Command="{Binding ElementName=MaltList, Path=DataContext.EditCommand}"
CommandParameter="{Binding}"/>
<toolkit:MenuItem
Header="Remove"
Command="{Binding ElementName=MaltList, Path=DataContext.RemoveCommand}"
CommandParameter="{Binding}"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</controls:PivotItem>
Thank you!

Try this, should fix you up;
<ListBox>
<!-- You want this part... -->
<ListBox.Template>
<ControlTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
</ListBox>

Related

Unable to scroll within WPF ListBox

I have a horizontal ListBox in my WPF window, and each item within it contains another vertical ListBox within it.
I want to be able to scroll vertically either within the internal ListBox or the external one.
Scrolling horizontally is not an issue as it automatically lets me do it when the item list is bigger than the ListBox margins, however i failed to make either of my ListBoxes to be vertically scrollable.
I have tried using ScrollViewer.VerticalScrollBarVisibility="Visible" on either ListBox but the scrollbar is grayed out and unusable, even though there are items outside the margins of the ListBox.
My XAML:
<StackPanel>
<Grid Margin="100">
<ListBox x:Name = "ColumnList" ItemsSource="{Binding Board.Columns}" VerticalAlignment="Top" >
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush = "Black" MinWidth="200" MaxWidth="200" MinHeight="300" MaxHeight="300" ScrollViewer.VerticalScrollBarVisibility="Visible">
<StackPanel>
<TextBox Text = "{Binding Name}" IsReadOnly="True"/>
<ListBox x:Name="TasksList" ItemsSource="{Binding Tasks}" MouseDoubleClick="TasksList_MouseDoubleClick" ScrollViewer.VerticalScrollBarVisibility="Visible" SelectedItem="{Binding Path=DataContext.Task,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush = "Black" BorderThickness="2">
<StackPanel>
<TextBox Text = "{Binding Title}" IsReadOnly="True" />
<TextBox Text="{Binding Description}" IsReadOnly="True"/>
<TextBox Text = "{Binding CreationDate}" IsReadOnly="True" />
<TextBox Text="{Binding DueDate}" IsReadOnly="True"/>
<TextBox Text = "{Binding EmailAssignee}" IsReadOnly="True" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel >
<ItemsPanelTemplate >
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</StackPanel>
you have to change outer ListBox ItemTemplate - use Grid instead of StackPanel. Inner ListBox in StackPanel get the height to display all items, so scroll can't be activated, even if those items are not seen on the screen.
<DataTemplate>
<Border BorderBrush = "Black"
MinWidth="200" MaxWidth="200"
MinHeight="300" MaxHeight="300"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Text = "{Binding Name}" IsReadOnly="True"/>
<ListBox x:Name="TasksList" Grid.Row="1">
<!--omitted for clarity-->
</ListBox>
</Grid>
</Border>
</DataTemplate>

Why ScrollViewer overrides Grid row height in wpf?

I have a ScrollViewer which contains a Grid having two rows of height '*'
<ScrollViewer>
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Grid.Column="0"
Header="XYZ"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Margin="0,10,0,0"
Width="Auto" MinWidth="160"
BorderThickness="0"
Style="{StaticResource MyGroupBoxStyle}">
<ListBox Name="lstMentorGroups" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=MyCollection}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Style="{StaticResource MyListBoxStyle}">
<ListBox.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
<GroupBox Grid.Row="1" Grid.Column="0" MinWidth="160"
Header="ABC" Margin="0,10,0,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderThickness="0"
Style="{StaticResource MyGroupBoxStyle}">
<ListBox ItemsSource="{Binding Path=List1, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Style="{StaticResource MyListBoxStyle}" Margin="0,0,5,0">
<ListBox.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding Path=Prop1}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</Grid>
</ScrollViewer>
But at run time 1st Group box gets full height needed by its containing listbox that means it overrides Grid height * to auto
It works fine if I don't use scrollviewer it gives 50-50% height to each groupbox.
You need set the Height of the Grid if it's in the ScrollViewer, otherwise the Grid will have as much Height as it need, which means the ListBox in the Grid will get unlimited Height to display its items.
To ensure that the both rows takes the same height you should use the SharedSizeGroupe
So what you should do is:
<ScrollViewer>
<Grid HorizontalAlignment="Stretch" Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="A" />
<RowDefinition SharedSizeGroup="A" />
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Grid.Column="0"
Header="XYZ"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Margin="0,10,0,0"
Width="Auto" MinWidth="160"
BorderThickness="0"
Style="{StaticResource MyGroupBoxStyle}">
<ListBox Name="lstMentorGroups" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=MyCollection}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Style="{StaticResource MyListBoxStyle}">
<ListBox.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
<GroupBox Grid.Row="1" Grid.Column="0" MinWidth="160"
Header="ABC" Margin="0,10,0,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderThickness="0"
Style="{StaticResource MyGroupBoxStyle}">
<ListBox ItemsSource="{Binding Path=List1, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Style="{StaticResource MyListBoxStyle}" Margin="0,0,5,0">
<ListBox.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding Path=Prop1}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</Grid>
As Grid is placed in ScrollViewer so it takes whole space how much it's controls requires. And I don't want to show vertical scroll bar.
I just set Grid MaxHeight to ScrollViewer ActualHeight
MaxHeight="{Binding ElementName=ScrollViewer, Path=ActualHeight}"

WPF height and scroll at listbox - imposible

To keep things simple all i am trying to do is to place a listbox and a button below it. Although it seems simple it isn't. It is not, because it is in a GRID...
The row size is star size and listbox having verticalAlign top works great all the time. The problem is that I cannot add a button below it. Having a grid with 2 rows one star height and the other auto would place the button at the bottom of the form even if listbox contents are little. Trying to set both rows auto doesn't work when resizing as no scrollbar is visible at the listbox.... Any workaround for this??
Update some code
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DockPanel >
<ListBox DockPanel.Dock="Top" Name="lbStaff" ItemsSource="{Binding}"
Grid.Row="0" VerticalAlignment="Top" BorderThickness="0"
Background="WhiteSmoke" Margin="15,10,20,30" Style="{DynamicResource
ListBoxUsers}" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource
ListBoxTest1}"></Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel Margin="5,4,5,4">
<TextBlock HorizontalAlignment="Stretch" FontSize="16"><Run
Text="{Binding Name}"/> - <Run Text="{Binding Mode=OneWay,
Path=PositionString}"/></TextBlock>
<TextBlock >Τηλέφωνο <Run Text="{Binding Phone}"/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button DockPanel.Dock="Top" Grid.Row="0" HorizontalAlignment="Right"
VerticalAlignment="Top" Margin="0,10,7,0" Style="{DynamicResource
ButtonStyleNew1}">
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="/WpfApplication1;component/Images/filenew1.png"
Stretch="None" VerticalAlignment="Center"></Image>
<TextBlock Margin="5,0,0,0" FontSize="16">Προσθήκη Χρήστη</TextBlock>
</StackPanel>
</Button.Content>
</Button>
</DockPanel>
</Grid>
</Grid>
What i usually do is not using rows of grid but using DockPanel you can adjust the alignments by nesting each elements. But again it totally depends on the designer how they design that using grid row or any other way. What i got from the question the following code should help you out.
but if you post some code then we can figure out where the actual problem is.
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<ListBox Height="50"/>
</DockPanel>
<DockPanel DockPanel.Dock="Bottom">
<Button Height="20" DockPanel.Dock="Top"/>
<Label/>
</DockPanel>
</DockPanel>
</Grid>
So your problem here is that you have the in the ListBox you have ScrollViewer.VerticalScrollbars=Visible that make it collapsed if you dont want scrollbars and i would suggest not to use margins more in the designing. if you still have scroll bars remove the height of the ListBox and make the window larger. It all depends upon the size of the window if you don't have the size and proper alignment. Refining your code
<Window xmlns:my="clr-
namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
x:Class="MyLightWpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
xmlns:local="clr-namespace:MyLightWpfApplication.ViewModel"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<DockPanel >
<DockPanel DockPanel.Dock="Top" Width="400" Height="200">
<ScrollViewer DockPanel.Dock="Top" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ListBox Name="lbStaff" ItemsSource="{Binding AutoCompleteData}"
BorderThickness="0"
Background="WhiteSmoke" >
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel >
<TextBlock HorizontalAlignment="Stretch" FontSize="16"><Run
Text="{Binding FirstName}"/> - <Run Text="{Binding Mode=OneWay,
Path=PositionString}"/></TextBlock>
<TextBlock >Τηλέφωνο <Run Text="{Binding LastName}"/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</DockPanel>
<DockPanel DockPanel.Dock="Bottom">
<Button DockPanel.Dock="Top" Height="30" Grid.Row="0">
<Button.Content>
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="16">Προσθήκη Χρήστη</TextBlock>
</StackPanel>
</Button.Content>
</Button>
<Label/>
</DockPanel>
</DockPanel>
</Grid>
</Window>
Its all dependent on how much data you have in the ListBox. I have tried this code populating listbox with 20 rows its not showing scrollbars at all
You might try auto for both and put a MaxHeight on the ListBox but you still take a chance of pushing the button off the screen. Or put both in a ScrollViewer. Or can you live with the button on top?
I solved this problem using a dockpanel. I docked buttons at bottom and then i let listbox to fill the remaining area..(lastchildfill=true) ...
It works fine...

Silverlight - Applying Converters within Templates

I am currently updating the template of a ListPicker. In particular, I'm trying to style the contents of the full mode popup. This information appears to be defined in the following code:
<Popup x:Name="FullModePopup">
<Border Background="{StaticResource PhoneChromeBrush}">
<!-- Popup.Child should always be a Border -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{TemplateBinding FullModeHeader}"
Foreground="{StaticResource PhoneForegroundBrush}"
FontFamily="{StaticResource PhoneFontFamilySemiBold}"
FontSize="{StaticResource PhoneFontSizeNormal}"
HorizontalAlignment="Left" Margin="24 12 0 0"/>
<ListBox x:Name="FullModeSelector" Grid.Row="1"
FontSize="{TemplateBinding FontSize}"
Margin="{StaticResource PhoneMargin}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
<!-- Ensures all containers will be available during the Loaded event -->
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</Border>
</Popup>
My challenge is, I need to trim the text of each item bound in this popup list. More importantly, I need to do this with a converter. Is this even possible? How do I use a converter in this template? Traditionally, I've used something like:
<TextBlock Text="{Binding Path=Name, Converter={StaticResource myConverter}}" />
How do I apply a Converter to the items in the Popup of my ListPicker?
Thank you!
Is this what you are looking for?
Basically overriding the itemtemplate, putting in a textblock and apply your converter to the binding.
Like this
<ListBox ItemsSource="{StaticResource customers}" Width="350" Margin="0,5,0,10">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Padding="5,0,5,0"
Text="{Binding FirstName, Converter={StaticResource yourConverter}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is the msdn documentation
HTH

Silverlight/WP7: I want to place an Button after the Listbox element which are data bound

I am fairly new to silverlight. I am developing on the windows phone platform.
I want to place a button at the end of the listbox entries which will be bound to data from the webservice (I am using a listbox template)
List item 1
List item 2
List item 3
List item 4
List item 5
List item 6
List item 7
List item 8
..Button..
I tried number of things of using grid/stackpanel etc to host the button and all my solutions place the button at the bottom of my screen instead of bottom of all the listbox entries which might span multiple screens.
XAML file I have is below. I want to add a button below the "LBoxItems"
<Grid x:Name="LayoutRoot"
Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="ads" >
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Margin="24,24,0,12"
x:Name="SearchTitle">
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold"
FontSize="{StaticResource PhoneFontSizeLarge}"
Text="{Binding Location}" />
<TextBlock FontSize="{StaticResource PhoneFontSizeLarge}"
Text=" > " />
<TextBlock FontWeight="Bold"
FontSize="{StaticResource PhoneFontSizeLarge}"
Text="{Binding Category}" />
</StackPanel>
<TextBlock FontSize="{StaticResource PhoneFontSizeMedium}"
Text="{Binding Converter={StaticResource SearchHistoryItemSubTitleConverter}}" />
</StackPanel>
</Grid>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentGrid"
Grid.Row="2">
<ListBox x:Name="LBoxItems"
HorizontalAlignment="Left"
Margin="24, 0"
SelectionChanged="LBoxItems_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="{StaticResource PhoneTouchTargetOverhang}" >
<TextBlock FontSize="{StaticResource PhoneFontSizeMediumLarge}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" Foreground="{StaticResource PhoneAccentBrush}"
Text="{Binding Title.Text}" TextWrapping="Wrap" Margin="-4,20,0,0">
</TextBlock>
<TextBlock Text="{Binding PublishDate, Converter={StaticResource ItemPublishDateConverter}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
<l:SpinnerControl x:Name="SpinnerControl"
Width="55"
Height="55"
Grid.RowSpan="2" />
<TextBlock x:Name="TxtNoResultsMessage"
FontSize="{StaticResource PhoneFontSizeLarge}"
Text="No results found"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Grid.RowSpan="2"
Visibility="Collapsed" />
</Grid>
You could use a ScrollViewer:
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="800"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0">
</ListBox>
<Button Grid.Row="1" Height="30" Content="Test"></Button>
</Grid>
</ScrollViewer>
Simply divide the Grid inside into two rows, the second one being for the Button.
For your specific case it would be:
<Grid x:Name="ContentGrid"
Grid.Row="2">
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="600"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ListBox x:Name="LBoxItems"
HorizontalAlignment="Left"
Margin="24, 0"
SelectionChanged="LBoxItems_SelectionChanged" Grid.Row="0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="{StaticResource PhoneTouchTargetOverhang}" >
<TextBlock FontSize="{StaticResource PhoneFontSizeMediumLarge}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" Foreground="{StaticResource PhoneAccentBrush}"
Text="{Binding Title.Text}" TextWrapping="Wrap" Margin="-4,20,0,0">
</TextBlock>
<TextBlock Text="{Binding PublishDate, Converter={StaticResource ItemPublishDateConverter}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Sample" Height="30" Grid.Row="1" />
</Grid>
</ScrollViewer>
</Grid>
Of course, you can set the appropriate height depending on your situation.
What you want to do, really is to mix and match different items in the DataTemplate. If you're not really happy with how the other solutions work out I might consider adding a button to the datatemplate, but set its visibility to collapsed. Then for the very last entry, set its visibility to visible.
Either way you are in for a bit of a hack, but this way the button is in your list box. If all the button events point to the same handler, and only one is visible, you should be good to go.
I had the same problem and tried using the ScrollViewer approach, however I couldn't get rid of the 'stuck scrolling' issue until I disabled the vertical scrollbar on the listbox. Maybe this will help others too.

Resources