I have a listbox with a couple of buttons underneath.
<ListBox ItemsSource="{Binding SongList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=.}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Orientation="Horizontal">
<Button Content="Add" Command="{Binding addSongCommand}" />
<Button Content="Remove"/>
</StackPanel>
When I click the add button I want the add and remove buttons to be replaced by a textbox and submit button.
Then when I click submit i want it to add the entered string into the collection (SongList) and bring back the add and remove buttons.
How would the hiding and showing of controls be done with MVVM? Assuming that I have access to this views viewmodel in the addSongCommand.Execute() method, what logic would I put there?
public class AddSongCommand : CommandBase
{
private ViewModelBase _vm;
public AddSongCommand(ViewModelBase vm)
{
_vm = vm;
}
public override bool CanExecute(object parameter) => true;
public override void Execute(object parameter)
{
// what goes here?
}
}
CodePudding user response:
<ListBox ItemsSource="{Binding SongList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=.}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Orientation="Horizontal" Visibility="{Binding IsAdding, Converter={booltovisflipconverter}}">
<Button Content="Add" Command="{Binding AddSongCommand}" />
<Button Content="Remove"/>
</StackPanel>
<StackPanel Orientation="Vertical" Visibility="{Binding IsAdding, Converter={booltovisconverter}}">
<TextBox Text="{Binding Song}"/>
<Button Content="Submit" Command="{Binding SubmitSongCommand}" />
</StackPanel>
Note: You will need to write the converter to flip the visibility, or use a datatrigger.
public class ViewModel : ViewModelBase
{
public ObservableCollection<string> SongList {get;set;} = new ObservableCollection<string>();
public bool IsAdding
{
get { ... }
set { notifychanged }
}
public string Song
{
get { ... }
set { notifychanged }
}
// called from add song command
public void EnableAdding()
{
IsAdding = true;
}
// called from submit command
public void SubmitSong()
{
SongList.Add(Song);
IsAdding = false;
}
}
public class SubmitSongCommand : CommandBase
{
private ViewModel _vm;
public SubmitSongCommand(ViewModel vm)
{
_vm = vm;
}
public override bool CanExecute(object parameter) => true;
public override void Execute(object parameter)
{
_vm.SubmitSong();
}
}
public class AddSongCommand : CommandBase
{
private ViewModel _vm;
public AddSongCommand(ViewModel vm)
{
_vm = vm;
}
public override bool CanExecute(object parameter) => true;
public override void Execute(object parameter)
{
_vm.EnableAdding();
}
}
The above uses specific types for each command. So many commands = many types. You could implement a basic command using delegates instead.
public class SimpleCommand : ICommand
{
public Action<object> ExecuteAction {get;set;}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
ExecuteAction?.Invoke(parameter);
}
}
public class ViewModel
{
public ViewModel()
{
AddSongCommand = new SimpleCommand()
{
ExecuteAction = (x) => { AddSong(); }
};
SubmitSongCommand = new SimpleCommand()
{
ExecuteAction = (x) => { SubmitSong(); }
};
}
public ICommand AddSongCommand { get; }
public ICommand SubmitSongCommand { get; }
public void AddSong()
{
// add song to list
}
public void SubmitSong()
{
// submit song
}
}