Home > Net >  WinUI 3. Programmatically scroll listview using MVVM
WinUI 3. Programmatically scroll listview using MVVM

Time:09-15

I'm trying to programmatically scroll the content of a listview, using two buttons, (one for each direction) and the MvvM pattern.

So far all other solutions i could find made use of x:Name and the code behind to access the listview and call ScrollIntoView(Object) function. I thought i could make use of the VisualTreeHelper and the FindChildren<T>, however this also requires the x:Name to find and return the listview object for my ViewModel to use.

Xaml:

<!-- ListView to scroll through: -->
<ListView
    SelectionMode="None"
    ItemsSource="{Binding ListOfListItems}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="UcViewModel:ListItemViewModel">
                <Uc:ListItemUserControl/>
            </DataTemplate>
        </ListView.ItemTemplate>
</ListView>

<!-- One of my two buttons: -->
<Button
    Content="Scroll up"
    Command="{Binding ScrollUp}"/>

ViewModel:

private DelegateCommand _scrollUp;
public ICommand ScrollUp => _scrollUp ??= new DelegateCommand(PerformScrollUp);

private void PerformScrollUp(object commandParameter)
{
    //Here i want to call ListView.BringIntoView(NextItem)
}

The MVVM library I'm using is the: Microsoft.Toolkit.Mvvm

I've tried reading through documentation as well, but as far as i understand i need to find a way to access the ListView Object in my ViewModel, and I'm unable to figure out how I would achieve this. I am somewhat new both to WinUI 3 and C#, so please say if there is any missing info, and I'll do my best to provide what is needed.

Edit: As I'm using DI for all pages and ViewModels, I do believe it's impossible to simply inject the listview into the ViewModels constructor in the code behind, using the x:Name. That said I'm looking to keep the code-behind as untouched as possible, to follow the MVVM pattern.

CodePudding user response:

After having put my problem a bit behind me and looked at it with new eyes, i found the following solution. However i'm unaware about its efficiency, and / or if it is bad coding principle.

What i do, is passing the control to the viewmodel via the "CommandParameter", and binding it with x:Bind to the required controls name. This technicaly uses bindings to the code-behind, however the code-behind it self is left unmodified.

In the xaml:

<ScrollViewer
   x:Name = "TargetScrollViewer">
   <ListView>
      //Disables listviews internal scrollviewer
      //Impliment rest of ListView code
   </ListView>
</ScrollViewer>

<Button
   Command = "{Binding ScrollUp}"
   CommandParameter = "{x:Bind TargetScrollViewer}"/>

<Button
   Command = "{Binding ScrollDown}"
   CommandParameter = "{x:Bind TargetScrollViewer}"/>

Many seems to recommend using the listviews "ScrollIntoView", If this is the case just parse the Listview rather than the scrollviewer as the command parameter.

And inside the viewmodel:

private DelegateCommand _scrollDown;
public ICommand ScrollDown => _scrollDown ??= new DelegateCommand(PerformScrollDown);

private void PerformScrollDown(object commandParameter)
{
    var scrollcontrol = commandParameter as ScrollViewer;
    scrollcontrol.ScrollToVerticalOffset(scrollcontrol.VerticalOffset   72);
}

Inside the function I simply cast the command parameter back to a Scrollviewer, and call the required functions.

In my case I'm using "Syncfusion" for the DelegateCommand to bind buttons to the view.

  • Related