New to the world of WPF and the MVVM pattern. I was hoping that someone could provide a super simple description of how I could create and add a new object to an observableColllection using textboxes to set the properties and a command fired by a button.
What I have so far:
Model
public class CarModel : NotifyBase
{
#region Private Members
/// <summary>
/// Private members for the class Car
/// </summary>
private string _manufacturer;
private string _model;
private string _year;
private string _color;
private string _regNumber;
private EngineModel _eng;
#endregion
#region Public Properties
/// <summary>
/// Public properties for the class Car (used for view binding)
/// </summary>
public string Manufacturer {
get { return _manufacturer; }
set {
if (_manufacturer != value)
{
_manufacturer = value;
RaisePropertyChanged("Manufacturer");
}
}
}
public string Model {
get { return _model; }
set {
if (_model != value)
{
_model = value;
RaisePropertyChanged("Model");
}
}
}
.
.
.
ViewModel
class MainViewModel : NotifyBase
{
#region Private Members
/// <summary>
/// Privtae members for the class MainViewModel
/// </summary>
private ObservableCollection<CarModel> _carRegistry;
private CarModel _selectedCar;
private CarModel _car;
private ICommand _addCarCommand;
#endregion
#region Public Properties
/// <summary>
/// Public members for the class MainModelView (used for view binding)
/// </summary>
public ObservableCollection<CarModel> CarRegistry{
get { return _carRegistry; }
set {
_carRegistry = value;
RaisePropertyChanged("CarRegistry");
}
}
public CarModel SelectedCar
{
get { return _selectedCar; }
set
{
_selectedCar = value;
RaisePropertyChanged("SelectedCar");
}
}
public CarModel Car
{
get { return _car; }
set {
_car = value;
RaisePropertyChanged("Car");
}
}
public ICommand AddCarCommand
{
get {
if (_addCarCommand == null)
{
_addCarCommand = new RelayCommand(param =>new CarModel(), param => true);
}
return _addCarCommand;
}
}
#endregion
#region Constructor
/// <summary>
/// MainViewModel constructor
/// </summary>
public MainViewModel()
{
CarRegistry = new ObservableCollection<CarModel>()
{
//Dummy data
new CarModel(){Manufacturer = "Volvo", Model = "V70", Year = "2000", Color = "White", RegNumber = "CDC-123",
Engine = new EngineModel(){Serial = "END125456#", Volume = "2.0"}},
new CarModel(){Manufacturer = "Volvo", Model = "V40", Year = "2005", Color = "Black", RegNumber = "ITI-456",
Engine = new EngineModel(){Serial = "IND554567#", Volume = "1.6"}},
new CarModel(){Manufacturer = "Ford", Model = "Escort", Year = "1995", Color = "Blue", RegNumber = "GHD-777",
Engine = new EngineModel(){Serial = "GHTSJ5556#", Volume = "2.0"}},
};
}
#endregion
#region Methods
private void AddCar(object o)
{
//add new carModel to _carRegistry
}
#endregion
}
RelayCommand
class RelayCommand:ICommand
{
#region Private Members
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
//if(parameter != null)
_canExecute(parameter);
}
}
If I understand correctly I then need I need to bind textboxes to properties in the ViewModel and a button to fire the AddCarCommand
but I am unclear on how it should be implemented. Any help would be greatly appreciated!!!
//Nathan
Edit
XAML
Currently my xaml looks like this:
<Window x:Class="CarData_Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CarData_Test"
Title="Car Data Test" Height="606.645" Width="758.759">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="CAR INFORMATION" VerticalAlignment="Top" FontWeight="Bold" FontFamily="Segoe WP Light" RenderTransformOrigin="0.497,-1.834" FontSize="16"/>
<TextBlock HorizontalAlignment="Left" Margin="28,10,0,0" TextWrapping="Wrap" Text="CAR REGISTER" VerticalAlignment="Top" FontFamily="Segoe WP Light" FontSize="16"/>
<ListView HorizontalAlignment="Left" Height="446" Margin="28,31,0,0" VerticalAlignment="Top" Width="323" ItemsSource="{Binding CarRegistry}" SelectedItem="{Binding SelectedCar}">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto" Header="Reg Number" DisplayMemberBinding="{Binding RegNumber}"/>
<GridViewColumn Width="Auto" Header="Manufacturer" DisplayMemberBinding="{Binding Manufacturer}"/>
<GridViewColumn Width="Auto" Header="Model" DisplayMemberBinding="{Binding Model}"/>
<GridViewColumn Width="Auto" Header="Year" DisplayMemberBinding="{Binding Year}"/>
<GridViewColumn Width="Auto" Header="Color" DisplayMemberBinding="{Binding Color}"/>
</GridView>
</ListView.View>
</ListView>
<StackPanel Grid.Column="1" HorizontalAlignment="Left" Height="446" Margin="10,31,0,0" VerticalAlignment="Top" Width="335">
<TabControl DataContext="{Binding SelectedCar}" Height="446">
<TabItem Header="Overview">
<Grid Background="#FFE5E5E5">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="20 30 0 0" Content="Registration Number"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Name="txtbox_regNumber" Text="{Binding RegNumber, UpdateSourceTrigger=PropertyChanged}" Width="112" Margin="25 3 0 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Manufacturer" Margin="21 0 0 0"/>
<Label Content="Model" Margin="76 0 0 0"></Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Name="txtbox_manufacturer" Text="{Binding Manufacturer, UpdateSourceTrigger=PropertyChanged}" Width="112" Margin="25 0 0 0"/>
<TextBox Name="txtbox_model" Text="{Binding Model, UpdateSourceTrigger=PropertyChanged}" Width="112" Margin="44 0 0 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Year" Margin="22 0 0 0"/>
<Label Content="Color" Margin="123 0 0 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Name="txtbox_year" Text="{Binding Year, UpdateSourceTrigger=PropertyChanged}" Width="112" Margin="25 0 0 0"/>
<TextBox Name="txtbox_color" Text="{Binding Color, UpdateSourceTrigger=PropertyChanged}" Width="112" Margin="44 0 0 0"/>
</StackPanel>
</StackPanel>
<GroupBox Header="Edit" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="182" Width="291"/>
</Grid>
</TabItem>
<TabItem Header="Engine Data">
<Grid Background="#FFE5E5E5">
<StackPanel>
<StackPanel>
<Label Content="Serial Number" Margin="20 30 0 0"/>
</StackPanel>
<StackPanel>
<TextBox HorizontalAlignment="Left" Text="{Binding Engine.Serial, UpdateSourceTrigger=PropertyChanged}" Width="112" Margin="25 3 0 0"/>
</StackPanel>
<StackPanel>
<Label Content="Volume" Margin="20 15 0 0"/>
</StackPanel>
<StackPanel>
<TextBox HorizontalAlignment="Left" Text="{Binding Engine.Volume, UpdateSourceTrigger=PropertyChanged}" Width="112" Margin="25,0,0,0"/>
</StackPanel>
</StackPanel>
<GroupBox Header="Edit" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="182" Width="291"/>
</Grid>
</TabItem>
</TabControl>
</StackPanel>
<Button Command="{Binding AddCarCommand}" Content="Add" Grid.Column="1" HorizontalAlignment="Left" Margin="10,494,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
Ideally I'd like to be able to add via the textboxes that are in the tab but they are currently bound to selectedCar.
Your RelayCommand
should call your AddCar
method:
public ICommand AddCarCommand
{
get
{
if (_addCarCommand == null)
{
_addCarCommand = new RelayCommand(param => AddCar(param), param => true);
}
return _addCarCommand;
}
}
In this method you add the new Car
object to the source collection:
private void AddCar(object o)
{
CarRegistry.Add(new CarModel { Model = Car.Model });
}
In the view, you could bind the TextBoxes
to the properties of the Car object:
<TextBox Text="{Binding Car.Model, UpdateSourceTrigger=PropertyChanged}" />
...and the Button
to the command property:
<Button Command="{Binding AddCarCommand}" />
Don't forget to set the DataContext
of the view:
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
...and to actually create a Car
object somwhere in the view model:
private CarModel _car = new CarModel();
Edit:
The Execute
method of your command should invoke the _execute
action:
public void Execute(object parameter)
{
_execute(parameter);
}