0

I have 3 different ObservableCollections which are binded to my view. I'm passing them to function where they are being edited in a loop and I want to show every single change made in these collections in my view.

All bindings are working, the only problem I have is the UI updating only when function ends so I can display these collections only after changes.

There is my function, it's implementation of NearestNeighbour algorithm to solve TSP problem and I want to print every step of solving it in my view.

public int TSP(ObservableCollection<City> VisitedCities, ObservableCollection<Edge> CurrentEdges, ObservableCollection<Edge> FinalEdges)
{
    int bestDistance = 0;
    City currentCity = cities.First();
    cities.RemoveAt(0);
    VisitedCities.Add(new City(currentCity.X, currentCity.Y, currentCity.Number));
    int minWeight = int.MaxValue;
    City tmp = currentCity;
    do
    {
        foreach(City city in cities)
        {
           if (minWeight > neighbourMatrix[currentCity.Number, city.Number] && neighbourMatrix[currentCity.Number,city.Number] !=0)
           {
               minWeight = neighbourMatrix[currentCity.Number, city.Number];
               tmp = city;
           }
            CurrentEdges.Add(new Edge(currentCity.X, currentCity.Y, city.X, city.Y, neighbourMatrix[currentCity.Number, city.Number]));
        }
        FinalEdges.Add(new Edge(currentCity.X, currentCity.Y, tmp.X, tmp.Y, neighbourMatrix[currentCity.Number, tmp.Number]));
        bestDistance += neighbourMatrix[currentCity.Number, tmp.Number];
        CurrentEdges.Clear();
        VisitedCities.Add(new City(tmp.X, tmp.Y, tmp.Number));
        currentCity = new City(tmp.X, tmp.Y, tmp.Number);
        cities.Remove(tmp);
        minWeight = int.MaxValue;

    } while (cities.Any());
    FinalEdges.Add(new Edge(VisitedCities.Last().X, VisitedCities.Last().Y, VisitedCities.First().X, VisitedCities.First().Y, neighbourMatrix[VisitedCities.Last().Number, VisitedCities.First().Number]));
    return bestDistance;
}        

I got an idea to use ComponentDispatcher and it worked fine when I replaced my do{...}while() with it, but as you can see there is another loop which I need for calculations. Because of that I could only print current Vertex, and the path to next Vertex every step. I want also print every edge which is currently checked in the foreach(..) loop.

Can someone help me with that? I also want to implement A* algorithm and simulated Annealing, so the solution shouldn't be limited to work only with that function.

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
shefff
  • 117
  • 7
  • Hi shefff, this code block is running inside a BackgroundWorker? If not, it should be! – Gustavo Oliveira May 13 '20 at 18:20
  • Hi, thanks for your answer. I'm not sure how to use the `BackgroundWorker` here. I'm working with MVVM template and I don't know how to report progress with the BackgroundWorker when the only progress I want to report is the change of collections, which automatically should change the UI because of `IObserver`interface. – shefff May 13 '20 at 19:45
  • When I tried somehow use the BackgroundWorker I got an exception saying that type `CollectionView` can be changed only from Dispatcher thread – shefff May 13 '20 at 20:02

1 Answers1

-1

Shefff,

Sample code using BackgroundWorker and Dispatcher to refresh the Binding of ObservableCollection

DataGrid XAML

    <DataGrid HorizontalAlignment="Left" 
              Height="203" 
              Margin="175,126,0,0"
              DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}" 
              ItemsSource="{ Binding Path=Persons }" 
              VerticalAlignment="Top" 
              Width="378" >
        <DataGrid.Resources>
            <Style TargetType="DataGridRow">
                <EventSetter Event="MouseDoubleClick" Handler="Row_DoubleClick"/>
            </Style>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
            <DataGridTextColumn Header="LastName" Binding="{Binding LastName}"/>
            <DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
        </DataGrid.Columns>
    </DataGrid>

Code Behind

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Row_DoubleClick(object sender, MouseButtonEventArgs e)
    {
        var dbClickedRow = (DataGridRow)sender;
        MessageBox.Show(((Person)dbClickedRow.DataContext).Name);
    }

    public ObservableCollection<Person> Persons { get; set; } = new ObservableCollection<Person>();

    private void Worker_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 0; i < 500; i++)
        {
            Thread.Sleep(1000);
            Dispatcher.Invoke(() => Persons.Add(new Person { Age = i, Name = "SomeName" + i, LastName = "SomeLastName" + i }));
        }
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        Persons.Add(new Person { Name = "Gustavo", LastName = "Oliveira", Age = 35 });
        Persons.Add(new Person { Name = "Another", LastName = "Person", Age = 23 });
        Persons.Add(new Person { Name = "Neymar", LastName = "Junior", Age = 28 });

        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += Worker_DoWork;
        worker.RunWorkerAsync();
    }
}

public class Person
{
    public string Name { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}
  • Code dumps without explanation are not useful, especially when you are posting an answer to a question that itself is not useful and a duplicate with many existing answers on Stack Overflow already. – Peter Duniho May 13 '20 at 21:43
  • Like I said in my previous comment I'm using MVVM so I would like to avoid code-behind. Also in this case BackgroundWorker is not working – shefff May 13 '20 at 21:49
  • Ok Peter, next time i'll pay attention in explain the code. – Gustavo Oliveira May 13 '20 at 21:52