我的程序在MDB中搜索每个Table& Row中的给定字符串。 当我开始搜索(Search_Button_Click)时,进度条没有显示,并且ui正在阻塞(无法移动窗口)。不能搞错了。
ListViewData
是ObservableCollection<ListViewData>
private async void Search_Button_Click(object sender, RoutedEventArgs e)
{
LoadBar.Visibility = Visibility.Visible;
await StartSearch();
LoadBar.Visibility = Visibility.Hidden;
}
private async Task StartSearch()
{
await Task.Run(() =>
{
SearchMDB();
});
}
private void SearchMDB()
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
ListViewData.Clear();
foreach (KeyValuePair<string, DataTable> _KVP in MDBContent)
{
for (int RowIndex = 0; RowIndex < _KVP.Value.Rows.Count; RowIndex++)
{
DataRow _DR = _KVP.Value.Rows[RowIndex];
for (int i = 0; i < _DR.ItemArray.Length; i++)
{
if (_DR[i].ToString().Contains(Search_TextBox.Text))
{
ListViewItemClass _LC = new ListViewItemClass();
_LC.Page = _KVP.Key;
_LC.Column = _DR.Table.Columns[i].ToString();
_LC.Row = (RowIndex + 1).ToString();
_LC.ItemValue = _DR[i].ToString();
ListViewData.Add(_LC);
}
}
}
}
}));
}
答案 0 :(得分:3)
您的代码是同步的。 BeginInvoke
用于封送回UI线程的调用。使用Task.Run
来呼叫BeginInvoke
不会改变任何内容。
我假设您从名称SearchMDB
开始尝试在MDB数据库上执行LIKE
搜索。最好的选择是让 Access 执行此操作。 Access有索引。你的代码没有。它被迫扫描所有数据。更好的是,找到一个可以处理MDB文件的全文搜索库。将所有内容加载到内存中实际上可以使事情更慢。
如果您希望此代码按原样运行,只需使用Task.Run并将过滤器字符串作为参数传递给SearchMDB
,例如StartSearch(Search_TextBox.Text)
:
private async void Search_Button_Click(object sender, RoutedEventArgs e)
{
LoadBar.Visibility = Visibility.Visible;
await Task.Run(StartSearch(Search_TextBox.Text));
LoadBar.Visibility = Visibility.Hidden;
}
private void SearchMDB()
{
ListViewData.Clear();
foreach (KeyValuePair<string, DataTable> _KVP in MDBContent)
{
.....
}
}
更好的是,避免全局ListViewData容器。使用全局状态时编写正确的多线程代码非常困难。错误处理也更难 - 如果SearchMDB
失败,您打算做什么?
假设ListViewData
是List<ListViewItemClass>
,您应该写:
private async void Search_Button_Click(object sender, RoutedEventArgs e)
{
LoadBar.Visibility = Visibility.Visible;
ListViewData=await Task.Run(StartSearch(Search_TextBox.Text));
LoadBar.Visibility = Visibility.Hidden;
}
private List<ListViewItemClass> SearchMDB()
{
var newData=new List<ListViewItemClass>();
foreach (KeyValuePair<string, DataTable> _KVP in MDBContent)
{
for ()
{
.....
newData.Add(_LC);
}
}
return newData();
}
这样可以避免并发错误和在SearchMDB
抛出时不会破坏您的UI。
<强>更新强>
整个方法可以重写为单个LINQ查询:
var items = from KeyValuePair<string, DataTable> pair in MDBContent
from DataRow row in pair.Value.Rows
from DataColumn column in pair.Value.Columns
let field=row[column].ToString()
where field.Contains(searchText)
select new ListViewItemClass
{
Page = pair.Key,
Column = column.Caption,
// Row = (RowIndex + 1).ToString(),
ItemValue = field
};
不仅更清楚发生了什么,只需调用`.AsParallel()就可以轻松将其转换为PLINQ,例如:
var items = from KeyValuePair<string, DataTable> pair in MDBContent.AsParallel()
from DataRow row in pair.Value.Rows
from DataColumn column in pair.Value.Columns
let field=row[column].ToString()
where field.Contains(searchText)
select new ListViewItemClass
{
Page = pair.Key,
Column = column.Caption,
// Row = (RowIndex + 1).ToString(),
ItemValue = field
};
return items.ToList();
请注意,没有Row
字段。表行不具有行索引。它们在结果中的位置由ORDER BY
子句控制。没有它,数据库可以,将不按顺序返回结果。
如果使用传递inded的Select()
重载以及要投影的项目,则可以引入行索引:
var items = from pair in MDBContent.AsParallel()
let indexedRows =pair.Value.Rows.OfType<DataRow>().Select((row,idx)=>new {Row=row,Idx=idx})
from indexedRow in indexedRows
from DataColumn column in pair.Value.Columns
let field=indexedRow.Row[column].ToString()
where field.Contains(searchText)
select new ListViewItemClass
{
Page = pair.Key,
Column = column.Caption,
Row = (indexedRow.Idx +1).ToString(),
ItemValue = field
};
更新2
另一个问题中的评论显示ListViewData
是一个ObservableCollection。这不会改变任何事情。数据仍应在侧面处理。 ObservableCollection
旨在观察单个项目的更改。
在这种情况下,整个集合会发生变化。处理此问题的最简单方法是替换集合并引发其相应属性更改的通知,从而强制UI重新加载数据。这就是WPF数据绑定的工作原理,通过绑定到属性而不是字段。它也便宜得多 - 清理集合并逐个添加项目会引发很多的通知。
点击事件处理程序应更改为:
private async void Search_Button_Click(object sender, RoutedEventArgs e)
{
LoadBar.Visibility = Visibility.Visible;
var data=await Task.Run(StartSearch(Search_TextBox.Text));
ListViewData=new ObservableCollection(data);
//Raise a change notification if `ListViewData` isn't a property
//or doesn't raise the event itself
//RaisePropertyChanged("ThatPropertyName);
LoadBar.Visibility = Visibility.Hidden;
}
答案 1 :(得分:2)
我可以看到两个问题:
A)。 SearchMDB()方法已经异步执行。您可以删除this.Dispatcher.BeginInvoke()
行,因为您再次回到UI线程上运行。
B)。但!您正在更新该代理中的UI!更好的做法是异步线程从DB获取所需的所有数据(如果需要,创建一个小的DTO类),然后在UI线程上填充/刷新ListView。
private async Task StartSearch()
{
var data = await SearchAndFetchMDBDataAsync();
RefreshListView(data);
}
private Task<List<object>> SearchAndFetchMDBDataAsync()
{
return Task.Run(() =>
{
List<MdbDto> data = new List<MdbDto>();
foreach (KeyValuePair<string, DataTable> _KVP in MDBContent)
// ...
return data;
});
}