解释
我在WPF中创建自己的搜索控件。此控件是UserControl
,其中包含具有搜索参数的区域(例如:搜索特定ID,名称,...)和显示结果的GridView
。
在我的控件中,我有dependency property
类型ICommand
,我绑定命令以执行我的搜索查询。
public static readonly DependencyProperty SearchCommandProperty =
DependencyProperty.Register("SearchCommand", typeof(ICommand), typeof(SearchControl));
在某个窗口中使用我的控件:
<customControls:SearchControl SearchCommand="{Binding SearchItemsCommand}"
SearchResult="{Binding SearchResult}" />
SearchItemsCommand
是我的ViewModel中的Command
,我可以在其中找到我的搜索查询。
在此命令中,您可以找到我的查询以检索结果。SearchResult
是我的ICollection
,其中包含搜索查询的结果。命令代码
视图模型
private DelegateCommand searchItemsCommand;
public DelegateCommand SearchItemsCommand
{
get
{
if (this.searchItemsCommand== null)
this.searchItemsCommand= new DelegateCommand(this.SearchItemsCommandExecuted);
return this.searchItemsCommand;
}
}
private ICollection<VoucherOverviewModel> voucherResults;
private void SearchItemsCommandExecuted()
{
using (DbContext context = new DbContext())
{
var query = (from v in context.Vouchers
join vt in context.VoucherTransactions on new
{
voucherID = v.VoucherID,
type = VoucherTransactionType.Out
} equals new
{
voucherID = vt.VoucherID,
type = vt.Type
}
join vtype in context.VoucherTypes on v.VoucherTypeID equals vtype.VoucherTypeID
join c in context.Customers on vt.CustomerID equals c.CustomerID
join pos in context.PointOfSales on v.PointOfSaleID equals pos.PointOfSaleID
select new VoucherOverviewModel()
{
PointOfSaleID = v.PointOfSaleID,
PointOfSaleName = pos.Name,
VoucherID = v.VoucherID,
VoucherCode = v.Code,
VoucherTypeID = v.VoucherTypeID,
VoucherTypeDescription = vtype.Code,
CustomerID = c.CustomerID,
CustomerName = c.Name,
Value = vt.Value,
UsedValue = context.VoucherTransactions
.Where(x => x.VoucherID == v.VoucherID &&
x.Type == VoucherTransactionType.In)
.Sum(x => x.Value),
CreateDate = vt.Date,
ValidFrom = v.ValidFrom,
ValidUntil = v.ValidUntil,
ParentVoucherID = v.ParentVoucherID,
Comment = v.Comment,
});
foreach (ISearchParameter searchParameter in this.SearchParameters)
{
if (!searchParameter.Value.IsNullOrDefault())
{
switch ((FilterVoucherParameterKey)searchParameter.Key)
{
case FilterVoucherParameterKey.CustomerID:
query = query.Where(x => x.CustomerID == (int)searchParameter.Value);
break;
case FilterVoucherParameterKey.VoucherID:
query = query.Where(x => x.VoucherCode.Contains((string)searchParameter.Value));
break;
case FilterVoucherParameterKey.PointOfSale:
query = query.Where(x => x.PointOfSaleID == (byte)searchParameter.Value);
break;
case FilterVoucherParameterKey.Type:
query = query.Where(x => x.VoucherTypeID == (byte)searchParameter.Value);
break;
}
}
}
this.voucherResults = query.ToList();
}
}
自定义控件
public static readonly DependencyProperty SearchCommandProperty =
DependencyProperty.Register("SearchCommand", typeof(ICommand), typeof(SearchControl));
public ICommand SearchCommand
{
get
{
return (ICommand)this.GetValue(SearchCommandProperty);
}
set
{
this.SetValue(SearchCommandProperty, value);
}
}
这是我的依赖项属性,因此我可以将SearchItemsCommand
绑定到我的自定义控件。
然后我有另一个ICommand
来执行binded命令并在我的自定义控件中显示加载元素。
单击按钮时将执行此LocalSearchCommand
。
private DelegateCommand localSearchCommand;
public DelegateCommand LocalSearchCommand
{
get
{
if (this.localSearchCommand == null)
this.localSearchCommand = new DelegateCommand(this.LocalSearchCommandExecuted);
return this.localSearchCommand;
}
}
private void LocalSearchCommandExecuted()
{
loadingElement.Visible = true;
Task.Factory.StartNew(() =>
{
this.Dispatcher.Invoke((Action)(() => this.SearchCommand.Execute(null)));
})
.ContinueWith(t =>
{
if (t.IsCompleted)
{
t.Dispose();
}
});
}
问题
我想在执行查询以与用户交互时显示 Loading元素。要显示此元素,我必须将其设置为visible
。
现在的问题是,当我将其设置为可见并想要执行搜索命令时,我的整个UI都会冻结。从数据库中获取结果并在GridView
中生成结果后,只有这样,它才会显示我的加载元素。我明白为什么会发生这种情况,我试图用Task
来解决它。
loadingElement.Visible = true;
Task.Factory.StartNew(() =>
{
this.Dispatcher.Invoke((Action)(() => this.SearchCommand.Execute(null)));
})
.ContinueWith(t =>
{
if (t.IsCompleted)
{
t.Dispose();
}
});
我必须使用Dispatcher
中的Task
来执行SearchCommand,因为它由UI线程拥有。
但由于使用了Dispatcher
类,我遇到了和以前一样的问题。我的加载元素仅在查询已执行时显示,因为Dispatcher
在UI线程上执行搜索命令。
如果不使用Dispatcher
类,它会给我以下错误:
The calling thread cannot access this object because a different thread owns it.
我收到了这个错误:
return (ICommand)this.GetValue(SearchCommandProperty);
即使使用空SearchItemsCommandExecuted
方法,也会发生错误。
我已尝试过的内容
我尝试将TaskScheduler
的{{1}}设置为
Task
我使用了很多TaskScheduler.FromCurrentSynchronizationContext()
和BeginInvoke
的组合。
Invoke
中设置加载元素的Visibility
。但以上都没有奏效。
如何解决我的问题,以便在执行查询时显示加载元素。我错过了一些明显的东西吗?
提前致谢!
Loetn
答案 0 :(得分:1)
问题是您正在使用ThreadPool线程创建新的Task
,但使用Dispatcher.Invoke
,它在UI线程上运行您的命令,因此您的UI冻结。
您需要将SearchCommand
工作卸载到后台线程,然后在UI线程上使用Continuation 更新您的UI(不要尝试在SearchCommand
内更新您的UI) :
然后,它显示我的加载元素。我明白为什么会发生这种情况,我试图用任务来解决它。
loadingElement.Visible = true;
Task.Factory.StartNew(() =>
{
return this.SearchCommand.Execute(null);
})
.ContinueWith(t =>
{
MyUIElement = t.Result; // Update your UI here.
}, TaskScheduler.FromCurrentSynchronizationContext());
答案 1 :(得分:0)
编辑:没有捕获第一个命令与第二个命令的绑定。因此,以下将无法正常工作。调查它......
编辑2:我假设你想从你的viewmodel开始后台操作。目前我想不出另一种方法,而不是使你的loadingItem.Visible属性成为依赖属性,将后台操作移动到你的viewmodel,从那里分配一个绑定到loadingItem.Visible的属性,并从你的中移除异步的东西用户控件。
您想在后台线程上开始查询并将结果分配给您的ui线程:
private void LocalSearchCommandExecuted(object obj)
{
//can also be your loadingItem.
VisibleElement.Visibility = Visibility.Collapsed;
//get the ui context
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
//do your query on the background thread
LongRunningOperation();
})
//this happens on the ui thread because of the second parameter scheduler
.ContinueWith(t =>
{
if (t.IsCompleted)
{
VisibleElement.Visibility = Visibility.Visible;
//assign the result from the LongRunningOperation to your ui list
_list = new List<string>(_tempList);
//if you need to...
RaisePropertyChanged("SearchResults");
}
}, scheduler );
}
private void LongRunningOperation()
{
//assign your result to a temporary collection
//if you do not do that you will get an exception: An ItemsControl is inconsistent with its items source
_tempList = new List<string>();
for (int i = 0; i < 100; i++)
{
_tempList.Add("Test" + i);
Thread.Sleep(10);
}
}
答案 2 :(得分:0)
我在this blog的帮助下解决了我的问题。
我必须要修改getter
的{{1}},以便它使用Dependency property SearchCommand
。
Dispatcher
这是我的public ICommand SearchCommand
{
get
{
return (ICommand)this.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Background,
(DispatcherOperationCallback)delegate { return this.GetValue(SearchCommandProperty); },
SearchCommandProperty);
// Instead of this
// return this.GetValue(SearchCommandProperty);
}
set
{
this.SetValue(SearchCommandProperty, value);
}
}
:
Command method
感谢您的帮助!