Listview Automatic Scrolling using MVVM - wpf

I'm trying to implement automatic scrolling of a Listview using MVVM. I know that you can update the listview by calling ScrollIntoView but that requires Code Behind which I'm trying to avoid.
I've bound my ListView's ItemSource to an ObservableCollection and would like the Listview to automatically scroll down to the newest item added to the log.
<ListView ItemsSource="{Binding Log}"
SelectedIndex="{Binding SelectedLine}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Grid.Row="1">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel>
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Top" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Type" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Type}" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>

MVVM and Attached Behaviors go hand in hand. You can use attached behavior to scroll to new item after the ObservableCollection.Add() takes place...
This article is a good example that tries to bring a tree view item into scroll view while sticking to MVVM.

Related

Disable ListView selection and hover

Usually I can disable ListView selection doing like this thread suggests - or something similar where you set the ItemContainerStyle.
However I have this ListView that is defined like this:
<ScrollViewer>
<ListView ItemsSource="{Binding List, Mode=OneWay}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumnHeader Style="{StaticResource header}"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<!-- Some data -->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridViewColumn>
...
</GridView>
</ListView.View>
</ListView>
</ScrollViewer>
And if I try to specify the ItemContainerStyle - the data in the list just disappears.
Giving the above ListView, how would I proceed just to removed the selection?
Can you try this ItemContainerStyle? This basically will not pick up any input events.
<ListView ItemsSource="{Binding List, Mode=OneWay}">
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListViewItem}}">
<Setter Property="IsHitTestVisible" Value="False"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>

WPF - Can't right-align a TextBlock when using StringFormat

I have a TextBlock within a GridViewColumn. I format to currency using StringFormat. This causes the field to be left aligned. I can't get it right aligned. Tried HorizontalAlignment and TextAlignment but nothing works.
<TextBlock HorizontalAlignment="Right" Text="{Binding Path=Amount, StringFormat={}{0:€ # ##0}}" />
<TextBlock TextAlignment="Right" Text="{Binding Path=Amount, StringFormat={}{0:€ # ##0}}" />
If I do this from this post, it works but right aligns all columns. I want numbers right aligned, dates centre and text left.
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Right" />
</Style>
</ListView.Resources>
Any clues please?
Set HorizontalContentAlignment to Stretch. This will work:
<ListView>
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Right"
Text="{Binding Path=Amount, StringFormat={}{0:€ # ##0}}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Change your resource to HorizontalContentAlignment to Stretch, this will allow the contents of each cell in the ListView to take up the entire space. Then your HorizontalAlignment attributes will work.

Matching vertical layout in different ItemsControls

I have ListView that uses a GridView to display several columns of data.
Two of the columns have cell templates containing ItemControl's to show a small collection in each cell.
The problem I have is that the vertical size of the ItemControls is not connected in any way since they are in different cells, and therefore they don't line up.
eg:(note this is ONE ROW in the parent listview)
XAML so far:
<ListView Margin="6,6,6,0" ItemsSource="{Binding Controllers}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" >
<Setter Property="VerticalContentAlignment" Value="Top" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<!-- snip other columns -->
<GridViewColumn Header="Mode">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Modes}" DisplayMemberPath="Name"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Parameters">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Modes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Blah:" Margin="5,0"/>
<ComboBox>
<ComboBoxItem>Hello</ComboBoxItem>
</ComboBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
My first thought was to use a grid as the ItemsPanel of the ItemsControls, so I could use SharedSizeScope on the rows to line them up. However, I can see no way of assigning items dynamically created by the ItemsControl to specific Grid rows.
Can anyone suggest a better solution?
Managed to fix this by setting ListView.ItemContainerStyle.VerticalContentAlignment to Stretch, and then using UniformGrid's for the ItemsControl panels:
Each cell now stretches vertically to match the largest cell, and using UniformGrid splits that vertical size evenly for each item.
eg
<GridViewColumn Header="Mode">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Modes}" DisplayMemberPath="Name">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

Is there any simplier/better way to auto-size the content of one-column ListView?

Alright, sorry for my english in advance, here what I need:
I got a ListView in XAML (WPF) where there items is located and what I need is that items to have the width equal to the ListView's width.
So far, I did this:
<ListView Name="lvFiles" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid Width="{Binding ElementName="lvFiles", Path=ActualWidth}">
<!-- All the controls inside I need to present an item -->
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
But as it turned out, the ListView items become clipped on the right when the vertical scrollbar appears. And it appears when the total height of all items exceeds the ListView.ActualHeight, so I needed to user bindings more complex and hence more complicated.
Also, my items, while I was spending hours to solve the problem, my items sometimes were clipped on the right with no obvious reason.
So I came up with this:
<ListView Name="lvFiles" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.View>
<GridView>
<GridViewColumn Width="{Binding ElementName=lvFiles, Path=ActualWidth}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Decorator Width="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=ListViewItem},
Path=ActualWidth}">
<Grid Margin="0,0,18,0">
<!-- All the controls inside... -->
</Grid>
</Decorator>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
It solved clipping problem and also it solved the problem with one-columned ListViewItem auto-size. But now it seems to be not the simpliest way. Is there any such?
You're not using a header so you can use ListView directly without using GridView. As for the width, don't adjust the content width, but adjust the ListViewItem width.
Here's an example:
<Style x:Key="singleListViewItemStyle"
BasedOn="{StaticResource {x:Type ListViewItem}}"
TargetType="{x:Type ListViewItem}">
<Setter Property="Width"
Value="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type VirtualizingStackPanel}}}" />
</Style>
If you do not have an existing style targeting ListViewItem or do not want to inherit from it, you can remove BasedOn="{StaticResource {x:Type ListViewItem}}".
AncestorType={x:Type VirtualizingStackPanel} is used because by default, that is what ListView displays its content in. If you have your own ListView theme, then see the following example:
<ListView Name="myListView"
ItemContainerStyle="{StaticResource ResourceKey=singleListViewItemStyle}"
ItemsSource="{Binding Path=MyItems}"
SelectedItem="{Binding Path=MySelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<!--Your favorite controls-->
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is the link for a demo project I made for you. You can also see my other controls.
I hope this helps.

Make a multi column button with a Gridview

I have the following XAML. I want one button per row to encompass all the data. Is this possible?
<ListView Name="ClosestListView">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="ShapeFileType" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ShapeFileType}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Address" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=fullMatch}" MinWidth="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
... more columns and closing tags
If you're just looking to add a Click event to your Row, you would probably be better off adding a Click event to your ListViewItem or GridViewCell
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="HandleDoubleClick" />
</Style>
If you want to bind the click event to a Command in your ViewModel, I'd suggest using the AttachedCommandBehavior found here
And if you really want a button that spams all columns, I would recommend looking into Grid.SharedSizeGroup to align your data
I guess its more logical to just handle selectionchanged event.
<ListView Name="ClosestListView" SelectionChanged="ClosestListView_SelectionChanged">

Resources