簡體   English   中英

WPF:在重新實例化綁定的ObservableCollection時,DataGrid上的水平ScrollViewer捕捉到右側

[英]WPF: Horizontal ScrollViewer on DataGrid snapping to right side on reinstantation of the bound ObservableCollection

我目前在WPF中遇到一個問題,當重新實例化綁定的ObservableCollection時,DataGrid的水平ScrollViewer捕捉到可能的滾動空間的右側(顯示DataGrid的最右邊的內容)。

即使觸發綁定事件時我觸發將手動將Horizo​​ntalOffset設置為0並在重新綁定列表后立即調用該事件的行為,該0也將被忽略,並且捕捉再次移至右側。 我認為這與ScrollViewer中的操作順序和命令隊列有關。

這似乎應該是默認行為(我不確定為什么您不希望在填充數據時默認情況下希望滾動條對齊到右側)。 有人知道該問題的解決方法嗎?

根據要求,復制項目中的代碼文件。

MainWindow.xaml

<Window x:Class="WpfScrollViewer.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfScrollViewer"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <Button Content="Rebind" Command="{Binding RebindCommand}"/>
        <Button Content="Clear and Set" Command="{Binding ClearCommand}"/>
    </StackPanel>

    <DataGrid ItemsSource="{Binding People}" Grid.Row="1" HorizontalScrollBarVisibility="Visible" FontSize="30">

    </DataGrid>
</Grid>

Person.cs

namespace WpfScrollViewer
{
    public class Person
    {
        public string FirstNames { get; set; }

        public string LastName { get; set; }

        public int Age { get; set; }

        public string Address { get; set; }

        public string PostCode { get; set; }

        public string PhoneNumber { get; set; }
    }
}

DelegateCommand.cs

using System;
using System.Windows.Input;

namespace WpfScrollViewer
{
    public class DelegateCommand : ICommand
    {
        private readonly Action _fn;
        private readonly Func<bool> _canExecute;

        public event EventHandler CanExecuteChanged;

        public DelegateCommand(Action fn, Func<bool> canExecute = null)
        {
            _fn = fn;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }

            return _canExecute();
        }

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

MainViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;

namespace WpfScrollViewer
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private readonly Random _random = new Random();

        private ObservableCollection<Person> _people;

        public ObservableCollection<Person> People
        {
            get => _people;
            set
            {
                if (_people == value)
                {
                    return;
                }

                _people = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(People)));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public ICommand RebindCommand { get; }

        public ICommand ClearCommand { get; }

        public MainViewModel()
        {
            RebindCommand = new DelegateCommand(RebindPeople);
            ClearCommand = new DelegateCommand(ClearAndSetPeople);
        }

        private void RebindPeople()
        {
            People = new ObservableCollection<Person>(GetPeople());
        }

        private void ClearAndSetPeople()
        {
            var people = GetPeople();
            People.Clear();
            foreach (var person in people)
            {
                People.Add(person);
            }
        }

        private List<Person> GetPeople()
        {
            var people = new List<Person>
            {
                new Person
                {
                    FirstNames = "John",
                    LastName = "Doe",
                    Address = "17 Random Street",
                    PostCode = "RN32 2JR",
                    Age = 31,
                    PhoneNumber = "07647123456"
                },
                new Person
                {
                    FirstNames = "Jane",
                    LastName = "Doe",
                    Address = "17 Random Street",
                    PostCode = "RN32 2JR",
                    Age = 30
                },
                new Person
                {
                    FirstNames = "Jack",
                    LastName = "Freens",
                    Address = "37 Badboi Lane",
                    Age = 30
                },
                new Person
                {
                    FirstNames = "Richard",
                    LastName = "Brodget",
                    Address = "69 Meme Street",
                    Age = 31
                },
                new Person
                {
                    FirstNames = "Sam",
                    LastName = "Orfitt",
                    Address = "16 Withernsea Road",
                    Age = 29
                },
                new Person
                {
                    FirstNames = "Tom",
                    LastName = "Orfitt",
                    Address = "16 Withernsea",
                    Age = 27
                }
            };

            var rmCount = _random.Next(1, 4);
            for (var i = 0; i < rmCount; i++)
            {
                people.RemoveAt(_random.Next(people.Count));
            }

            return people;
        }
    }
}

使用“重新綁定”按鈕將顯示我上面描述的行為。 確保稍微向右滾動,水平滾動條在重新綁定時將向右對齊。 如果該欄完全位於左側,則水平滾動條將正確對齊到左側,就像我希望在所有情況下一樣。

干杯

這是由列自動生成功能引起的。 每次更改數據時,它將刪除所有列(我猜這是滾動條位置丟失),並根據傳遞的數據創建新列。 如果您靜態定義列並使用AutoGenerateColumns="False"參數禁用該功能,則不會重置滾動條位置。

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding People}" Grid.Row="1" HorizontalScrollBarVisibility="Visible" FontSize="30">
    <DataGrid.Columns>
        <DataGridTextColumn Header="FirstNames" Binding="{Binding FirstNames}" />
        <DataGridTextColumn Header="LastName" Binding="{Binding LastName}" />
        <DataGridTextColumn Header="Age" Binding="{Binding Age}" />
        <DataGridTextColumn Header="Address" Binding="{Binding Address}" />
        <DataGridTextColumn Header="PostCode" Binding="{Binding PostCode}" />
        <DataGridTextColumn Header="PhoneNumber" Binding="{Binding PhoneNumber}" />
    </DataGrid.Columns>
</DataGrid>

如果要動態生成列並且還需要記住滾動條的位置,則可以使用反射從后面的代碼生成列。 缺點是您不能綁定它,但必須手動生成。 例如

<DataGrid AutoGenerateColumns="False" Loaded="DataGrid_Loaded" ItemsSource="{Binding People}" Grid.Row="1" HorizontalScrollBarVisibility="Visible" FontSize="30">
</DataGrid>

和DataGrid_Loaded:

DataGrid dg = (DataGrid)sender;
MainViewModel mvm = (MainViewModel)this.DataContext;
Type classType = typeof(Person);
PropertyInfo[] properties = classType.GetProperties();
foreach (PropertyInfo prop in properties) {
    dg.Columns.Add(new DataGridTextColumn() {
        Binding = new Binding(prop.Name),
        Header = prop.Name
    });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM