RxUI填充ObservableCollection异步

时间:2013-06-17 21:56:29

标签: c# thread-safety observablecollection dispatcher reactiveui

是否存在将项添加到ReactiveAsyncCommand中的ObservableCollection或是否必须使用Dispather的方法?什么是RxUI处理这种情况的方式?

编辑:当LoadData正在运行时,这些项目应逐个加载到集合中。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        MaxRows = 100;
        Translations = new ReactiveCollection<string>();

        LoadDataCommand = new ReactiveAsyncCommand();

        LoadDataCommand.RegisterAsyncAction(_ => LoadData());

        InitializeComponent();

        this.DataContext = this;
    }

    private void LoadData()
    {
        for (int i = 1; i < MaxRows; ++i)
        {
            Thread.Sleep(100);
            // What's the RxUI Way for this?
            this.Dispatcher.Invoke(() => Translations.Add(i.ToString()));
        }
    }

    public int MaxRows { get; set; }
    public ReactiveCollection<string> Translations { get; set; }
    public ReactiveAsyncCommand LoadDataCommand { get; set; }
}

1 个答案:

答案 0 :(得分:1)

放手一搏:

public MainWindow()
{
    InitializeComponent();
    MaxRows = 100;
    Translations = new ReactiveCollection<string>();

    LoadDataCommand = new ReactiveAsyncCommand();

    LoadDataCommand.RegisterAsyncFunction(_ => LoadData())
        .Subscribe(items => 
        {
            foreach(var i in items) Translations.Add(i);
        })

    LoadDataCommand.ThrownExceptions.Subscribe(ex => 
    {
        Console.WriteLine("Oh crap: {0}", ex);
    })

    InitializeComponent();

    this.DataContext = this;
}

private List<int> LoadData()
{
    // TODO: Return the list of stuff you want to add
    return Enumerable.Range(0, maxRows).ToList();
}

这个想法是,LoadData将在后台线程上调用,你可以在那里做任何你想做的事情,阻止网络调用,读取文件等。但是,RegisterAsync *的订阅是保证在UI线程上运行。方便!

注意,我还添加了ThrownExceptions订阅 - 这很重要,否则如果LoadData失败,您的RegisterAsync *调用将停止工作。

编辑:好的,现在你有点花哨了。虽然我在讨论像这样的结果流式传输的UI的实用程序(尝试在不断移动的ListBox中选择内容令人沮丧),但我们仍然这样做 - 首先,将LoadData更改为流式传输结果:

private IObservable<int> LoadData()
{
    var ret = new Subject<int>();

    // Run something in the background
    Observable.Start(() => 
    {
        try 
        {
            for(int i=0; i < 100; i++) 
            {
                // TODO: Replace with streaming items
                ret.OnNext(10);
                Thread.Sleep(100);
            }

            ret.OnCompleted();
        }
        catch (Exception ex) 
        {
            ret.OnError(ex);
        }
    }, RxApp.TaskpoolScheduler)

    return ret;
}

然后,您所做的只是在命令上更改几行:

// Now it's RegisterAsyncObservable instead
LoadDataCommand.RegisterAsyncObservable(_ => LoadData())
    .Subscribe(item => 
    {
        Translations.Add(item);
    })

请注意,这不是很强大的功能™,但我想给你一些容易开始的东西。