我试图找到一个使用异步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;
}
}
}
答案 0 :(得分:1)
我找到了一个简单的解决方案。我没有设置DataContext
,而是手动添加了低优先级的项目。 UI不会冻结。目标已实现。
foreach (var r in sec.Records)
{
listView.Dispatcher.Invoke((new Action(delegate()
{
listView.Items.Add(r);
})), DispatcherPriority.SystemIdle);
}