【问题标题】:WPF, how do I trigger ListBox.ScrollIntoView using MVVM pattern?WPF,如何使用 MVVM 模式触发 ListBox.ScrollIntoView?
【发布时间】:2019-01-24 19:20:54
【问题描述】:

我有一个 WPF 应用程序,其中 ListBox 的内容会在用户按下按钮时更新。我最初的问题是将 ListBox 重新聚焦到特定的 SelectedIndex 值,该值绑定到我的 ViewModel 中的 ActiveItem 属性。我能够使用以下代码解决此问题:

XAML:

<ListBox ItemsSource="{Binding ListOfItems}" SelectedIndex="{Binding ActiveItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                 IsSynchronizedWithCurrentItem="True"  SelectionChanged="ListBox_SelectionChanged" x:Name="ListBoxSelector">

代码隐藏:

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ListBoxSelector.ScrollIntoView(ListBoxSelector.SelectedItem);
    }

但是,我目前在 MainWindow.xaml.cs 文件而不是我的 ViewModel 中有上述代码隐藏。我的问题是如何将此代码移动到 ViewModel 以便我可以坚持 MVVM 模式?我不太清楚如何从 ViewModel 正确处理 ListBox 的 ScrollIntoView 属性。

【问题讨论】:

  • 我认为没有办法。通常,MVVM 的 VM 部分响应数据绑定,而不是 UI 控制。您想要做的事情特定于 WPF,因此,如果您将视图模型移动到另一个项目(例如 Xamarin) - 您想要访问的代码将不起作用。当然,您可以将列表框作为 ViewModel 构造函数的参数传递,并在绑定属性更改时滚动到视图中,但这不是最好的主意
  • 如果您使用的是 MVVM light,您可以考虑使用消息来通知您要滚动查看的窗口。

标签: c# wpf xaml mvvm


【解决方案1】:

您可以使用Behavior 类强制所选项目滚动到视图中。

public class perListBoxHelper : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
        base.OnDetaching();
    }

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

        if (listBox?.SelectedItem == null)
            return;

        Action action = () =>
        {
            listBox.UpdateLayout();

            if (listBox.SelectedItem != null)
                listBox.ScrollIntoView(listBox.SelectedItem);
        };

        listBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
    }
}

更多详情请关注我的blog post

另外,我个人会绑定到 ListBox 的 SelectedItem 而不是 SelectedIndex,并在该属性设置器中处理对项目选择的任何处理,而不是使用事件处理程序。

【讨论】:

  • 行为确实是 MVVM 中这类问题的最佳解决方案。
【解决方案2】:

MVVM 模式并不排除使用背后的代码。事实上,它不能,因为在某些情况下,代码背后是正确的做法。视图模型是模型,它不是视图替换。另一种思考方式是视图模型包含what,视图包含how。至少,这是我对 MVVM 模式的解读。

在您的情况下,what 是项目列表和当前项目。 howListBox 本身及其默认行为。在我看来,将所选项目滚动到视图中是一种附加行为,因此应该保留在视图中。您没有违反 MVVM 模式,因为您将 what 保留在视图模型中,并将 how 保留在视图中。

【讨论】:

  • 感谢您的回复。我刚刚从我看过的 MVVM 教程中得到印象,在 MainWindow.xaml.cs 文件中包含代码是一种不好的做法。
  • @alduin 我会说在后面的代码中包含某种代码是不好的做法,但不是全部。您绝对不希望在您的代码中包含业务逻辑。在我看来,您甚至不应该在视图模型中包含业务逻辑。
  • 感谢您的建议。对 MVVM 模式来说还是很新的,所以我试图通过我的方式。
【解决方案3】:

我不确定我的解决方案是否是 MVVM 模式。但是对于这样的问题,它可以解决问题。

这是我要做的:如果按下按钮,它将触发一个命令来调用 ViewModel 中的方法。当 ViewModel 完成它的工作时,viewModel 会抛出一个自定义事件(其中包括列表框应该滚动到的项目索引)。而在此之前,当 View 被加载时,View 的 Code-behind 应该通过 View 的 DataContext 监听它的 ViewModel,并做 scrollIntoView。

正如我所说,我不确定它是否是 MVVM 方式,但我认为这是可以接受的。

【讨论】:

    【解决方案4】:

    对于这方面的每个请求都没有通用的解决方案,正如其他人所提到的,MVVM 并不意味着没有代码,但也没有不必要的代码。

    但是,在您的特定请求中,如果您不希望隐藏任何代码,则有解决方案 - 创建一个继承自 ListView 的类并按照您希望的方式处理请求,然后在您的 XAML 中使用它。

    【讨论】:

      猜你喜欢
      • 2016-07-12
      • 2010-12-19
      • 2019-04-05
      • 1970-01-01
      • 2011-03-29
      • 1970-01-01
      • 1970-01-01
      • 2011-12-05
      • 2014-05-12
      相关资源
      最近更新 更多