Now I added a clear button to the example that will clear the input textbox when pressed. I had to change a bit of the implementation to accomplish this. I can now no longer use a CellProperty as i now have to use 2 streams. http://sodium.nz/t/logic-for-clearing-a-cell/119
So the code for the model is this
class Model
{
public readonly Cell<string> OutString;
public readonly Cell<string> InputString;
public readonly Cell<bool> CanClear;
public Model(Stream<object> sClicked, Stream<string> sText)
{
CanClear = sText.Map(s => !string.IsNullOrEmpty(s)).Hold(false);
InputString = sClicked.MapTo(string.Empty).OrElse(sText).Hold(string.Empty);
OutString = InputString.Map(s => s.ToUpper());
}
}
OutString is the same as before. InputString is the content of the TextBox that can now be cleared by clicking the button.
CanClear is used for the clear command and will disable the button if the textbox is empty.
The ViewModel looks now like this:
class ViewModel : ReactiveObject
{
private readonly ReadOnlyCellProperty<string> _out;
private readonly ReadOnlyCellProperty<string> _in;
private readonly StreamSink<string> _inputStream;
public ViewModel(Cell<string> inputCell, StreamSink<object> sClear, Cell<bool> canClear, StreamSink<string> sText, Cell<string> outCell)
{
_inputStream = sText;
_in = Attach(inputCell, nameof(In));
_out = Attach(outCell, nameof(Out));
ClearCommand = new StreamCommand(sClear, canClear);
}
public ICommand ClearCommand { get; private set; }
public string In
{
set
{
_inputStream.Send(value);
}
get
{
return _in;
}
}
public string Out
{
get
{
return _out;
}
}
}
As you can see In-property changed quiet a bit and is now a combination of a StreamSink for the Setter and a ReadOnlyCellProperty for the Getter.
I also created a StreamCommand that can be used for binding a button to a stream. Please note that all the logic is kept inside the model. The ViewModel is only responseable for preparing data and alike for the View.
Here is the StreamCommand
internal class StreamCommand : ICommand
{
private bool _canExecute;
private readonly StreamSink<object> _sExecute;
public StreamCommand(StreamSink<object> sExecute, Cell<bool> canExecute)
{
_sExecute = sExecute;
//we only care about changes, so calm down
canExecute.Calm().Listen(b =>
{
_canExecute = b;
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
});
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute;
}
public void Execute(object parameter)
{
_sExecute.Send(parameter);
}
}
The construction is pretty simple:
StreamSink<object> sClear = new StreamSink<object>();
StreamSink<string> sText = new StreamSink<string>();
Model m = new Model(sClear, sText);
_vm = new ViewModel(m.InputString, sClear, m.CanClear, sText, m.OutString);