I just started learning MVVM and here is what seems to be basic question but I spent whole day trying to figure it out.
I have a solution that contains 3 projects one for Model, one for ViewModel and one for View. The Model contains a class that has 2 properties Text and CheckStatus.
The ViewModel has a list called listOfItems that has three items, each item has these 2 properties from the Model.
The View has a listView inside it there is a CheckBox. What is the proper way to bind the CheckBox content to the property Text?
Here is the model
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TheModel
{
public class CheckBoxListModel : INotifyPropertyChanged
{
private string text;
public string Text
{
get { return text; }
set
{
text = value;
RaiseChanged("Text");
}
}
private bool checkStatus;
public bool CheckStatus
{
get { return checkStatus; }
set
{
checkStatus = value;
RaiseChanged("CheckStatus");
}
}
private void RaiseChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Here is the view model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using TheModel;
namespace TheViewModel
{
public class TheViewModel
{
public List<CheckBoxListModel> ListOfItems { get; set; }
public TheViewModelClass()
{
ListOfItems = new List<CheckBoxListModel>
{
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 1",
},
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 2",
},
new CheckBoxListModel
{
CheckStatus = false,
Text = "Item 3",
}
};
}
public static implicit operator List<object>(TheViewModelClass v)
{
throw new NotImplementedException();
}
}
}
and here is the View XAML
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:TheView.Managers" xmlns:TheViewModel="clr-
namespace:TheViewModel;assembly=TheViewModel"
x:Class="TheView.Styles.ListViewDatabaseStyle">
<UserControl.DataContext>
<TheViewModel:TheViewModelClass/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Button Content="Continue" Style="{StaticResource ButtonStyle}"
Margin="1104,27,40,40"/>
<ListView x:Name="listView1" SelectionMode="Multiple"
Style="{StaticResource ListViewStyle}" Margin="10,55,10,10"
ctrl:ListViewLayoutManager.Enabled="true" ItemsSource="
{Binding TheViewModelClass}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Competency Items"
ctrl:ProportionalColumn.Width="1100"/>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle >
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding
CheckedStatus}"/>
<Setter Property="HorizontalContentAlignment"
Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox
Click="CheckBox_Click"
Content="{Binding Path=TheViewModelClass.Text}"
IsChecked="{Binding
Path=TheViewModelClass.CheckedStatus}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
Here is the View behind code, I know I shouldn't have something here but where should that part go?
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System;
using System.Text;
using TheViewModel;
namespace TheView.Styles
{
public partial class ListViewDatabaseStyle : UserControl
{
public ListViewDatabaseStyle()
{
InitializeComponent();
}
public List<string> selectedNames = new List<string>();
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var ChkBox = sender as CheckBox;
var item = ChkBox.Content;
bool isChecked = ChkBox.IsChecked.HasValue ? ChkBox.IsChecked.Value
: false;
if (isChecked)
selectedNames.Add(item.ToString());
else
selectedNames.Remove(item.ToString());
}
}
}
First of all. Set dependencies of projects. ViewModel must have access Model. (View and Model projects do not have to reference to other projects.) If i were you i would make a StartUp Project to transfer the control to ViewModel. This "StartUp" project should be WPF, all of others should be "class library" but don't forget to add the required references to projects (For example the system.xaml for your view project to create usercontrols.)
Projects dependencies: - StartUp --> ViewModel; (- ViewModel --> View; or avoid this with DI) - ViewModel --> Model; (I should make another project for interfaces just this is just my perversions.)
StartUp Project: Now in your startup (WPF) project should contains in (app.xaml.cs):
The only one thing (Window) in your startup wpf project (to display your UserControls).
MainWindow.xaml content:
(and xaml.cs)
And Thats all your StartUp WPF project. In this way we gave the control to your ViewModel project.
(Okay, its just an extra, but i should make a "ViewService" to handle my UserControls)
Interface to find all of View and match the View with ViewModel.
I created a singleton to store and match my views with my viewmodels. (You can skip this part.) I defined this in Model project.
And now you have got a "service" to show your UserControls in the mainwindow from your ViewModel:
If you don't want to use this "ViewService". Just create an UserControl instance, match DataContext of View with your ViewModel and give this view to Control property. Here is your ViewModel with list (still in ViewMoldel project.)
RelayCommand article
View: (Keep here all of your usercontrols.)
In your UserControl (xaml):
And with interface here is the xaml.cs code (for UserControls):
And the last one is the Model project with your models:
Excuse me for english grammar mistakes, i hope you understood my post.
Update: Use the DI techn. to avoid the reference to view from viewmodel. DI service will inject the correct object with constructor injection.