DataVirtualization和ListView UI冻结

时间:2011-11-05 12:18:46

标签: wpf

我试图找到一个使用异步DataProvider的解决方案,但是到处都绑定到一个静态类,但我需要动态绑定到不同的对象。

我尝试使用this solution,但我的用户界面仍然冻结。任何人都可以解释为什么我会得到这种行为,以及如何让UI活跃。 应用程序XAML文件:

    <Window x:Class="WpfApplication_async.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication_async"
        Title="MainWindow" Height="350" Width="525">
  <Window.Resources>
    <local:RecordValueDataTemplateSelector x:Key="myDataTemplateSelector"/>
    <DataTemplate x:Key="ComboBoxTemplate">
      <ComboBox Width="{Binding Path=ColumnDef.ColumnWidth,Mode=OneTime}"
                ItemsSource="{Binding Path=viewEntityList,Mode=TwoWay}" 
                SelectedIndex="{Binding Path=Index, Mode=TwoWay}">
      </ComboBox>
    </DataTemplate>
    <DataTemplate x:Key="TextBoxTemplate">
      <TextBox Text="{Binding Path=Text,Mode=TwoWay}" Width="{Binding Path=ColumnDef.ColumnWidth,Mode=OneTime}"/>
    </DataTemplate>
    <DataTemplate x:Key="HeaderTextBlockTemplate">
      <TextBlock Width="{Binding Path=ColumnWidth,Mode=OneTime}" Text="{Binding Path=ColumnName,Mode=TwoWay}" ToolTip="{Binding Path=ColumnName}" />
    </DataTemplate>
    <DataTemplate x:Key="ListBoxTemplate">
      <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Path=Key, Mode=TwoWay, 
          UpdateSourceTrigger=PropertyChanged}" Width="80"/>
        <ListBox ItemsSource="{Binding Path=Items}"
                 ItemTemplateSelector="{StaticResource myDataTemplateSelector}" 
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                 ScrollViewer.CanContentScroll="False" >
          <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
              <StackPanel Orientation="Horizontal">
              </StackPanel>
            </ItemsPanelTemplate>
          </ListBox.ItemsPanel>
        </ListBox>
      </StackPanel>
    </DataTemplate>
  </Window.Resources>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition></RowDefinition>
      <RowDefinition Height="Auto"></RowDefinition>
    </Grid.RowDefinitions>
    <ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Visible">
      <DockPanel>
        <ListBox Name="headerListBox" MinHeight="20" DockPanel.Dock="Top"  
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                 ScrollViewer.CanContentScroll="False"
                 ItemTemplate="{StaticResource HeaderTextBlockTemplate}">
          <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
              <StackPanel Orientation="Horizontal"></StackPanel>
            </ItemsPanelTemplate>
          </ListBox.ItemsPanel>
        </ListBox>
        <ScrollViewer Name="listScrollViewer" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
          <ListView Name="listView" SelectionMode="Extended"
            VirtualizingStackPanel.IsVirtualizing="True"
            ScrollViewer.IsDeferredScrollingEnabled="True"
            VirtualizingStackPanel.VirtualizationMode="Recycling"
            ItemsSource="{Binding}"
            ItemTemplate="{StaticResource ListBoxTemplate}" >
          </ListView>
        </ScrollViewer>
      </DockPanel>
    </ScrollViewer>
    <Button Grid.Row="1" Content="Button" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
  </Grid>
</Window>

我的dataProvider类:

using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Windows.Data;
using System.Windows;
using DataVirtualization;
using System.Threading;
//using System.Threading;

namespace WpfApplication_async
{
  public enum MySubKeyValueType { StringValue=0, ListValue=1 }

  public class MySection : IItemsProvider<MyRecord>
  {
    /// <summary>
    /// Fetches the total number of items available.
    /// </summary>
    /// <returns></returns>
    public int FetchCount()
    {
      //Thread.Sleep(1000);
      return Records.Count;
    }
    /// <summary>
    /// Fetches a range of items.
    /// </summary>
    /// <param name="startIndex">The start index.</param>
    /// <param name="count">The number of items to fetch.</param>
    /// <returns></returns>
    public IList<MyRecord> FetchRange(int startIndex, int count)
    {
      //Thread.Sleep(1000);
      if (startIndex > Records.Count) startIndex = Records.Count;
      if (startIndex + count > Records.Count) count = Records.Count - startIndex;
      return Records.ToList().GetRange(startIndex, count).ToList();
    }
    public MySection()
    {
      Records = new ObservableCollection<MyRecord>();
    }
    public ObservableCollection<MyRecord> Records { get; set;}
  }

  public class MyRecord : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string info)
    {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(info));
      }
    }
    public MyRecord() 
    {
      Items = new ObservableCollection<MySubKeyValue>();
    }
    private string key;
    public string Key { 
      get { 
        return key; 
      } 
      set {
        //if (Key!=null && this.Parent is MySection && (this.Parent as MySection).SectionDefinition.IsNumberedKeys)
        //  return;
        key = value;
        OnPropertyChanged("Key");
      } 
    }
    ObservableCollection<MySubKeyValue> items = new ObservableCollection<MySubKeyValue>();
    public ObservableCollection<MySubKeyValue> Items
    {
      get {
        return items;
      }
      set {
        items = value;
        OnPropertyChanged("NumberedColumnText");
      }
    } 
  }

  public class MySubKeyValue : DependencyObject, INotifyPropertyChanged
  {
    private ColumnDefinition columnDef = null;
    public ColumnDefinition ColumnDef
    {
      get
      {
        if (columnDef == null)
          return columnDef = new ColumnDefinition();
        return columnDef;
      }
      set { columnDef = value; }
    }
    public MySubKeyValue(string str = null)
    {
      Text = str;
      ValueType = MySubKeyValueType.StringValue;
      IsValidData = true;
      ErrorMessage = "error";
    }
    private string text;
    public MySubKeyValueType ValueType { get; set; }
    public string Text
    {
      get
      {
        if (text == null) return String.Empty;
        return text;
      }
      set
      {
        if (text != value)
        {
          text = value;
          OnPropertyChanged("Text");
        }
      }
    }
    public bool isValidData = true;
    public bool IsValidData
    {
      get { return isValidData; }
      set
      {
        if (isValidData != value)
        {
          isValidData = value;
          OnPropertyChanged("IsValidData");
        }
      }
    }
    public string ErrorMessage { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string info)
    {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(info));
      }
    }
  }

  public class StringValue : MySubKeyValue
  {
    public StringValue(string str = null)
    {
      ValueType = MySubKeyValueType.StringValue;
      Text = str;
    }
  }
  public class ListValue : MySubKeyValue 
  {
    private int index;
    public int Index
    {
      get { return index; }
      set
      {
        index = value;
        if (index > -1 && index < valueEntityList.Count)
        {
          base.Text = valueEntityList[index];
          IsValidData = true;
        }
        else
          IsValidData = false;
        OnPropertyChanged("Index");
      }
    }

    public List<string> valueEntityList { get; set; }
    public List<string> viewEntityList { get; set; }
    public ListValue(string str, ListValue l)
    {
      ValueType = MySubKeyValueType.ListValue;
      valueEntityList = l.valueEntityList;
      viewEntityList = l.viewEntityList;
      base.Text = str;
      Index = valueEntityList.FindIndex(v => v == str);
    }
    public ListValue(List<string> _vals = null, List<string> _views = null, string str = "")
    {
      Index = -1;
      ValueType = MySubKeyValueType.ListValue;
      valueEntityList = new List<string>();
      viewEntityList = new List<string>();
      if (_vals != null)
        if (_views != null && _views.Count == _vals.Count)
        {
          valueEntityList.AddRange(_vals);
          viewEntityList.AddRange(_views);
        }
        else
        {
          valueEntityList.AddRange(_vals);
          viewEntityList.AddRange(_vals);
        }
      base.Text = str;
      Index = valueEntityList.FindIndex(v => v == str);
    }
  }
  public class ColumnDefinition : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string info)
    {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(info));
      }
    }
    public ColumnDefinition(string name = "", int width = 80)
    {
      ColumnName = name;
      ColumnWidth = width;
    }
    public string ColumnName { get; set; }
    private int columnWidth;
    public int ColumnWidth
    {
      get { return columnWidth; }
      set { columnWidth = value; OnPropertyChanged("ColumnWidth"); }
    }
  }

}

主窗口:

namespace WpfApplication_async
{
  public partial class MainWindow : Window
  {
    MySection sec1,sec2;
    bool isSec1 = true;
    public MainWindow()
    {
      InitializeComponent();

      sec1 = new MySection();
      for(int i=0;i<50;i++){
        MyRecord rec = new MyRecord();
        rec.Key = i.ToString();
        for(int j=0;j<20;j++){
          rec.Items.Add(new StringValue("abc"));
          rec.Items[rec.Items.Count - 1].ColumnDef = new ColumnDefinition(j.ToString());
        }
        sec1.Records.Add(rec);
      }
      sec2 = new MySection();
      for (int i = 0; i < 50; i++)
      {
        MyRecord rec = new MyRecord();
        rec.Key = i.ToString();
        for (int j = 0; j < 20; j++)
        {
          rec.Items.Add(new ListValue(new List<string> { "a", "b" }, new List<string> { "a", "b" }, "a"));
          rec.Items[rec.Items.Count - 1].ColumnDef = new ColumnDefinition(j.ToString());
        }
        sec2.Records.Add(rec);
      }
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {

      if (isSec1)
        //listView.DataContext = sec2;
        listView.DataContext = new AsyncVirtualizingCollection<MyRecord>(sec2, 10, 30 * 1000);
      else
        //listView.DataContext = sec1;
        listView.DataContext = new AsyncVirtualizingCollection<MyRecord>(sec1, 10, 30 * 1000);
      isSec1 = !isSec1;
    }
  }
}

1 个答案:

答案 0 :(得分:1)

我找到了一个简单的解决方案。我没有设置DataContext,而是手动添加了低优先级的项目。 UI不会冻结。目标已实现。

foreach (var r in sec.Records)
{
    listView.Dispatcher.Invoke((new Action(delegate()
    {
        listView.Items.Add(r);
    })), DispatcherPriority.SystemIdle);
}