thiseful thiseful - 2 months ago 15
C# Question

How can I refresh ItemsControl in wpf to show progress bar animations

Here is my merge algorithm which I call using a background Thread:

public static class MergeAlghorithm
{


public static void mergeSort(int[] vector, int n)
{
int curr_size;
int left_start;

for (curr_size = 1; curr_size <= n - 1; curr_size = 2 * curr_size)
{
for (left_start = 0; left_start < n - 1; left_start += 2 * curr_size)
{


int mid_point = left_start + curr_size - 1;
int right_end = Math.Min(left_start + 2 * curr_size - 1, n - 1);

Merge(vector, left_start, mid_point, right_end);

}
}
}

public static void Merge(int[] vector, int left, int mid, int right)
{
int i, j, k;
int n1 = mid - left + 1;
int n2 = right - mid;

int[] L = new int[150];
int[] R = new int[150];



for (i = 0; i < n1; i++)
{
L[i] = new int();
L[i] = vector[left + i];

}
for (j = 0; j < n2; j++)
{
R[j] = new int();
R[j] = vector[mid + 1 + j];
}

i = 0;
j = 0;
k = left;

while (i < n1 && j < n2)
{
if (L[i] <= R[j])
{
vector[k] = L[i];
var nr = L[i];
i++;

}
else
{
vector[k] = R[j];
j++;

}
k++;
}

while (i < n1)
{
vector[k] = L[i];
i++;
k++;

}
while (j < n2)
{
vector[k] = R[j];
j++;
k++;

}
}
}


Here is my MaiWindow code where I populate the array and call the StarMerge method with a backgound thread in order to refresh the UI :

public partial class MainWindow : Window
{
public static List<Number> items = new List<Number>();
public int[] a { get; set; } = new int[150];
public MainWindow()
{
InitializeComponent();
Random randomNumber = new Random();
// populate the array with random numbers
for (int i = 0; i < a.Length; i++)
{
a[i] = new int();
a[i] = randomNumber.Next(1, 200);
}

StartMerge(a, 100);// her I start the merge alghorithm
for (int i = 0; i < 100; i++)
{
items.Add(new Number() { Title = $"{a[i]}", Completion = a[i] });
}
SortedNumbers.ItemsSource = items;
}
// using the below method I tried to run the alghorithm in a background thread
public Thread StartMerge(int[] vector, int n)
{
var thread = new Thread(() => MergeAlghorithm.mergeSort(vector, n));
thread.IsBackground = true;
thread.Start();
return thread;
}

//with this I created a List on which I have the binding
public class Number
{
public string Title { get; set; }
public int Completion { get; set; }
}

}


And the XAML where I have the binding on Completion from the Listitems:

<Window x:Class="WpfApplication1.MainWindow"
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:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title=" MainWindow" Height="800" Width="400">
<Grid Margin="10">
<ItemsControl Name="SortedNumbers">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5,0,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="800" />
</Grid.ColumnDefinitions>
<ProgressBar Grid.Column="1" Minimum="0" Maximum="600" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>


And this is what I get in MainWindow:

100 progressbars

Answer

A simple way to get the animation would be to move creating & assigning the items list into the Merge method

public static void mergeSort(int[] vector, int n, ItemsControl ic)
{
  int curr_size;
  int left_start;

  for (curr_size = 1; curr_size <= n - 1; curr_size = 2 * curr_size)
  {
    for (left_start = 0; left_start < n - 1; left_start += 2 * curr_size)
    {
      int mid_point = left_start + curr_size - 1;
      int right_end = Math.Min(left_start + 2 * curr_size - 1, n - 1);

      Merge(vector, left_start, mid_point, right_end);

      List<Number> items = new List<Number>();
      for (int i = 0; i < n; i++)
      {
        items.Add(new Number() { Title = $"{vector[i]}", Completion = vector[i] });
      }
      ic.Dispatcher.Invoke(() => ic.ItemsSource = items);
    }
  }
}

Note that a new List is created each time, otherwise the ItemsSource will not be seen to change so the GUI will not be updated.

If recreating the list each loop were to be too time consuming (maybe due to much bigger arrays) then you would have to change the Number class so that it contained references to the array you are sorting - you could not use an integer array for sorting in that case, as it is not possible to make references to value types. You could then create the original items list in your main method as you do now & just ensure that the ItemsControl is forced to update each loop.

Comments