WPF ListView从视图模型滚动

时间:2014-12-01 21:36:38

标签: c# wpf listview

我在视图模型中有一个名为(Users)的可观察集合,它与视图中的ListViewControl(lstUsers)绑定,我需要的是在列表视图中滚动到当前登录用户。 我在大多数示例中看到,使用后面的代码滚动如下所示。 :

lstUsers.ScrollIntoView(lstUsers[5]);

但我需要的是从视图模型中处理它。

请指教!

3 个答案:

答案 0 :(得分:1)

这样做的一种方法是使用具有当前项目的ICollectionView。然后,您可以将IsSynchronizedWithCurrentItem设置为true,以将视图模型中的当前项链接到ListView中的选定项。

最后在视图后面的代码中处理事件SelectionChanged以更改滚动位置,以便它始终显示所选项目。

对我来说,这种方法的好处是视图模型不会意识到视图的任何内容,这是MVVM的目标之一。视图背后的代码是关于视图的任何代码的理想位置。

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListView x:Name="View"
              SelectionChanged="Selector_OnSelectionChanged" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Items}"/>
    <Button Grid.Row="1" Command="{Binding ChangeSelectionCommand}">Set</Button>       
</Grid> 


public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }

    private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        View.ScrollIntoView(View.SelectedItem);
    }
}

public class ViewModel
{
    private readonly CollectionViewSource _source = new CollectionViewSource();

    public ICollectionView Items
    {
        get { return _source.View; }
    }

    public ICommand ChangeSelectionCommand { get; set; }

    public ViewModel()
    {
        SetUp();
        ChangeSelectionCommand = new Command(ChangeSelection);
    }

    private void SetUp()
    {
        var list = new List<string>();
        for (int i = 0; i < 100; i++)
        {
            list.Add(i.ToString(CultureInfo.InvariantCulture));
        }
        _source.Source = list;
    }

    private void ChangeSelection()
    {
        var random = new Random(DateTime.Now.Millisecond);

        var n = random.Next(100);

        Items.MoveCurrentToPosition(n);
    }
}

public class Command : ICommand
{
    private readonly Action _action;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _action();
    }

    public event EventHandler CanExecuteChanged;

    public Command(Action action)
    {
        _action = action;
    }
}

答案 1 :(得分:0)

让我与您分享我的解决方案

  1. 使用依赖项属性TargetListItem创建自己的ListView后代

    public class ScrollableListView : ListView
    {
        /// <summary>
        /// Set this property to make ListView scroll to it
        /// </summary>
        public object TargetListItem
        {
            get { return (object)GetValue(TargetListItemProperty); }
            set { SetValue(TargetListItemProperty, value); }
        }
    
        public static readonly DependencyProperty TargetListItemProperty = DependencyProperty.Register(
            nameof(TargetListItem), typeof(object), typeof(ScrollableListView), new PropertyMetadata(null, TargetListItemPropertyChangedCallback));
    
        static void TargetListItemPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var owner = (ScrollableListView)d;
            owner.ScrollToItem(e.NewValue);
        }
    
        public void ScrollToItem(object value)
        {
            if (value != null && Items != null && Items.Contains(value))
            {
                ScrollIntoView(value);
            }
        }
    }
    
  2. 在ViewModel中创建属性

    object currentListItem;
    public object СurrentListItem
    {
        get => сurrentListItem;
        set
        {
            if (сurrentListItem != value)
            {
                сurrentListItem = value;
                OnPropertyChanged(nameof(СurrentListItem));
            }
        }
    }
    
  3. 绑定

    <controls:ScrollableListView ... TargetListItem="{Binding CurrentListItem}"/>

现在,您可以在需要时在ViewModel中设置CurrentListItem。相应的视觉元素将立即在ListView中可见。

也许您也可以在ListView上使用附加属性,而不是创建ScrollableListView。但是我不确定。

答案 2 :(得分:-1)

是的,当您需要获得控件时,MVVM中总会存在时间。有各种各样的方法可以做到这一点,但这是一种简单易行的方法,可以在不使用控件或搞乱路由命令或其他类似玩具的情况下使用WPF中的内容。

总结:

  • 在视图模型上创建附加属性。
  • 在XAML中设置附加属性,以将列表框传递回视图模型。
  • 按需调用.ScrollIntoView。

注意,这是一个粗略而准备好的示例,请确保在显示窗口之前设置了DataContext。

代码/视图模型:

public class ViewModel
{
    private ListBox _listBox;

    private void ReceiveListBox(ListBox listBox)
    {
        _listBox = listBox;
    }

    public static readonly DependencyProperty ListBoxHookProperty = DependencyProperty.RegisterAttached(
        "ListBoxHook", typeof (ListBox), typeof (ViewModel), new PropertyMetadata(default(ListBox), ListBoxHookPropertyChangedCallback));

    private static void ListBoxHookPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
        var listBox = (ListBox) dependencyObject;
        var viewModel = (ViewModel) listBox.DataContext;
        viewModel.ReceiveListBox(listBox);
    }

    public static void SetListBoxHook(DependencyObject element, ListBox value)
    {
        element.SetValue(ListBoxHookProperty, value);
    }

    public static ListBox GetListBoxHook(DependencyObject element)
    {
        return (ListBox) element.GetValue(ListBoxHookProperty);
    }
}

好的,这样我们就可以让ListBox传回视图了;你可以按照自己的意愿去做。

现在,只需在XAML中设置属性:

<ListBox wpfApplication1:ViewModel.ListBoxHook="{Binding RelativeSource={RelativeSource Self}}" />

很高兴去!