Reputation: 35841
I'm building an MVVM Light WPF app in Visual Studio 2015. It has a user control that contains a WindowsFormsHost
with a ReportViewer
for SQL Server Reporting Services local report. A button in ReportView.xaml
calls a command, which in turn sends a message to the code-behind of MasterListView.xaml
to generate the report.
Here's the command called by the button in ReportViewModel.cs
:
public ICommand RunReportRelayCommand =>
new RelayCommand(async ()=> { await RunReport(); });
private async Task RunReport()
{
try
{
IsBusy = true;
await Task.Run(() =>
{
Messenger.Default.Send(true, "RunMasterListReport");
});
}
finally
{
IsBusy = false;
}
}
Here's the definition of IsBusy
property in ReportViewModel.cs
:
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
if (value == _isBusy) return;
_isBusy = value;
RaisePropertyChanged();
}
}
The same view, ReportView.xaml
, which contains the button calling the above command also contains the following Extended WPF Toolkit Busy Indicator:
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="MasterListViewTemplate">
<view:MasterListView />
</DataTemplate>
</UserControl.Resources>
<xctk:BusyIndicator IsBusy="{Binding IsBusy}">
<StackPanel>
<!-- Other XAML here -->
<ContentControl ContentTemplate="{StaticResource MasterListViewTemplate}" />
</StackPanel>
</xctk:BusyIndicator>
</UserControl>
Then in the MasterListView.cs
code-behind, we have this:
public partial class MasterListView : UserControl
{
public MasterListView()
{
InitializeComponent();
Messenger.Default.Register<bool>(this, "RunMasterListReport", RunMasterListReport);
}
public async void RunMasterListReport(bool val)
{
await Task.Run(() =>
{
var dataSet = new DrugComplianceDataSet();
dataSet.BeginInit();
ReportViewer.ProcessingMode = ProcessingMode.Local;
ReportViewer.LocalReport.ShowDetailedSubreportMessages = true;
ReportViewer.LocalReport.DataSources.Clear();
var dataSource = new ReportDataSource
{
Name = "MasterListRandomDataSet",
Value = dataSet.MasterListRandom
};
ReportViewer.LocalReport.DataSources.Add(dataSource);
ReportViewer.LocalReport.ReportEmbeddedResource = "MasterListRandom.rdlc";
dataSet.EndInit();
var adapter = new MasterListRandomTableAdapter { ClearBeforeFill = true }
.Fill(dataSet.MasterListRandom);
Dispatcher.Invoke((MethodInvoker)(() => { ReportViewer.RefreshReport(); }));
});
}
}
However, the busy indicator doesn't trigger, though the report does show after 5 seconds or so. What am I doing wrong? Thanks.
Upvotes: 3
Views: 1313
Reputation:
You got a whole soup of wat in there. Things like
await Task.Run(() =>
suggest you don't really understand how async/await works. I'd step down and find some nice documentation to read. Also, you appear to be doing all your work on the UI thread.
Dispatcher.Invoke((MethodInvoker)(() => { ReportViewer.RefreshReport(); }));
You shouldn't be touching a dispatcher in a background worker unless you're updating the UI. It appears you're offloading the actual work you intend to do in a background thread (refreshing the report) on the UI thread.
Maybe your report viewer HAS to run in the UI thread. If that's so (maybe it was designed a while ago and doesn't take advantage of multitasking) there isn't much you can do about this situation. Your UI will be locked while it's processing.
If all your long-running work has to run on the UI thread, then strip out all that nonsense. Before kicking off your work, update IsBusy, then offload execution of ReportViewer.RefreshReport()
onto the Dispatcher, but using a low priority DispatcherPriority, so that it runs after the UI updates and shows that it is busy. Your UI will be frozen during processing, but at least you'll give the user an indication of what's going on.
Upvotes: 2