0

I've got a project where there is a ListView and when the User clicks the New button the new Object is added to the bottom of the ListView. I've tried using a Content Style class but that didn't work. I just need something that will scroll to the selected item. Below is my code:

View:

<ListView Margin="103,0,0,10" ScrollViewer.CanContentScroll="True"  Height="87" SelectedItem="{Binding SelectedSession, Mode=TwoWay}" ItemsSource="{Binding SessionCollection}">
    <ListView.Resources>
        <Style TargetType="{x:Type GridViewColumnHeader}">
            <Setter Property="HorizontalContentAlignment" Value="Left" />
        </Style>
    </ListView.Resources>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Session Name" Width="180"  DisplayMemberBinding="{Binding SessionName, Mode=TwoWay}" />
            <GridViewColumn Header="Operator Name" Width="180" DisplayMemberBinding="{Binding OperatorName, Mode=TwoWay}"/>
            <GridViewColumn Header="Session Date" Width="180" DisplayMemberBinding="{Binding SessionDate, Mode=TwoWay}"/>
        </GridView>
    </ListView.View>
</ListView>

View Model code for the New Button :

public void NewSession()
{
    Session newSession = new Session();

    SessionCollection.Add(newSession);
    SelectedSession = newSession;
    SessionDate = DateTime.Now;
    StartTime = DateTime.Now;
    EndTime = DateTime.Now;
    ProjectManager.Instance.CurrentSession = null;
}

public ObservableCollection<Session> SessionCollection
{
    get
    {
        if (currentDatabaseProj.Sessions == null)
        {
            return currentDatabaseProj.Sessions;
        }
        else
        {
            return currentDatabaseProj.Sessions;
        }
    }
    private set
    {
        currentDatabaseProj.Sessions = value;
        IsNewProjClicked = true;
        this.RaisePropertyChanged("SessionCollection");
    }
}
Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
yams
  • 942
  • 6
  • 27
  • 60

2 Answers2

1

One simple handler in the code behind should do the trick (i simplify your code to made it clear)

    <Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListView x:Name="listView" Margin="10" ScrollViewer.CanContentScroll="True"   SelectedItem="{Binding SelectedSession, Mode=TwoWay}" ItemsSource="{Binding SessionCollection}">
        <ListView.Resources>
            <Style TargetType="{x:Type GridViewColumnHeader}">
                <Setter Property="HorizontalContentAlignment" Value="Left" />
            </Style>
        </ListView.Resources>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Session Name" Width="180"  DisplayMemberBinding="{Binding}" />   
            </GridView>
        </ListView.View>
    </ListView>
    <Button Click="UIElement_NewElementHandler" Grid.Row="1" Content="Add Item" Command="{Binding AddItem}"></Button>
</Grid>

and in the view's code behind add the following handler :

  private void UIElement_NewElementHandler(object sender, RoutedEventArgs routedEventArgs)
    {            
        var border = VisualTreeHelper.GetChild(listView, 0) as Decorator;            
        var scrollViewer = border.Child as ScrollViewer;
        scrollViewer.ScrollToBottom();
    }

ps: scrolling down to a new added item is something ViewRelated, none of the View Model Properties are used in the codebehind, so i believe you are not violating any mvvm rule by doing that.

SamTh3D3v
  • 9,854
  • 3
  • 31
  • 47
0

Working example with behavior

xaml

<Window x:Class="ListView.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:listView="clr-namespace:ListView"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Content="Add New" Grid.Row="0" Click="Button_Click"></Button>
    <ListView Grid.Row="1" ItemsSource="{Binding MySessionList}" SelectedItem="{Binding SelectedSession, Mode=TwoWay}">
        <i:Interaction.Behaviors>
            <listView:ScrollIntoViewBehavior/>
        </i:Interaction.Behaviors>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Name" Width="200" DisplayMemberBinding="{Binding Name, Mode=TwoWay}" />
            </GridView>
        </ListView.View>
    </ListView>
</Grid>
</Window>

cs

public partial class MainWindow : Window
{
    private Viewmodel _data;
    public MainWindow()
    {
        InitializeComponent();
        _data = new Viewmodel();
        this.DataContext = _data;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        _data.AddNew();
    }
}

public class Viewmodel : INotifyPropertyChanged
{
    private MySession _selectedSession;
    public ObservableCollection<MySession> MySessionList { get; set; }

    public Viewmodel()
    {
        this.MySessionList = new ObservableCollection<MySession>();

        //fill with some data
        for (int i = 0; i < 20; i++)
        {
            this.MySessionList.Add(new MySession(){Name =  "Index : " + i});
        }
    }

    public MySession SelectedSession
    {
        get { return _selectedSession; }
        set
        {
            _selectedSession = value; 
            OnPropertyChanged();
        }
    }

    //should be a Command for MVVM but for quick and dirty
    public void AddNew()
    {
        var toAdd = new MySession() {Name = "New Added " + DateTime.Now.ToLongTimeString()};
        this.MySessionList.Add(toAdd);
        this.SelectedSession = toAdd;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MySession
{
    public string Name { get; set; }
}

//ADD REFERENCE:  System.Windows.Interactivity
public class ScrollIntoViewBehavior : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
    }

    private void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var ctrl = sender as ListBox;

        if (ctrl == null)
            return;


        if (ctrl.SelectedItem != null)
        {
            ctrl.Dispatcher.BeginInvoke(
                DispatcherPriority.Render,
                new Action(() =>
                {
                    ctrl.UpdateLayout();
                    ctrl.ScrollIntoView(ctrl.SelectedItem);
                }));
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
    }
}
blindmeis
  • 22,175
  • 7
  • 55
  • 74
  • One question will my code for the New Session where the button is clicked throw a wrench in this? Where I create a new Session Object and set SelectedSession = newSession;? – yams Jan 21 '15 at 16:01
  • Also where do I get the Behaviors add on I am getting an error in my xaml on that ScrollIntoViewBehavior is not supported in wpf? – yams Jan 21 '15 at 16:07
  • I understand that but Visual Studio 2013 is giving me the error that Behaviors.Scrollintoviewbehavior is not supported. – yams Jan 21 '15 at 21:08
  • Yeah on .net 4.5 it isn't. Hmm – yams Jan 22 '15 at 18:09
  • Yes but the xaml code for the Behavior is throwing a error. – yams Jan 29 '15 at 18:09
  • check the namespace (xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity") in your xaml and dont forget to add a reference to the system.windows.interactivity.dll from the Blend SDK (https://www.nuget.org/packages/Expression.Blend.Sdk/) – blindmeis Jan 29 '15 at 18:27