WinUI 3.使用MVVM以编程方式滚动列表视图



我正在尝试使用两个按钮(每个方向一个(和MvvM模式以编程方式滚动列表视图的内容。

到目前为止,我能找到的所有其他解决方案都使用了x:Name和后面的代码来访问listview并调用ScrollIntoView(Object)函数。我认为我可以使用VisualTreeHelper和FindChildren<T>,但是这也需要x:Name查找并返回listview对象以供ViewModel使用。

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)
}

我使用的MVVM库是:Microsoft.Toolkit.Mvvm

我也尝试过阅读文档,但据我所知,我需要找到一种方法来访问ViewModel中的ListView对象,但我不知道如何实现这一点。我对WinUI 3和C#都有点陌生,所以如果有任何遗漏的信息,请告诉我,我会尽我所能提供所需的信息。

编辑:由于我对所有页面和ViewModels都使用DI,我确实认为不可能简单地使用x:Name将listview注入代码后面的ViewModels构造函数中。也就是说,我希望尽可能保持后面的代码不变,以遵循MVVM模式。

在把我的问题抛在脑后并用新的眼光看待它之后,我找到了以下解决方案。然而,我不知道它的效率,和/或它是否是糟糕的编码原理。

我所做的是通过";CommandParameter";,并将其与x:Bind绑定到所需的控件名称。这种技术性使用了到后台代码的绑定,但是后台代码本身是未修改的。

在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}"/>

许多人似乎建议使用listviews";滚动IntoView";,如果是这种情况,只需将Listview而不是scrollviewer解析为命令参数。

在视图模型内部:

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);
}

在函数内部,我只需将命令参数强制转换回Scrollviewer,并调用所需的函数。

在我的情况下,我使用";Syncfusion";用于DelegateCommand将按钮绑定到视图。

在我的ViewModel中,除了实现ItemsSource,我还实现了SelectedItem和属性绑定到我的ListView。在下面的例子中,我使用MVVM模式来实现一个类似控制台的应用程序(控制台的更新会将行附加到集合中,并更新所选行/索引(:

public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<string> ConsoleLines = new ObservableCollection<string>();
public string? SelectedConsoleLine = null;
public int SelectedConsoleIndex = -1;
public event PropertyChangedEventHandler PropertyChanged;
}

然后,在我的XAML中,我可以配置我的ListView:

<ListView x:Name="ConsoleView"
ItemTemplate="{StaticResource consoleTemplate}"
ItemsSource="{Binding ConsoleLines}"
SelectedItem="{Binding SelectedConsoleLine,Mode=TwoWay}">
</ListView>

在视图的代码中,我们监听ViewModel的PropertyChange事件来实现滚动,例如

public MainPage()
{
BindingContext = viewModel = new MainViewModel();
InitializeComponent();
PrimesButton.Clicked += PrimesButton_Clicked;
ClearButton.Clicked += ClearButton_Clicked;
viewModel.PropertyChanged += ViewModel_PropertyChanged;
}
private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
// ConsoleView is ListView
case nameof(viewModel.SelectedConsoleLine):
ConsoleView.ScrollTo(viewModel.SelectedConsoleLine, ScrollToPosition.MakeVisible, true); // ListView
break;
// ConsoleView is CollectionView
//case nameof(ViewModel.SelectedConsoleIndex):
//    ConsoleView.ScrollTo(viewModel.SelectedConsoleIndex);
//    break;
}
}

最新更新