Привязка реактивного пользовательского интерфейса с асинхронным сбоем обновления модели представленияC#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Привязка реактивного пользовательского интерфейса с асинхронным сбоем обновления модели представления

Сообщение Anonymous »

У меня вопрос о Reactive ui, его привязках и о том, как он обрабатывает обновления пользовательского интерфейса. Я всегда предполагал, что использование ReactiveUi позаботится обо всех обновлениях пользовательского интерфейса в потоке пользовательского интерфейса. Но недавно я обнаружил, что это не всегда так.

Короче говоря, вопрос таков: как я могу использовать reactiveui для двусторонней привязки модели представления и представление и убедиться, что обновление ViewModel не приводит к сбою при запуске в потоке, отличном от ui-потока? Без необходимости вручную подписываться на изменения и явно обновлять uiThread, поскольку это противоречит цели reactiveui, а также усложняет инкапсуляцию всей логики в PCL.

Ниже я представил очень простой (Android) проект с использованием Xamaring и Reactiveui, позволяющий сделать следующее:
  • Кнопка с текстом ' Привет, мир!
  • При нажатии на нее к тексту кнопки добавляется буква «a».
  • Я позволяю Activity реализовать IViewFor и использую производную ViewModel из ReactiveObject, содержащего текст, который я хочу изменить.
  • Я привязываю кнопку Activity button.Text к ViewModel.Text, чтобы позволить reactiveui обрабатывать все изменения и обновления пользовательского интерфейса.
    Наконец, я добавляю функцию при нажатии кнопки, чтобы добавить букву «a» в ViewModel.
У меня возникла следующая проблема:

button.Click += delegate
{
this.ViewModel.Text += "a"; // does not crash
Task.Run(() => { this.ViewModel.Text += "a"; }); // crash
};


Непосредственное добавление «a» не является проблемой. Однако добавление 'a' в другой поток приводит к известному исключению Java: Исключение: только исходный поток, создавший иерархию представлений, может касаться своих представлений.

Я понимаю исключение и откуда оно взялось. Фактически, если бы я добавил букву «а» в другой поток, у меня уже все работало бы, просто не привязывая текст. А лучше подписаться на изменения и использовать метод RunOnUiThread для внесения изменений в пользовательский интерфейс. Но этот сценарий противоречит цели использования ReactiveUi. Мне очень нравится чистый способ кодирования простого оператора «this.Bind(ViewModel, x => x.Text, x => x.button.Text);», но если это нужно запустить в uiThread я не понимаю, как заставить его работать.

И, естественно, это минимум, чтобы показать проблему. Настоящая проблема, почему я поднимаю этот вопрос, заключается в том, что я хочу использовать метод GetAndFetchLatest из akavache. Он асинхронно получает данные, кэширует их и выполняет функцию (обновляя ViewModel). Если данные уже находятся в кеше, он выполнит обновление ViewModel с кэшированным результатом И выполнит логику вычислений в другом потоке, а затем снова вызовет ту же функцию, как только она будет выполнена (что приведет к сбою, поскольку это происходит в другом потоке). поток обновляет ViewModel, что приводит к сбою).

Обратите внимание, что хотя явное использование RunOnUiThread работает, я действительно не хочу (даже не могу) вызовите это в ViewModel. Потому что у меня есть более сложный фрагмент кода, в котором кнопка просто сообщает ViewModel, что нужно получить данные и обновиться. Если бы мне пришлось сделать это в uiThread (т. е. после того, как я получил данные обратно, я обновил ViewModel), то я больше не могу привязать iOS к той же ViewModel.

И наконец, вот весь код, вызывающий сбой. Я видел, как часть Task.Run иногда работает, но если вы добавите еще несколько задач и продолжите обновлять в них ViewModel, в конечном итоге произойдет сбой в потоке пользовательского интерфейса.

public class MainActivity : Activity, IViewFor
{
public RandomViewModel ViewModel { get; set; }
private Button button;

protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);

SetContentView(Resource.Layout.Main);

this.button = FindViewById(Resource.Id.MyButton);

this.ViewModel = new RandomViewModel { Text = "hello world" };
this.Bind(ViewModel, x => x.Text, x => x.button.Text);

button.Click += delegate
{
this.ViewModel.Text += "a"; // does not crash
Task.Run(() => { this.ViewModel.Text += "a"; }); // crash
};
}

public class RandomViewModel : ReactiveObject
{
private string text;

public string Text
{
get
{
return text;
}
set
{
this.RaiseAndSetIfChanged(ref text, value);
}
}
}

object IViewFor.ViewModel
{
get
{
return ViewModel;
}
set
{
ViewModel = value as RandomViewModel;
}
}
}


Подробнее здесь: https://stackoverflow.com/questions/329 ... te-crashes
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»