WPF 列表控件滚动到当前视图

  • 框架使用.NET40
  • Visual Studio 2022;
  • 后台选中数据后列表控件滚动到当前所在行的几种方法
    • 1.可通过 Behavior 行为实现。
    • 2.可通过附加属性去调用 ScrollIntoView 方法实现 DataGrid ListBox ListView 滚动到视图。.

方式一

1)ScrollBehavior 实现如下:

  • 找到 System.Windows.Interactivity.dll文件并引入
public class ScrollBehavior : Behavior<ListBox>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.SelectionChanged += new SelectionChangedEventHandler(AssociatedObject_SelectionChanged);
        }
        void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (sender is ListBox listBox)
            {
                if (listBox.SelectedItem != null)
                {
                    listBox.Dispatcher.BeginInvoke((Action)delegate
                    {
                        listBox.UpdateLayout();
                        listBox.ScrollIntoView(listBox.SelectedItem);
                    });
                }
            }
        }

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

2)Xaml 使用如下,即可完成跳转:

  • 引入命名空间;
  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
  • 使用如下:
 <ListBox HorizontalAlignment="Center" Margin="20">
   <i:Interaction.Behaviors>
      <local:ScrollBehavior/>
   </i:Interaction.Behaviors>
  </ListBox>

方式二

1)新建 ScrollIntoView 如下:

using System;
using System.Windows;
using System.Windows.Controls;

namespace WPFDevelopers.Helpers
{
    public class ScrollIntoView
    {
        public static readonly DependencyProperty IsPositionProperty = DependencyProperty.RegisterAttached(
          "IsPosition",
          typeof(object),
          typeof(ScrollIntoView),
          new PropertyMetadata(default(object), OnIsPositionChanged));

        public static object GetIsPosition(DependencyObject target)
        {
            return (object)target.GetValue(IsPositionProperty);
        }

        public static void SetIsPosition(DependencyObject target, object value)
        {
            target.SetValue(IsPositionProperty, value);
        }

        static void OnIsPositionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var type = sender.GetType();
            switch (type)
            {
                case Type _ when type == typeof(DataGrid):
                    var dataGrid = (DataGrid)sender;
                    if (dataGrid == null)
                        return;
                    dataGrid.SelectionChanged += delegate
                    {
                        if (dataGrid.SelectedItem == null) return;
#if NET40
                        dataGrid.Dispatcher.BeginInvoke((Action)(() =>
                        {
                            dataGrid.UpdateLayout();
                            dataGrid.ScrollIntoView(dataGrid.SelectedItem, null);
                        }));
#else                   
                    dataGrid.Dispatcher.InvokeAsync(() =>
                    {
                        dataGrid.UpdateLayout();
                        dataGrid.ScrollIntoView(dataGrid.SelectedItem, null);
                    });
#endif
                    };
                    break;
                case Type _ when type == typeof(ListBox):
                    var listBox = (ListBox)sender;
                    if (listBox == null)
                        return;
                    listBox.SelectionChanged += delegate
                    {
                        if (listBox.SelectedItem == null) return;
#if NET40
                        listBox.Dispatcher.BeginInvoke((Action)(() =>
                        {
                            listBox.UpdateLayout();
                            listBox.ScrollIntoView(listBox.SelectedItem);
                        }));
#else                   
                    listBox.Dispatcher.InvokeAsync(() =>
                    {
                        listBox.UpdateLayout();
                        listBox.ScrollIntoView(listBox.SelectedItem);
                    });
#endif
                    };
                    break;
                case Type _ when type == typeof(ListView):
                    var listView = (ListView)sender;
                    if (listView == null)
                        return;
                    listView.SelectionChanged += delegate
                    {
                        if (listView.SelectedItem == null) return;
#if NET40
                        listView.Dispatcher.BeginInvoke((Action)(() =>
                        {
                            listView.UpdateLayout();
                            listView.ScrollIntoView(listView.SelectedItem);
                        }));
#else                   
                    listView.Dispatcher.InvokeAsync(() =>
                    {
                        listView.UpdateLayout();
                        listView.ScrollIntoView(listView.SelectedItem);
                    });
#endif
                    };
                    break;
                default:
                    break;
            }
        }

    }
}

2)SelectorExample.xaml 实例代码如下:

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.SelectorExample"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
             xmlns:vm="clr-namespace:WPFDevelopers.Samples.ViewModels"
             xmlns:model="clr-namespace:WPFDevelopers.Sample.Models"
             xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
             
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Margin="4">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="0,4"
                    HorizontalAlignment="Center">
            <TextBox wd:ElementHelper.IsWatermark="True"
                     wd:ElementHelper.Watermark="输入数字0~199"
                     x:Name="MyTextBox"/>
            <Button 
                Style="{StaticResource DangerPrimaryButton}"
                Content="定位行"
                Click="Button_Click"
                Margin="4,0"/>
        </StackPanel>
       
        <UniformGrid Rows="2" Columns="2" Grid.Row="1">
            <DataGrid ItemsSource="{Binding ListArrays,RelativeSource={RelativeSource AncestorType=UserControl}}"
                      SelectedItem="{Binding SelectItem,RelativeSource={RelativeSource AncestorType=UserControl}}"
                      wd:ScrollIntoView.IsPosition="true"
                      Margin="4" IsReadOnly="True">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
                    <DataGridTextColumn Header="Date" Binding="{Binding Date}" Width="Auto"/>
                </DataGrid.Columns>
            </DataGrid>
            <ListBox ItemsSource="{Binding ListArrays,RelativeSource={RelativeSource AncestorType=UserControl}}"
                     SelectedItem="{Binding SelectItem,RelativeSource={RelativeSource AncestorType=UserControl}}"
                     wd:ScrollIntoView.IsPosition="true"
                     Margin="4">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <UniformGrid Columns="2">
                            <TextBlock Text="{Binding Name}"/>
                            <TextBlock Text="{Binding Date}"/>
                        </UniformGrid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
            <ListView ItemsSource="{Binding ListArrays,RelativeSource={RelativeSource AncestorType=UserControl}}"
                      SelectedItem="{Binding SelectItem,RelativeSource={RelativeSource AncestorType=UserControl}}"
                      wd:ScrollIntoView.IsPosition="true"
                      Margin="4">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
                        <GridViewColumn Header="Date" DisplayMemberBinding="{Binding Date}" />
                    </GridView>
                </ListView.View>
            </ListView>
        </UniformGrid>
    </Grid>
</UserControl>

3)SelectorExample.xaml.cs 实例代码如下:

using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Collections.ObjectModel;
using WPFDevelopers.Sample.Models;
using System;
using System.Linq;

namespace WPFDevelopers.Samples.ExampleViews
{
    /// <summary>
    /// SelectorExample.xaml 的交互逻辑
    /// </summary>
    public partial class SelectorExample : UserControl
    {
        public ObservableCollection<UserModel> ListArrays
        {
            get { return (ObservableCollection<UserModel>)GetValue(ListArraysProperty); }
            set { SetValue(ListArraysProperty, value); }
        }

        public static readonly DependencyProperty ListArraysProperty =
            DependencyProperty.Register("ListArrays", typeof(ObservableCollection<UserModel>), typeof(SelectorExample), new PropertyMetadata(null));


        public UserModel SelectItem
        {
            get { return (UserModel)GetValue(SelectItemProperty); }
            set { SetValue(SelectItemProperty, value); }
        }

        public static readonly DependencyProperty SelectItemProperty =
            DependencyProperty.Register("SelectItem", typeof(UserModel), typeof(SelectorExample), new PropertyMetadata(null));


        public SelectorExample()
        {
            InitializeComponent();
            Loaded += SelectorExample_Loaded;
        }

        private void SelectorExample_Loaded(object sender, RoutedEventArgs e)
        {
            ListArrays = new ObservableCollection<UserModel>();
            for (int i = 0; i < 200; i++)
            {
                ListArrays.Add(new UserModel { Name = i.ToString(), Date = DateTime.Now.AddDays(i) });
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var index = 150;
            if (string.IsNullOrWhiteSpace(MyTextBox.Text) || !int.TryParse(MyTextBox.Text, out index))
                index = 150;
            SelectItem = ListArrays.FirstOrDefault(i => i.Name == index.ToString());
        }
    }
}
WPF 列表控件滚动到当前视图