TodoListViewwe added an "Add an item" button. It's time now to make that button do something. When the button is clicked we want to replace the list of items with a new view which will allow the user to enter the description of a new item.
UserControlusing a template):
<TextBox>control which is a control that allows a user to input text. We set three properties on it:
AcceptsReturncreates a multi-line
Textbinds the text that is displayed in the
Descriptionproperty on the view model
Watermarkcauses a placeholder to be displayed when the
Descriptionproperty that the
TextBoxis bound to for starters. We'll add to this as we go along.
TodoListViewin the window and show the
AddItemView. We can alter the
MainWindowViewModelto let us do this:
Contentproperty which is initially set to our list view model. When the
AddItem()method is called, we assign an
Contentproperty setter calls
RaiseAndSetIfChangedwhich will cause a change notification to be fired each time the property changes value. Avalonia's binding system needs change notifications in order to know when to update the user-interface in response to a property change.
Window.Contentproperty to this new
Contentproperty instead of the
Listproperty that it is currently bound to:
Button.Commandproperty describes a command to be called when the button is clicked
$parent[Window]means find an ancestor control of type
MainWindowViewModelin this case)
AddItemmethod on that view model
MainWindowViewModel.AddItem()method to be invoked when the button is clicked.
Button.Commandto a method. This is a convenience feature of Avalonia which means that you don't have to create an
ICommandfor simple commands that are always enabled.
Description, and for subsequent changes
string.IsNullOrWhiteSpace()with the value
okEnabledrepresents a stream of
boolvalues which will produce
Descriptionis a non-empty string and
falsewhen it is an empty string. This is exactly how we want the
OKbutton to be enabled.
ReactiveCommandand assign it to the
ReactiveCommand.Createcontrols the enabled state of the command, and so the observable we just created is passed there.
TodoItemwith the description entered by the user.
Cancelcommands we just created on the view model:
ReactiveCommandis itself an observable which produces a value each time the command is executed. You'll notice than when we defined the commands they had slightly different declarations:
ReactiveCommandspecifies the type of result it produces when the command is executed.
Unitis the reactive version of
void. It means the command produces no value.
Observable.Mergecombines the output from any number of obervables and merges them into a single observable stream. Because they're being merged into a single stream, they need to be of the same type. For this reason we call
vm.Cancel.Select(_ => (TodoItem)null): this has the effect that every time the
Cancelobservable produces a value we select a
Take(1)means "just take the first value produced by the observable sequence".
modelbeing produced (i.e. OK was clicked) then add this model to the list. We then set
Listin order to display the list in the window and hide the "AddItemView".