我有一个数据网格,用于显示从数据服务器读取的日志信息。
由于日志数量巨大,因此我不想在页面初始化时立即加载数据。所以下面是我的设计:
在初始化中加载前20行。当用户拖动滚动条时,我将计算滑块移动的距离,获取日志索引,然后从服务器检索新日志。将清除旧日志,以确保数据网格的内存消耗不大。
但是在实施过程中,我遇到了以下问题
1.如何设置滑块的尺寸?如果我只向datagrid添加20行,则滑块非常长
2.在我拖动滑块(未完成)的过程中,datagride的内容也会自动更改。我应该如何禁用它。
下面是我的代码:
<DataGrid x:Name="LogsGrid"
Margin="0,0,0,0"
Height="457"
HeadersVisibility="Column"
ItemsSource="{Binding LogsList}"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
ScrollViewer.CanContentScroll="True">
<i:Interaction.Triggers>
<local:RoutedEventTrigger RoutedEvent="ScrollViewer.ScrollChanged">
<local:CustomCommandAction Command="{Binding ScrollCommand}" />
</local:RoutedEventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
...
</DataGrid.Columns>
Blew是我的视图模型:
public LogsViewModel()
{
InitializeCommand();
scrollTimer = new DispatcherTimer();
scrollTimer.Interval = new TimeSpan(0,0,0,0,250);
scrollTimer.Tick += new EventHandler(ScrollTimer_Elapsed);
}
private void ScrollTimer_Elapsed(object sender, EventArgs e)
{
scrollTimer.Stop();
GetNewLog(nCurrentScrollIndex);
}
private int nCurrentScrollIndex = 0;
private void OnScroll(object param)
{
ScrollChangedEventArgs args = param as ScrollChangedEventArgs;
if (args.VerticalOffset == 0)
{
return;
}
int nLogTotalNumber = int.Parse(LogTotalNumber);
nCurrentScrollIndex = (int)(args.VerticalOffset/args.ExtentHeight * nLogTotalNumber);
if (scrollTimer!= null || scrollTimer.IsEnabled)
{
scrollTimer.Stop();
}
scrollTimer.Start();
}
答案 0 :(得分:1)
我认为没有必要像这样进入WPF的细节。 DataGrid支持虚拟化,因此,除非您做一些破解工作,否则它应仅获取屏幕上实际可见的项目。就是说,我一直无法让DataGrid在不锁定GUI线程的情况下异步正确地获取项目,因此您可能必须在视图模型中自行处理。
下面是一个示例,它可以轻松地在i7上管理1000万条记录,我将从模板DataGrid开始:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Text">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
这是主视图模型,它创建一个项目数组并仅在需要时才取回每个项目:
public class MainViewModel : ViewModelBase
{
public Item[] Items { get; }
public MainViewModel()
{
this.Items = Enumerable
.Range(1, 1000)
.Select(i => new Item { Value = i })
.ToArray();
}
}
public class Item : ViewModelBase
{
public int Value { get; set; }
private string _Text;
public string Text
{
get
{
// if already loaded then return what we got
if (!String.IsNullOrEmpty(this._Text))
return this._Text;
// otherwise load the value in a task (this will raise property changed event)
Task.Run(async () => {
Debug.WriteLine($"Fetching value # {Value}");
await Task.Delay(1000); // simulate delay in fetching the value
this.Text = $"Item # {Value}";
});
// return default string for now
return "Loading...";
}
set
{
if (this._Text != value)
{
this._Text = value;
RaisePropertyChanged(() => this.Text);
}
}
}
}
这当然是一个非常基本的示例,仅为了说明这一点而显示。在现实世界中,您需要设置某种缓存,并一次获取记录块。您可能还需要某种优先的预先冥想队列,以预期将来可能会获取记录。无论哪种方式,WPF都不支持数据虚拟化,所以我认为您将不得不做这样的事情来获得您想要的确切行为。