According to you XAML code, you have two (2) instances of your ViewModel set in three (3) locations.
Once here bound to the user control:
<UserControl.DataContext>
<viewmodel:ExtractInfoVM></viewmodel:ExtractInfoVM>
</UserControl.DataContext>
and here:
<StackPanel DataContext="{Binding Source={StaticResource vm}}">
and here:
<StackPanel Grid.Row="2" Grid.RowSpan="2" DataContext="{Binding Source={StaticResource vm}}" Margin="0,0,0,34">
Remove the last two instances and also this below and it should work:
<UserControl.Resources>
<viewmodel:ExtractInfoVM x:Key="vm"></viewmodel:ExtractInfoVM>
</UserControl.Resources>
Lastly, also check that you have not set the DataContext in the code-behind.
Update: Richard Deeming suggests to use a
BackGroundWorker
but these days, going forward, it is better to learn how to use
Task-based Asynchronous Pattern (TAP)[
^] as the
BackGroundWorker
is limited to a specific type of job.
Here is an example of a long running task with a
IsBusy
state flag. This example wraps a synchronous process and moves it off the UI thread to stop it blocking and run in the background. This is done using a
TaskCompletionSource(TResult) Class (System.Threading.Tasks)[
^]. I'm also using
MvvmLight[
^] for the
RelayCommand
,
DispatherHelper
, and
ViewModelBase
classes to keep the example code simple.
1. ViewModel:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using GalaSoft.MvvmLight.Threading;
using System.Threading.Tasks;
namespace LongRunningTask
{
public class MainViewModel : ViewModelBase
{
public MainViewModel()
=> ExecuteTaskCommand = new RelayCommand(
() => busyTask = StartExport(),
() => !isBusy);
private Task busyTask;
public RelayCommand ExecuteTaskCommand { get; }
private bool isBusy;
public bool IsBusy
{
get => isBusy;
set => Set(ref isBusy, value);
}
private async Task StartExport()
{
try
{
IsBusy = true;
bool success = await ExportDataAsync().ConfigureAwait(false);
}
finally
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
IsBusy = false;
ExecuteTaskCommand.RaiseCanExecuteChanged();
});
}
}
private Task<bool> ExportDataAsync()
{
var tcs = new TaskCompletionSource<bool>();
Task.Run(async () =>
{
await Task.Delay(3000).ConfigureAwait(false);
tcs.SetResult(true);
}).ConfigureAwait(false);
return tcs.Task;
}
}
}
2. MainWindow code-behind to initialze the
DispatherHelper
:
using GalaSoft.MvvmLight.Threading;
using System.Windows;
namespace LongRunningTask
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DispatcherHelper.Initialize();
}
}
}
3. The MainWindow XAML:
<Window
x:Class="LongRunningTask.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:LongRunningTask"
Title="CODE PROJECT - LONG RUNNING TASK"
Height="350" Width="525" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.Resources>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBusy}" Value="True">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Button Content="Start Task" Padding="10,5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding ExecuteTaskCommand}"/>
</Grid>
</Window>
When the button is clicked, the background of the form is changed to Red and the button is disabled. This is to indicate the busy state. Once completed, the background and button return to their normal states.