Refreshing XAML items without using Refresh() meth

2019-09-20 07:22发布

问题:

I'm using C # WPF.

I'm trying to figure out if there's a way to update the XAML elements without having to use the Refresh() method that you see in the following code.

Now I am encasing the codes related to the C# doPlay() method that is launched inside the MainWindow through a thread and the XAML code.

Can anyone suggest a way to update progress bars without having to use the Refresh() method?

   private void doPlay()
    {

        Transfers.Add(new Transfer(Utils.RandomString(6), Utils.RandomString(6), Transfer.Type_t.download, "Red"));
        Transfers.Add(new Transfer(Utils.RandomString(6), Utils.RandomString(6), Transfer.Type_t.download, "Blue"));
        Transfers.Add(new Transfer(Utils.RandomString(6), Utils.RandomString(6), Transfer.Type_t.download, "Yellow"));
        Transfers.Add(new Transfer(Utils.RandomString(6), Utils.RandomString(6), Transfer.Type_t.download, "Cyan"));
        Transfers.Add(new Transfer(Utils.RandomString(6), Utils.RandomString(6), Transfer.Type_t.download, "Black"));
        Transfers.Add(new Transfer(Utils.RandomString(6), Utils.RandomString(6), Transfer.Type_t.download, "Brown"));

        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
        {
            TransfersXAML.ItemsSource = Transfers;
        }));

        Random rnd = new Random();

        for (var i = 0; i < 100 * Transfers.Count; i++)
        {
            var next = rnd.Next(0, Transfers.Count);
            mre.WaitOne();
            int index =  next;

            if (Transfers[index].CurrentStep < 100)
            {
                Thread.Sleep(100);
                Transfers[index].CurrentStep++;

                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
                {
                    TransfersXAML.Items.Refresh();
                }));

            }
            else i--;
        }
     }

The XAML relative code:

        <UniformGrid x:Name="DownLeftPanel" Grid.Column="2" Grid.Row="2">
            <ListBox x:Name="TransfersXAML" HorizontalContentAlignment="Stretch" ItemsSource="{Binding Transfers}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <ProgressBar Height="30" Minimum="0" Maximum="{Binding NSteps}"
                                     Value="{Binding CurrentStep}" Foreground="{Binding Color}" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
                <ListBox.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Get info" Click="GetTransferInfoClick" />
                        <MenuItem Header="Cancel" Click="CancelTransferClick" />
                    </ContextMenu>
                </ListBox.ContextMenu>
            </ListBox>
        </UniformGrid>

I tried to simply remove the Action from BeginInvoke, but in this way the bars are no longer displayed, that is, removing these instructions from the first posted code:

Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
     TransfersXAML.Items.Refresh();
}));

This question is related to the following one: Network threads blocking the GUI, in particular, referring to the comments below the question itself.

回答1:

If the Transfers class implements the INotifyPropertyChanged interface and raise the PropertyChanged event in the setter of the CurrentStep property, you could simply set this property on the background thread without ever calling the Refresh() method:

for (var i = 0; i< 100 * Transfers.Count; i++)
{
    var next = rnd.Next(0, Transfers.Count);
    mre.WaitOne();
    int index = next;

    if (Transfers[index].CurrentStep < 100)
    {
        Thread.Sleep(100);
        Transfers[index].CurrentStep++;
    }
    else i--;
}