【问题标题】:WPF - update UI asynchronously with ObservableCollection [duplicate]WPF - 使用 ObservableCollection 异步更新 UI [重复]
【发布时间】:2020-05-13 18:01:43
【问题描述】:

我有 3 个不同的 ObservableCollections 绑定到我的视图。我将它们传递给循环编辑它们的函数,我想在我的视图中显示这些集合中所做的每一个更改。

所有绑定都在工作,我唯一的问题是 UI 仅在函数结束时更新,所以我只能在更改后显示这些集合。

有我的功能,它是解决 TSP 问题的 NearestNeighbour 算法的实现,我想在我的视图中打印解决它的每一步。

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;
}        

我有一个使用ComponentDispatcher 的想法,当我用它替换我的do{...}while() 时它工作得很好,但是你可以看到我需要另一个循环来进行计算。因此,我只能打印当前顶点,以及每一步到下一个顶点的路径。我还想打印当前在foreach(..) 循环中检查的每条边。

有人可以帮我吗?我还想实现 A* 算法和模拟退火,因此解决方案不应仅限于使用该功能。

【问题讨论】:

  • 嗨,谢夫,这个代码块在 BackgroundWorker 中运行?如果没有,应该是!
  • 您好,感谢您的回答。我不确定如何在这里使用BackgroundWorker。我正在使用 MVVM 模板,当我想要报告的唯一进度是集合的更改时,我不知道如何使用 BackgroundWorker 报告进度,由于IObserverinterface,它应该自动更改 UI。
  • 当我尝试以某种方式使用 BackgroundWorker 时,我得到一个异常,说类型 CollectionView 只能从 Dispatcher 线程更改

标签: c# asynchronous observablecollection dispatcher


【解决方案1】:

谢夫,

使用BackgroundWorker和Dispatcher刷新ObservableCollection的Binding的示例代码

数据网格 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>

代码背后

/// <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; }
}

【讨论】:

  • 没有解释的代码转储是没有用的,尤其是当您发布一个本身无用的问题的答案并且已经在 Stack Overflow 上与许多现有答案重复时。
  • 就像我在之前的评论中所说的那样,我正在使用 MVVM,所以我想避免代码隐藏。同样在这种情况下 BackgroundWorker 不工作
  • 好的,彼得,下次我会注意解释代码。
猜你喜欢
  • 2019-03-16
  • 2021-11-15
  • 1970-01-01
  • 2013-06-08
  • 1970-01-01
  • 2014-02-16
  • 1970-01-01
  • 2019-12-16
相关资源
最近更新 更多