Я перехожу с реактивной версии 4.5 на 6.5.0 и сталкиваюсь с некоторыми проблемами. У меня есть приложение WPF с кнопкой, привязанной к ReactiveCommand. Раньше я использовал такой конструктор ReactiveCommand:
_runProcessCommand = new ReactiveCommand(CanRunProcess(null));
_runProcessCommand.Subscribe(RunImpl);
public IObservable<bool> CanRunProcess(object arg)
{
return this.WhenAny( ... )
}
Теперь я изменил его на:
_runProcessCommand = ReactiveCommand.Create(CanRunProcess(null));
_runProcessCommand..Subscribe(RunImpl);
Поэтому я ожидал, что поведение должно быть точно таким же, но это не так. Моя кнопка отключена, пока я не изменю что-нибудь из WhenAny in CanRunProcess
bound, которое в основном является свойствами пользовательского интерфейса. Это происходит во многих местах проекта, поэтому нет никакой ошибки. Есть ли что-нибудь различное между этими двумя способами создания ReactiveCommand? Как добиться такого же результата? Самое смешное, что когда я подписываюсь на CanExecuteObservable, он работает как положено:
_runProcessCommand.CanExecuteObservable.Subscribe(x =>
{
Debug.WriteLine(x);
});
и то же самое, когда я явно вызываю CanExecute:
var c = _runProcessCommand.CanExecute(null);
Я предполагаю, что это может быть связано с ленью, но я не понимаю, почему это так, потому что кнопка должна вызывать CanExecute для получения текущего начального значения.
Когда я подписываюсь на CanRunProcess, я получаю много ложных ошибок, за которыми следует много истин, и последнее значение истинно, что, как я подозреваю, должно активировать команду.
CanRunProcess(null).Subscribe(x =>
{
Debug.WriteLine(x);
});
РЕДАКТИРОВАТЬ: Я загрузил источники ReactiveUI и заметил, что подписки на canExecute нет, но вместо этого используется Do
function:
this.canExecute = canExecute.CombineLatest(isExecuting.StartWith(false), (ce, ie) => ce && !ie)
.Catch<bool, Exception>(ex => {
exceptions.OnNext(ex);
return Observable.Return(false);
})
.Do(x => {
var fireCanExecuteChanged = (canExecuteLatest != x);
canExecuteLatest = x;
if (fireCanExecuteChanged) {
this.raiseCanExecuteChanged(EventArgs.Empty);
}
})
.Publish();
Похоже, что-то нужно создать экземпляр - что-то нужно вызвать
либо CanExecuteObservable
, либо CanExecute
для создания экземпляра объекта canExecute. Почему он не создается, когда вы привязываете его к кнопке?
После отладки источников ReactiveUI я точно знаю, что происходит. Do
- ленивая функция, поэтому, пока не будет вызвана connect
функция, обработчик не будет выполняться. Это означает, что canExecuteLatest
будет ложным, когда команда привязана к кнопке и при вызове функции CanExecute
, поэтому кнопка остается отключенной.
Воспроизводимый пример (обратите внимание, что он работает, когда я делаю тот же пример с WhenAny):
public class MainViewModel : ReactiveObject
{
private ReactiveCommand<object> _saveCommand;
private string _testProperty;
private ReactiveList<string> _ReactiveList;
public ReactiveCommand<object> SaveCommand
{
get
{
return _saveCommand;
}
set { this.RaiseAndSetIfChanged(ref _saveCommand, value); }
}
public ReactiveList<string> ReactiveList
{
get
{
return _ReactiveList;
}
set { this.RaiseAndSetIfChanged(ref _ReactiveList, value); }
}
public MainViewModel()
{
ReactiveList = new ReactiveList<string>();
ReactiveList.ChangeTrackingEnabled = true;
SaveCommand = ReactiveCommand.Create(CanRunSave(null));
SaveCommand.Subscribe(Hello);
// SaveCommand.CanExecute(null); adding this line will invoke connect so the next line will run CanSave and enable the button.
ReactiveList.Add("sad");
}
public void Hello(object obj)
{
}
private IObservable<bool> CanRunSave(object arg)
{
return ReactiveList.Changed.Select(x => CanSave());
}
private bool CanSave()
{
return ReactiveList.Any();
}
}
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="test" Command="{Binding SaveCommand}" />
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
Кнопка по-прежнему отключена, хотя я что-то добавляю в ReactiveList. Проблема в том, что обновления между созданием команды и привязкой ее к кнопке игнорируются, потому что соединение не было вызвано, поэтому изменения просто не отражаются.
this.WhenAny
, всегда должен возвращать начальное значение, поэтому это не должно быть важным. Я думаю, вам нужно привести некоторые подробности, желательно минимально воспроизводимый пример. - person Charles Mager   schedule 10.05.2016CanExecute
наблюдаемое подключается. Если первым результатом вашего наблюдаемого являетсяtrue
, тогда он будет отличаться отcanExecuteLatest
и будет обновлен / событие будет вызвано / будет возвращеноtrue
. Вы сами это проверили, позвонивCanExecute
, и он вернулtrue
. Вся «привязка» вызывает этот метод для установки включенного состояния. - person Charles Mager   schedule 10.05.2016WhenAny
, вернет «много ложных слов, за которыми следует много истин» - вероятно, что-то в способе определения наблюдаемого объекта объясняет проблему. - person Charles Mager   schedule 10.05.2016WhenAny
. - person Charles Mager   schedule 10.05.2016