WPF Datagrid/Datatable:大量行

WPF Datagrid/Datatable:大量行,wpf,performance,datagrid,datatable,rows,Wpf,Performance,Datagrid,Datatable,Rows,我有一个连接到Datatable的Datagrid,它需要加载大量的行 为了加快速度,我加载了10%的行,并显示表单。大多数情况下,用户只需要这10%(它们是最新的条目)。在后台线程中,我将其余90%的行加载到另一个datatable(SecondData)中。然后合并两个数据表: FirstData.BeginLoadData() FirstData.Merge(SecondData, False) FirstData.EndLoadData() 这很好,但是合并操作需要很长时间。如果我反转

我有一个连接到Datatable的Datagrid,它需要加载大量的行

为了加快速度,我加载了10%的行,并显示表单。大多数情况下,用户只需要这10%(它们是最新的条目)。在后台线程中,我将其余90%的行加载到另一个datatable(SecondData)中。然后合并两个数据表:

FirstData.BeginLoadData()
FirstData.Merge(SecondData, False)
FirstData.EndLoadData()
这很好,但是合并操作需要很长时间。如果我反转操作(将SecondData与FirstData合并),则所需时间要少得多。但随后我必须将itemsource(SecondData)重新分配给Datagrid,用户会松开当前的滚动位置、选定的行等

我还尝试从后台线程将行直接添加到FirstData中,它似乎工作得很好。但是当我在那之后滚动Datagrid时,我会被冻结,然后“DataTable内部索引被破坏”


正确的方法是什么?

如果使用窗口或控件的Dispatcher属性的BeginInvoke方法,则 将委托添加到调度程序的事件队列;但是,您有机会指定 它的优先级较低。通过执行一次只加载一个项的方法,窗口 有机会在项目之间执行任何其他更高优先级的事件。这允许 控件或窗口,以便立即显示和渲染,并一次加载一个项目

下面是加载列表框的一些示例代码。
您可以根据您的数据网格进行调整。
在本例中,我使用了一个ViewModel,它包含一个ObservableCollection,其中包含一个对象。
如果您在转换到DataGrid时遇到问题,我将返工

下面是Window XAML:

<Window x:Class="ListBoxDragDrop.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Models="clr-namespace:ListBoxDragDrop.Models" 
    Loaded="Window_Loaded"
    Title="Main Window" Height="400" Width="800">
  <DockPanel>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <ListBox Grid.Column="0" ItemsSource="{Binding Path=MyData}">
            <ListBox.ItemTemplate>
                <DataTemplate DataType="{x:Type Models:Person}">
                    <StackPanel>
                        <TextBlock Text="{Binding Name}" ></TextBlock>
                        <TextBlock Text="{Binding Description}" ></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
  </DockPanel>
</Window>

下面是一个有点黑客攻击的附加版本,它演示了如何在使用still BeginInvoke绑定到DataView时加载DataGrid。 代码仍然一次加载一行到DataGrid中。 您需要根据需要进行修改;我使用加载的事件从AdventureWorks示例加载

以下是ViewModel的工作原理:

  • 首先使用带有Where子句1=0的SQL语句加载列
  • 调用Dispatcher.BeginInvoke以首先加载数据子集
  • 然后再次调用Dispatcher.BeginInvoke以加载剩余的数据
  • 这是窗户:

    <Window x:Class="DatagridBackgroundWorker.Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:WpfToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
        Loaded="Window_Loaded"
        Title="Main Window" Height="400" Width="800">
      <DockPanel>
        <Grid>
            <WpfToolkit:DataGrid  
                Grid.Column="1"
                SelectedItem="{Binding Path=SelectedGroup, Mode=TwoWay}"
                ItemsSource="{Binding Path=GridData, Mode=OneWay}" >
            </WpfToolkit:DataGrid>
        </Grid>
      </DockPanel>
    </Window>
    
    以下是ViewModel:

    public class MainViewModel : ViewModelBase
    {
      public MainViewModel()
      {
      }
    
      private ObservableCollection<Person> _myData = new ObservableCollection<Person>();
      public ObservableCollection<Person> MyData
      {
         get
         {
            return _myData;
         }
         set
         {
            _myData = value;
            OnPropertyChanged("MyData");
         }
      }
    }
    
    public class MainViewModel : ViewModelBase
    {
      public MainViewModel()
      {
         // load the connection string from the configuration files
         _connectionString = ConfigurationManager.ConnectionStrings["AdventureWorks"].ConnectionString;
    
         using (SqlConnection conn = new SqlConnection(ConnectionString))
         {
            conn.Open();
    
            // load no data 1=0, but get the columns...
            string query =
               "SELECT [BusinessEntityID],[Name],[SalesPersonID],[Demographics],[rowguid],[ModifiedDate] FROM [Sales].[Store] Where 1=0";
            SqlCommand cmd = conn.CreateCommand();
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = query;
    
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            da.Fill(_ds);
         }
      }
    
      // only show grid data after button pressed...
      private DataSet _ds = new DataSet("MyDataSet");
      public DataView GridData
      {
         get
         {
            return _ds.Tables[0].DefaultView;
         }
      }
    
      private void AddRow(SqlDataReader reader)
      {
         DataRow row = _ds.Tables[0].NewRow();
         for (int i = 0; i < reader.FieldCount; i++)
         {
            row[i] = reader[i];
         }
         _ds.Tables[0].Rows.Add(row);
      }
    
      public void LoadData(Dispatcher dispatcher)
      {
         // Execute a delegate to load the first number on the UI thread, with a priority of Background.
         dispatcher.BeginInvoke(DispatcherPriority.Background, new LoadNumberDelegate(LoadNumber), dispatcher, true, 1);
      }
    
      // Declare a delegate to wrap the LoadNumber method
      private delegate void LoadNumberDelegate(Dispatcher dispatcher, bool first, int id); 
      private void LoadNumber(Dispatcher dispatcher, bool first, int id)
      {
         try
         {
            using (SqlConnection conn = new SqlConnection(ConnectionString))
            {
               conn.Open();
    
               // load first 10 rows...
               String query = string.Empty;
               if (first)
               {
                  // load first 10 rows
                  query =
                     "SELECT TOP 10 [BusinessEntityID],[Name],[SalesPersonID],[Demographics],[rowguid],[ModifiedDate] FROM [AdventureWorks2008].[Sales].[Store] ORDER By [BusinessEntityID]";
                  SqlCommand cmd = conn.CreateCommand();
                  cmd.CommandType = CommandType.Text;
                  cmd.CommandText = query;
                  int lastId = -1;
                  SqlDataReader reader = cmd.ExecuteReader();
                  if (reader != null)
                  {
                     if (reader.HasRows)
                     {
                        while (reader.Read())
                        {
                           lastId = (int)reader["BusinessEntityID"];
                           AddRow(reader);
                        }
                     }
                     reader.Close();
                  }
    
                  // Load the remaining, by executing this method recursively on 
                  // the dispatcher queue, with a priority of Background.
                  dispatcher.BeginInvoke(DispatcherPriority.Background,
                     new LoadNumberDelegate(LoadNumber), dispatcher, false, lastId);
               }
               else
               {
                  // load the remaining rows...
    
                  // SIMULATE DELAY....
                  Thread.Sleep(5000);
    
                  query = string.Format(
                        "SELECT [BusinessEntityID],[Name],[SalesPersonID],[Demographics],[rowguid],[ModifiedDate] FROM [Sales].[Store] Where [BusinessEntityID] > {0} ORDER By [BusinessEntityID]",
                        id);
                  SqlCommand cmd = conn.CreateCommand();
                  cmd.CommandType = CommandType.Text;
                  cmd.CommandText = query;
                  SqlDataReader reader = cmd.ExecuteReader();
                  if (reader != null)
                  {
                     if (reader.HasRows)
                     {
                        while (reader.Read())
                        {
                           AddRow(reader);
                        }
                     }
                     reader.Close();
                  }
               }
            }
         }
         catch (SqlException ex)
         {
         }
      }
    
      private string _connectionString = string.Empty;
      public string ConnectionString
      {
         get { return _connectionString; }
         set
         {
            _connectionString = value;
            OnPropertyChanged("ConnectionString");
         }
      }
    }
    
    public类MainViewModel:ViewModelBase
    {
    公共主视图模型()
    {
    //从配置文件加载连接字符串
    _connectionString=ConfigurationManager.connectionString[“AdventureWorks”]。connectionString;
    使用(SqlConnection conn=newsqlconnection(ConnectionString))
    {
    conn.Open();
    //不加载数据1=0,但获取列。。。
    字符串查询=
    从[Sales].[Store]中选择[BusinessEntityID]、[Name]、[SalesorSonid]、[Demographics]、[rowguid]、[ModifiedDate],其中1=0;
    SqlCommand cmd=conn.CreateCommand();
    cmd.CommandType=CommandType.Text;
    cmd.CommandText=查询;
    SqlDataAdapter da=新的SqlDataAdapter(cmd);
    da.填充(_ds);
    }
    }
    //仅在按下按钮后显示网格数据。。。
    私有数据集_ds=新数据集(“MyDataSet”);
    公共数据视图网格数据
    {
    得到
    {
    返回_ds.Tables[0].DefaultView;
    }
    }
    私有void AddRow(SqlDataReader)
    {
    数据行行=_ds.Tables[0].NewRow();
    对于(int i=0;i{0}按[BusinessEntityID]排序”,
    身份证);
    SqlCommand cmd=conn.CreateCommand();
    cmd.CommandType=CommandType.Text;
    cmd.CommandText=查询;
    
    <Window x:Class="DatagridBackgroundWorker.Views.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:WpfToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
        Loaded="Window_Loaded"
        Title="Main Window" Height="400" Width="800">
      <DockPanel>
        <Grid>
            <WpfToolkit:DataGrid  
                Grid.Column="1"
                SelectedItem="{Binding Path=SelectedGroup, Mode=TwoWay}"
                ItemsSource="{Binding Path=GridData, Mode=OneWay}" >
            </WpfToolkit:DataGrid>
        </Grid>
      </DockPanel>
    </Window>
    
    public partial class MainView : Window
    {
      ViewModels.MainViewModel _mvm = new MainViewModel();
    
      public MainView()
      {
         InitializeComponent();
         this.DataContext = _mvm;
      }
    
      private void Window_Loaded(object sender, RoutedEventArgs e)
      {
         Dispatcher d = this.Dispatcher;
         _mvm.LoadData(d);
      }
    }
    
    public class MainViewModel : ViewModelBase
    {
      public MainViewModel()
      {
         // load the connection string from the configuration files
         _connectionString = ConfigurationManager.ConnectionStrings["AdventureWorks"].ConnectionString;
    
         using (SqlConnection conn = new SqlConnection(ConnectionString))
         {
            conn.Open();
    
            // load no data 1=0, but get the columns...
            string query =
               "SELECT [BusinessEntityID],[Name],[SalesPersonID],[Demographics],[rowguid],[ModifiedDate] FROM [Sales].[Store] Where 1=0";
            SqlCommand cmd = conn.CreateCommand();
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = query;
    
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            da.Fill(_ds);
         }
      }
    
      // only show grid data after button pressed...
      private DataSet _ds = new DataSet("MyDataSet");
      public DataView GridData
      {
         get
         {
            return _ds.Tables[0].DefaultView;
         }
      }
    
      private void AddRow(SqlDataReader reader)
      {
         DataRow row = _ds.Tables[0].NewRow();
         for (int i = 0; i < reader.FieldCount; i++)
         {
            row[i] = reader[i];
         }
         _ds.Tables[0].Rows.Add(row);
      }
    
      public void LoadData(Dispatcher dispatcher)
      {
         // Execute a delegate to load the first number on the UI thread, with a priority of Background.
         dispatcher.BeginInvoke(DispatcherPriority.Background, new LoadNumberDelegate(LoadNumber), dispatcher, true, 1);
      }
    
      // Declare a delegate to wrap the LoadNumber method
      private delegate void LoadNumberDelegate(Dispatcher dispatcher, bool first, int id); 
      private void LoadNumber(Dispatcher dispatcher, bool first, int id)
      {
         try
         {
            using (SqlConnection conn = new SqlConnection(ConnectionString))
            {
               conn.Open();
    
               // load first 10 rows...
               String query = string.Empty;
               if (first)
               {
                  // load first 10 rows
                  query =
                     "SELECT TOP 10 [BusinessEntityID],[Name],[SalesPersonID],[Demographics],[rowguid],[ModifiedDate] FROM [AdventureWorks2008].[Sales].[Store] ORDER By [BusinessEntityID]";
                  SqlCommand cmd = conn.CreateCommand();
                  cmd.CommandType = CommandType.Text;
                  cmd.CommandText = query;
                  int lastId = -1;
                  SqlDataReader reader = cmd.ExecuteReader();
                  if (reader != null)
                  {
                     if (reader.HasRows)
                     {
                        while (reader.Read())
                        {
                           lastId = (int)reader["BusinessEntityID"];
                           AddRow(reader);
                        }
                     }
                     reader.Close();
                  }
    
                  // Load the remaining, by executing this method recursively on 
                  // the dispatcher queue, with a priority of Background.
                  dispatcher.BeginInvoke(DispatcherPriority.Background,
                     new LoadNumberDelegate(LoadNumber), dispatcher, false, lastId);
               }
               else
               {
                  // load the remaining rows...
    
                  // SIMULATE DELAY....
                  Thread.Sleep(5000);
    
                  query = string.Format(
                        "SELECT [BusinessEntityID],[Name],[SalesPersonID],[Demographics],[rowguid],[ModifiedDate] FROM [Sales].[Store] Where [BusinessEntityID] > {0} ORDER By [BusinessEntityID]",
                        id);
                  SqlCommand cmd = conn.CreateCommand();
                  cmd.CommandType = CommandType.Text;
                  cmd.CommandText = query;
                  SqlDataReader reader = cmd.ExecuteReader();
                  if (reader != null)
                  {
                     if (reader.HasRows)
                     {
                        while (reader.Read())
                        {
                           AddRow(reader);
                        }
                     }
                     reader.Close();
                  }
               }
            }
         }
         catch (SqlException ex)
         {
         }
      }
    
      private string _connectionString = string.Empty;
      public string ConnectionString
      {
         get { return _connectionString; }
         set
         {
            _connectionString = value;
            OnPropertyChanged("ConnectionString");
         }
      }
    }