none
ProgressBar on BusyIndicator doesn't update when value changes

    Question

  • I have a BusyIndicator and I want to display a progress bar on it.

    Apparently although the default style of the BusyIndicator has a progress bar on it, you can't set its value so you have to add your own using BusyContentTemplate.

    Having done so, once again there doesn't seem to be any way to directly access the Value property of the ProgressBar, so you have to use a Binding.

    I have created a class to bind the ProgressBar to so that I can set its Value and Maximum, but the Value doesn't update on screen even though I've implemented INotifyPropertyChanged on my class.

    Here is my XAML:

            <toolkit:BusyIndicator Name="busy" BusyContent="{Binding}">
                <toolkit:BusyIndicator.BusyContentTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="Uploading..." HorizontalAlignment="Center" />
                            <ProgressBar Value="{Binding value}" Maximum="{Binding maximum}" Height="15" />
                        </StackPanel>
                    </DataTemplate>
                </toolkit:BusyIndicator.BusyContentTemplate>
                <!-- Remove unnecessary default ProgressBar -->
                <toolkit:BusyIndicator.ProgressBarStyle>
                    <Style TargetType="ProgressBar">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </Style>
                </toolkit:BusyIndicator.ProgressBarStyle>
                
                <Grid Name="grdPage">
            <toolkit:BusyIndicator Name="busy" BusyContent="{Binding}">
                <toolkit:BusyIndicator.BusyContentTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="Uploading..." HorizontalAlignment="Center" />
                            <ProgressBar Value="{Binding value}" Maximum="{Binding maximum}" Height="15" />
                        </StackPanel>
                    </DataTemplate>
                </toolkit:BusyIndicator.BusyContentTemplate>
    
                <!-- Remove unnecessary default ProgressBar -->
                <toolkit:BusyIndicator.ProgressBarStyle>
                    <Style TargetType="ProgressBar">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </Style>
                </toolkit:BusyIndicator.ProgressBarStyle>
                
                <Grid Name="grdPage">
                    <!-- Page contents in here -->
                </Grid>
            </toolkit:BusyIndicator>
    And my code:

    Public Class ProgressUpdater
        Implements INotifyPropertyChanged
    
        Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    
        Private pVal As Integer, pMax As Integer
    
        Public Property value As Integer
            Get
                Return pVal
            End Get
            Set(newvalue As Integer)
                pVal = newvalue
    
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("value"))
            End Set
        End Property
    
        Public Property maximum As Integer
            Get
                Return pMax
            End Get
            Set(newvalue As Integer)
                pMax = newvalue
    
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("maximum"))
            End Set
        End Property
    End Class

    Friday, May 20, 2011 1:19 PM

Answers

  • <ProgressBar Value="{Binding value}" Maximum="{Binding maximum}" Height="15" />

    You need to create a ValueChanged event to your ProgressBar, and check when the value comes to the maximum, set the IsBusy property of the BusyIndicator to false.

    Hope you can understand.

    Monday, May 23, 2011 11:25 PM

All replies

  • your binding code or xaml please?

    Friday, May 20, 2011 1:35 PM
  • An instance of ProgressUpdater is set as private in the code-behind class:

        Private pPU As New ProgressUpdater

    Then the binding is set in the constructor:

            busy.DataContext = pPU

    Then the maximum is set to the size of the file being uploaded and the BusyIndicator is shown:

            pPU.maximum = pFileLen
            busy.IsBusy = True
    

    Then every time a piece of the file is uploaded it sets the value of the ProgressUpdater to the offset in the file:

            pPU.value = pOffset
    

    I've put in breakpoints and the value does get updated, the progress bar just doesn't show any change.

    Friday, May 20, 2011 1:48 PM
  • I recall there was something to do with the blocking of UI main thread. Does it get updated eventually after the process code done?

    Friday, May 20, 2011 1:53 PM
  • No, at the end of the upload I set IsBusy to False and the BusyIndicator goes away, never having moved from its original value.

    I'd be surprised if it was blocking, since so much in Silverlight is asynchronous.

    Friday, May 20, 2011 2:02 PM
  • I maybe wrong on this. Sometimes my memory was a mess due to workload and love to hanging out on this board. Embarassed

    Friday, May 20, 2011 2:05 PM
  • Your code looks fine and this example does update the UI through the use of some test buttons.

    MainPage XAML:

    <UserControl x:Class="ProgressBar.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400">
    
        <StackPanel x:Name="LayoutRoot" Background="White">
            <toolkit:BusyIndicator Name="busy" BusyContent="{Binding}">
                <toolkit:BusyIndicator.BusyContentTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="Uploading..." HorizontalAlignment="Center" />
                            <ProgressBar Value="{Binding value}" Maximum="{Binding maximum}" Height="15" />
                        </StackPanel>
                    </DataTemplate>
                </toolkit:BusyIndicator.BusyContentTemplate>
    
                <!-- Remove unnecessary default ProgressBar -->
                <toolkit:BusyIndicator.ProgressBarStyle>
                    <Style TargetType="ProgressBar">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </Style>
                </toolkit:BusyIndicator.ProgressBarStyle>
    
                <Grid Name="grdPage">
                    <!-- Page contents in here -->
                </Grid>
            </toolkit:BusyIndicator>
            
            <Button Content="Go to 25" Click="Button_Click" />
            <Button Content="Go to 50" Click="Button_Click_1" />
            <Button Content="Go to 75" Click="Button_Click_2" />
            <Button Content="Go to 100" Click="Button_Click_3" />
        </StackPanel>
    </UserControl>
    

    MainPage code-behind:

    Imports System.ComponentModel
    
    Partial Public Class MainPage
        Inherits UserControl
    
        Private pPU As New ProgressUpdater
    
        Public Sub New()
            InitializeComponent()
    
            busy.DataContext = pPU
    
            pPU.maximum = 100
            busy.IsBusy = True
        End Sub
    
        Private Sub Button_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
            pPU.value = 25
        End Sub
    
        Private Sub Button_Click_1(sender As System.Object, e As System.Windows.RoutedEventArgs)
            pPU.value = 50
        End Sub
    
        Private Sub Button_Click_2(sender As System.Object, e As System.Windows.RoutedEventArgs)
            pPU.value = 75
        End Sub
    
        Private Sub Button_Click_3(sender As System.Object, e As System.Windows.RoutedEventArgs)
            pPU.value = 100
        End Sub
    End Class
    
    Public Class ProgressUpdater
        Implements INotifyPropertyChanged
    
        Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    
        Private pVal As Integer, pMax As Integer
    
        Public Property value As Integer
            Get
                Return pVal
            End Get
            Set(newvalue As Integer)
                pVal = newvalue
    
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("value"))
            End Set
        End Property
    
        Public Property maximum As Integer
            Get
                Return pMax
            End Get
            Set(newvalue As Integer)
                pMax = newvalue
    
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("maximum"))
            End Set
        End Property
    End Class

    How are you doing the file uploads? Are you sure this isn't happening on the UI thread? Are you using a BackgroundWorker? Please post the file upload code too so we can test that out.

    Brice 

    Friday, May 20, 2011 4:31 PM
  • The upload is done using a Web Service, so it's not anything I can send you. Undecided

    It's all asynchronous though.

    In the constructor I set up event handlers for the WS methods I'll be using:

            AddHandler api.startLargeDocumentUploadCompleted, AddressOf api_startLargeDocumentUploadCompleted
            AddHandler api.uploadLargeDocumentChunkCompleted, AddressOf api_uploadLargeDocumentChunkCompleted
            AddHandler api.finishUpload_CreatePolicyDocCompleted, AddressOf api_finishUpload_CreatePolicyDocCompleted
    

    Then when the user starts the upload I get the file length and then make an async call to tell the server to get ready to receive a file:

            api.startLargeDocumentUploadAsync(pFileLen)

    When the completed event is raised, I open the file that I'm uploading as a stream and then start uploading chunks of the file:

        Private Sub api_startLargeDocumentUploadCompleted(sender As Object, e As startLargeDocumentUploadCompletedEventArgs)
            If e.Error Is Nothing Then
                pUploadID = e.Result
    
                Try
                    pUploadStream = pFileToUpload.OpenRead()
                Catch ex As IOException
                    enableControls()
    
                    Dim frmMsgBox As New MessageBox("The selected file is locked by another program.  Please close the file and try again.", "New Policy", MessageBox.MessageBoxIcon.ErrorIcon, MessageBox.MessageBoxType.OK)
                    frmMsgBox.Show()
    
                    busy.IsBusy = False
    
                    Return
                End Try
    
                uploadChunk()
            Else
                busy.IsBusy = False
                pUploadStream.Close()
    
                Dim se As SOAPException = defaultExceptionHandler(e.Error)
                If Not se Is Nothing Then
                    Throw se
                End If
            End If
        End Sub

    To upload a chunk of the file I first read some of the file asynchronously:

        Private Sub uploadChunk()
            If pOffset = pFileLen Then
                pUploadStream.Close()
    
                If pUploadType = UploadType.Document Then
                    api.finishUpload_CreatePolicyDocAsync(pUploadID, pFileToUpload.Extension, pPolicy)
                End If
    
                Return
            End If
    
            If pFileLen - pOffset < pChunkLen Then
                pChunkLen = pFileLen - pOffset
            End If
    
            Array.Resize(pChunk, pChunkLen)
    
            pUploadStream.BeginRead(pChunk, 0, pChunkLen, AddressOf cbUploadStreamRead, Nothing)
    
            pOffset += pChunkLen
    
            pPU.value = pOffset
        End Sub

    When the completed event is raised, I upload that chunk of the file with another async call:

        Private Sub cbUploadStreamRead(ar As IAsyncResult)
            pUploadStream.EndRead(ar)
    
            api.uploadLargeDocumentChunkAsync(pUploadID, pChunk)
        End Sub

    When that completed event is raised, I call the method to read and upload the next chunk.

        Private Sub api_uploadLargeDocumentChunkCompleted(sender As Object, e As AsyncCompletedEventArgs)
            If e.Error Is Nothing Then
                uploadChunk()
            Else
                pUploadStream.Close()
    
                Dim se As SOAPException = defaultExceptionHandler(e.Error)
                If Not se Is Nothing Then
                    Throw se
                End If
            End If
        End Sub
    When the whole file has been uploaded, the uploadChunk method makes a different async call to tell the server that the upload is finished and how to file the document.

    Friday, May 20, 2011 4:52 PM
  • <ProgressBar Value="{Binding value}" Maximum="{Binding maximum}" Height="15" />

    You need to create a ValueChanged event to your ProgressBar, and check when the value comes to the maximum, set the IsBusy property of the BusyIndicator to false.

    Hope you can understand.

    Monday, May 23, 2011 11:25 PM
  • That would automatically switch off the BusyIndicator when the progress reached 100%, but doesn't address my actual problem, which is the progress bar not updating when the value changes

    Wednesday, May 25, 2011 8:58 AM
  • Wow, actually just implementing the ValueChanged event got it working

    Wednesday, May 25, 2011 1:03 PM
  • Hi! I have the same problem, I've tried that you did but I still getting the progress bar without moving....can you help me please?

    Friday, September 09, 2011 2:45 PM
  • Hi - I got everything working fine except 1 piece with updating the status text in the textblock tag. I use PropertyChanged event to set the value for the progressbar successfully. When I try to do the same for the textblock Text property, it never updates.
     <TextBlock Text="{Binding BusyText}" /> //this doesnt work
     <ProgressBar Value="{Binding value}" Height="15"/> // this works fine
    Monday, December 30, 2013 3:36 PM