Search code examples
c#wpfmvvmprogress

Reporting progress from ViewModel method executed in a Task


i'm trying to report progress from ViewModel (MVVM Light) methods that are executed in a task. The ProgressViewModel contains a ProgressModel property that provides properties to describe the current state. Those properties are bound to an Xceed BusyIndicator. So far, so good. But its not working as expected. The ProgressModel.IsRunning is bound to BusyIndiciator.IsBusy and toggles the visibility - that works. ProgressModel.Description and ProgressModel.Percentage are bound to a TextBlock.Text/ProgressBar.Value - but they are not updated...

So... since IsRunning works, whats wrong with Description and Progress...?

// Model
public sealed class ProgressModel: ObservableObject
{
    private string m_description;
    private float m_percentage;
    private bool m_running;

    public string Description
    {
        get { return m_description; }
        set { Set(ref m_description, value); }
    }
    public float Percentage
    {
        get { return m_percentage; }
        set { Set(ref m_percentage, value); }
    }
    public bool IsRunning
    {
        get { return m_running; }
        set{Set(ref m_running, value);}
    }

    public void Initiate()
    {
        Description = string.Empty;
        Percentage = 0;
        IsRunning = true;
    }
}
// Command
private void DownloadWatch()
{
    DispatcherHelper.UIDispatcher.Invoke(() =>
    {
        Progress.Initiate();
    });
    using (var watch = new PolarWatch())
    {
        watch.Connect();
        for (var i = 0; i < watch.Sessions.Count; i++)
        {
            DispatcherHelper.UIDispatcher.Invoke(() =>
            {
                Progress.Description = $"Writing session data for '{session.DateTime}'...";
            });
        }
    }
    DispatcherHelper.UIDispatcher.Invoke(() =>
    {
        Progress.IsRunning = false;
    });
}
// View
<xctk:BusyIndicator Name="Busy" IsBusy="{Binding Progress.IsRunning}">
    <xctk:BusyIndicator.BusyContentTemplate>
        <DataTemplate>
            <Grid Width="150">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Progress.Description}"/>
                <ProgressBar Grid.Row="1" Grid.Column="0" Value="{Binding Progress.Percentage}" Height="14" Margin="0,5,0,0"/>
            </Grid>
        </DataTemplate>
    </xctk:BusyIndicator.BusyContentTemplate>
    <xctk:BusyIndicator.ProgressBarStyle>
        <Style TargetType="ProgressBar">
            <Setter Property="Visibility" Value="Collapsed"/>
        </Style>
    </xctk:BusyIndicator.ProgressBarStyle>

Solution

  • The DataContext of the root element in the ItemTemplate is not the same as the DataContext of the control itself. Try this:

    <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding DataContext.Progress.Description, RelativeSource={RelativeSource AncestorType=xctk:BusyIndicator}}"/>
    <ProgressBar Grid.Row="1" Grid.Column="0" Value="{Binding DataContext.Progress.Percentage,RelativeSource={RelativeSource AncestorType=xctk:BusyIndicator}}" Height="14" Margin="0,5,0,0"/>
    

    Or set the DataContext of the Grid:

    <Grid Width="150" DataContext="{Binding DataContext.Progress, RelativeSource={RelativeSource AncestorType=xctk:BusyIndicator}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Description}"/>
        <ProgressBar Grid.Row="1" Grid.Column="0" Value="{Binding Percentage}" Height="14" Margin="0,5,0,0"/>
    </Grid>