There is a ComboBox in the application which is bound to a collection of items. There are cases that user can select an item from the ComboBox but the selected item might not be ready yet so the ComboBox selected item must get back to the previous selected item (or some other item in the collection), but in the current application ComboBox always shows the selected item from the user instead of retrieving the valid item after setting it back and calling notify property change.
The flowing is a simplified code of which shows the problem.
public partial class MainWindow : Window, INotifyPropertyChanged
{
private List<Customer> _Customers = new List<Customer>();
public List<string> CustomerNames
{
get
{
var list = new List<string>();
foreach (var c in _Customers)
{
list.Add(c.Name);
}
return list; ;
}
}
public string CustomerName
{
get
{
var customer = _Customers.Where(c => c.IsReady).FirstOrDefault();
return customer.Name;
}
set
{
NotifyPropertyChanged("CustomerName");
}
}
public MainWindow()
{
SetupCustomers();
InitializeComponent();
this.DataContext = this;
}
private void SetupCustomers()
{
_Customers.Add(new Customer("c1", true));
_Customers.Add(new Customer("c2", false));
_Customers.Add(new Customer("c3", false));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Customer
{
public Customer(string name, bool isReady)
{
this.Name = name;
this.IsReady = isReady;
}
public bool IsReady { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
<Window x:Class="TryComboboxReset.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox Width="100"
Height="25"
ItemsSource="{Binding Path=CustomerNames, Mode=OneWay}"
SelectedItem="{Binding Path=CustomerName, Mode=TwoWay}"/>
</Grid>
The problem was UI thread, I used dispatcher to fix this problem
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<Customer> _Customers =
new ObservableCollection<Customer>();
public ObservableCollection<Customer> CustomerNames
{
get
{
return _Customers;
}
}
public Customer CustomerName
{
get
{
return _Customers.Where(c => c.IsReady == true).FirstOrDefault();
}
set
{
// Delay the revert
Application.Current.Dispatcher.BeginInvoke(
new Action(() => NotifyPropertyChanged("CustomerName")), DispatcherPriority.ContextIdle, null);
}
}
public MainWindow()
{
SetupCustomers();
InitializeComponent();
this.DataContext = this;
}
private void SetupCustomers()
{
_Customers.Add(new Customer("c1", true));
_Customers.Add(new Customer("c2", false));
_Customers.Add(new Customer("c3", false));
CustomerName = _Customers.Where(c => c.IsReady == true).FirstOrDefault();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Customer
{
public Customer(string name, bool isReady)
{
this.Name = name;
this.IsReady = isReady;
}
public bool IsReady { get; set; }
public string Name { get; set; }
}
<ComboBox Width="400"
Height="25"
ItemsSource="{Binding Path=CustomerNames}"
SelectedValue="{Binding CustomerName,Mode=TwoWay}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You aren't actually setting the value of the selected customer name in your setter, and your getter is always going to return the first "ready" customer name it finds...
If I'm understanding the problem correctly, you need to be doing something more along these lines:
private string _customerName = null;
public string CustomerName
{
get
{
if(_customerName == null)
{
_customerName = _Customers.Where(c => c.IsReady).FirstOrDefault().Name;
}
return _customerName;
}
set
{
_customerName = value;
NotifyPropertyChanged("CustomerName");
}
}
Related
I am binding a view model to a TextBlock. The Inlines property is not a DependencyProperty so I made a TextBlockExtensions class and created an attached DependencyProperty called BindableInlines.
Here below is my View Model.
public class MainWindowModel: INotifyPropertyChanged
{
private buttonAddNewTextCommand _btnAddNewTextCommand;
public ObservableCollection<Inline> ProcessTrackerInlines { get; set; }
public ICommand btnAddNewTextCommand
{
get
{
return _btnAddNewTextCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MainWindowModel()
{
_btnAddNewTextCommand = new buttonAddNewTextCommand(this);
loadProcessTracker();
}
public void addErrorLine(String errorMessage)
{
addText(errorMessage, Brushes.Red, true);
}
public void addNewTextLine()
{
String message = Guid.NewGuid().ToString();
var rand = new Random();
byte[] brushes = new byte[4];
rand.NextBytes(brushes);
ProcessTrackerInlines.Add(new LineBreak());
ProcessTrackerInlines.Add(addText(message, new SolidColorBrush(Color.FromArgb(brushes[0], brushes[1], brushes[2], brushes[3])), true));
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ProcessTrackerInlines"));
}
}
private void loadProcessTracker()
{
ProcessTrackerInlines = new ObservableCollection<Inline>();
var rand = new Random();
byte[] brushes = new byte[4];
for(int i=0; i<5; i++)
{
rand.NextBytes(brushes);
ProcessTrackerInlines.Add(addText($"{Guid.NewGuid().ToString()}\r\n", new SolidColorBrush(Color.FromArgb(brushes[0], brushes[1], brushes[2], brushes[3])), true));
}
}
private Run addText(String textToAdd, Brush foreground = null, Boolean? addTime = null)
{
if (addTime ?? false)
{
textToAdd = addCurrentTime(textToAdd);
}
if (foreground != null)
{
return new Run(textToAdd) { Foreground = foreground };
}
else
{
return new Run(textToAdd);
}
}
private String addCurrentTime(String prefixText)
{
return $"[{DateTime.Now.ToString("h:mm ss tt")}] {prefixText}";
}
}
public class buttonAddNewTextCommand : ICommand
{
private MainWindowModel owner;
public buttonAddNewTextCommand(MainWindowModel _object)
{
owner = _object;
}
public Boolean CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
owner.addNewTextLine();
}
public event EventHandler CanExecuteChanged;
}
My TextBlockExtensions class:
public class TextBlockExtensions
{
public static IEnumerable<Inline> GetBindableInlines(DependencyObject obj)
{
return (IEnumerable<Inline>)obj.GetValue(BindableInlinesProperty);
}
public static void SetBindableInlines(DependencyObject obj, IEnumerable<Inline> value)
{
obj.SetValue(BindableInlinesProperty, value);
}
public static readonly DependencyProperty BindableInlinesProperty =
DependencyProperty.RegisterAttached("BindableInlines", typeof(IEnumerable<Inline>), typeof(TextBlockExtensions), new PropertyMetadata(null, OnBindableInlinesChanged));
private static void OnBindableInlinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var Target = d as TextBlock;
if (Target != null)
{
Target.Inlines.Clear();
Target.Inlines.AddRange((System.Collections.IEnumerable)e.NewValue);
}
}
}
And my XAML:
<Window x:Class="MVVMTextBlock.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MVVMTextBlock"
xmlns:viewModels="clr-namespace:MVVMTextBlock.ViewModels"
xmlns:extensions="clr-namespace:MVVMTextBlock"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<viewModels:MainWindowModel/>
</Window.DataContext>
<Grid>
<TextBlock extensions:TextBlockExtensions.BindableInlines="{Binding ProcessTrackerInlines, Mode=OneWay}" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Width="578" Margin="25,0,0,0" Height="398"/>
<Button Content="Add New Text" HorizontalAlignment="Left" VerticalAlignment="Top" Width="123" Margin="638,10,0,0" Command="{Binding btnAddNewTextCommand, Mode=OneWay}"/>
</Grid>
</Window>
When the button is clicked the MainWindowModel.addNewTextLine() method is getting hit but the TextBlockExtensions.OnBindableInlinesChanged() is not. So the TextBlock is not getting updated.
I have already changed the BindableInlinesProperty to be an ObservableCollection but it didn't work.
What I am doing wrong?
I have a datagrid populated with elements and a checkbox for each element.
I'm looking for a way to have an object in my ViewModel be whichever element currently has its checkbox checked.
Here is my XAML so far :
<Window x:Class="fun_with_DataGridCheckBoxColumns.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:fun_with_DataGridCheckBoxColumns"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Label Content="Chosen One : " />
<Label Content="{Binding ChosenOne.Name, Mode=OneWay}" />
</StackPanel>
<DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ID, Mode=OneWay}" IsReadOnly="True"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name, Mode=OneWay}" IsReadOnly="True"/>
<DataGridCheckBoxColumn Header="Is Chosen"/>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
and my CS :
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace fun_with_DataGridCheckBoxColumns
{
public partial class MainWindow : Window
{
public Person ChosenOne { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = new Viewmodel();
}
}
public class Viewmodel : INotifyPropertyChanged
{
public ObservableCollection<Person> People { get; private set; }
private Person _chosenOne = null;
public Person ChosenOne
{
get
{
if (_chosenOne == null) { return new Person { Name = "Does Not Exist" }; }
else return _chosenOne;
}
set
{
_chosenOne = value;
NotifyPropertyChanged("ChosenOne");
}
}
public Viewmodel()
{
People = new ObservableCollection<Person>
{
new Person { Name = "John" },
new Person { Name = "Marie" },
new Person { Name = "Bob" },
new Person { Name = "Sarah" }
};
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Person
{
private static int person_quantity = 0;
private int _id = ++person_quantity;
public int ID { get { return _id; } }
public string Name { get; set; }
}
}
Here is the behavior I am looking for :
ChosenOne in ViewModel becomes whichever Person has its checkbox checked
When a checkbox is checked, all others are unchecked
If no checkboxes are checked, sets ChosenOne to null
Basically it is the same behavior as if I had put this in the DataGrid (XAML) :
SelectedItem="{Binding ChosenOne, Mode=TwoWay}"
But in my case ChosenOne can not be the SelectedItem of the datagrid since I need SelectedItem for something else, and I have to use checkboxes for Company reasons.
I have not found how to simulate this "SelectedItem" logic with checkboxes.
I know I could put a "bool IsChosen" property in my Person class and bind the checkbox to it, but I would really rather avoid this. It will be my solution if all else fails.
Thank you.
An alternative would be to wrap your object with something that supports the checking.
Source
using System.ComponentModel;
namespace Jarloo
{
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked;
private T item;
public CheckedListItem()
{}
public CheckedListItem(T item, bool isChecked=false)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
}
Add an IsChecked property to the Person class and implement the INotifyPropertyChanged interface:
public class Person : INotifyPropertyChanged
{
private static int person_quantity = 0;
private int _id = ++person_quantity;
public int ID { get { return _id; } }
public string Name { get; set; }
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; NotifyPropertyChanged("IsChecked"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Bind the DataGridCheckBoxColumn to this property:
<DataGridCheckBoxColumn Header="Is Chosen" Binding="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}"/>
You can then handle the logic in your view model class. This should make sure that only a single Person is selected at a time:
public class Viewmodel : INotifyPropertyChanged
{
public ObservableCollection<Person> People { get; private set; }
private Person _chosenOne = null;
public Person ChosenOne
{
get
{
if (_chosenOne == null) { return new Person { Name = "Does Not Exist" }; }
else return _chosenOne;
}
set
{
_chosenOne = value;
NotifyPropertyChanged("ChosenOne");
}
}
public Viewmodel()
{
People = new ObservableCollection<Person>
{
new Person { Name = "John" },
new Person { Name = "Marie" },
new Person { Name = "Bob" },
new Person { Name = "Sarah" }
};
foreach(Person p in People)
p.PropertyChanged += P_PropertyChanged;
}
private bool handle = true;
private void P_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(handle && e.PropertyName == "IsChecked")
{
handle = false;
//uncheck all other persons
foreach (Person p in People)
if(p != sender)
p.IsChecked = false;
ChosenOne = sender as Person;
handle = true;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
If you plan to add new Person objects to the People collection dynamically at runtime you should also make sure that you handle the PropertyChanged event for these as well:
public Viewmodel()
{
People = new ObservableCollection<Person>
{
new Person { Name = "John" },
new Person { Name = "Marie" },
new Person { Name = "Bob" },
new Person { Name = "Sarah" }
};
foreach (Person p in People)
p.PropertyChanged += P_PropertyChanged;
People.CollectionChanged += (s, e) =>
{
if (e.NewItems != null)
{
foreach (object person in e.NewItems)
{
(person as INotifyPropertyChanged).PropertyChanged
+= new PropertyChangedEventHandler(P_PropertyChanged);
}
}
if (e.OldItems != null)
{
foreach (object person in e.OldItems)
{
(person as INotifyPropertyChanged).PropertyChanged
-= new PropertyChangedEventHandler(P_PropertyChanged);
}
}
};
People.Add(new Person() { Name = "New..." });
}
Here is my Person Class:
public class Person
{
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
}//close class
Here is my XAML:
<StackPanel>
<TextBox x:Name="txtLastName"
Height="50" Width="300"
DataContext="{Binding ElementName=_this, Path=PersonObject}"
Text="{Binding Path=LastName}" />
<Button Height="50" Width="100" x:Name="btnChangeValue" Content="Change Value" Click="btnChangeValue_Click"/>
</StackPanel>
Here is my XAML.CS
public partial class ClassDependency : Window
{
public Person objPerson = new Person();
public ClassDependency()
{
objPerson.LastName = "testing...";
InitializeComponent();
}
public Person PersonObject
{
get { return objPerson; }
set { objPerson = value; }
}
private void btnChangeValue_Click(object sender, RoutedEventArgs e)
{
objPerson.LastName = "New value after click....";
}
}//close class
My question is: After clicking "btnChangeValue" it does changing Last Name in my code behind but it is not reflection my textbox "txtLastName". How can I fix this??? Should I implement Dependency Property in my xaml.cs file?? I tried that too but no use.
public static readonly DependencyProperty PersonObjectProperty = DependencyProperty.Register("PersonObject", typeof(object), typeof(ClassDependency));
public Person PersonObject
{
get { return (Person)GetValue(PersonObjectProperty); }
set { SetValue(PersonObjectProperty, value); }
}
What should I do?? Please advice..
Try this:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
}
This way, the framework gets notified when the property changes. See INotifyPropertyChanged Interface.
The problem with your code is that you are not raising the PropertyChanged event, so the UI is not aware of the value change, on the setter of your dependency properties raise the PropertyChanged event as shown below:
public class Person : INotifyPropertyChanged
{
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
}
Need a simple Example of cascading combo boxes using MVVM
Wpf / Silverlight
If I understand your question you want to have the next combobox to fill with data based on the previous value.
I have a generic ViewModel that you can have to capture the list of items and the selected item
class ItemListViewModel<T> : INotifyPropertyChanged where T : class
{
private T _item;
private ObservableCollection<T> _items;
public ItemListViewModel()
{
_items = new ObservableCollection<T>();
_item = null;
}
public void SetItems(IEnumerable<T> items)
{
Items = new ObservableCollection<T>(items);
SelectedItem = null;
}
public ObservableCollection<T> Items
{
get { return _items; }
private set
{
_items = value;
RaisePropertyChanged("Items");
}
}
public T SelectedItem
{
get { return _item; }
set
{
_item = value;
RaisePropertyChanged("SelectedItem");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then have the main viewmodel that will be bound to the DataContext of the view. Have the Load methods do what you want
class MyViewModel : INotifyPropertyChanged
{
public MyViewModel()
{
First = new ItemListViewModel<string>();
Second = new ItemListViewModel<string>();
Third = new ItemListViewModel<string>();
First.PropertyChanged += (s, e) => Update(e.PropertyName, First, Second, LoadSecond);
Second.PropertyChanged += (s, e) => Update(e.PropertyName, Second, Third, LoadThird);
LoadFirst();
}
public ItemListViewModel<string> First { get; set; }
public ItemListViewModel<string> Second { get; set; }
public ItemListViewModel<string> Third { get; set; }
private void LoadFirst()
{
First.SetItems(new List<string> { "One", "Two", "Three" });
}
private void LoadSecond()
{
Second.SetItems(new List<string> { "First", "Second", "Third" });
}
private void LoadThird()
{
Third.SetItems(new List<string> { "Firsty", "Secondly", "Thirdly" });
}
private void Update<T0, T1>(string propertyName, ItemListViewModel<T0> parent, ItemListViewModel<T1> child, Action loadAction)
where T0 : class
where T1 : class
{
if (propertyName == "SelectedItem")
{
if (parent.SelectedItem == null)
{
child.SetItems(Enumerable.Empty<T1>());
}
else
{
loadAction();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
And in your view have this code somewhere.
<ComboBox ItemsSource="{Binding First.Items}" SelectedItem="{Binding First.SelectedItem}" />
<ComboBox ItemsSource="{Binding Second.Items}" SelectedItem="{Binding Second.SelectedItem}" />
<ComboBox ItemsSource="{Binding Third.Items}" SelectedItem="{Binding Third.SelectedItem}" />
You can refactor to make it nicer, use MVVM frameworks or derive the ItemListViewModel specifically for the list of items and have the load in there for better encapsulation. Its up to you.
If any parent combobox value gets changed then all child lists will get cleared.
HTH
I have an wpf mvvm application. I try to write checkbox list control.
I can bind the checkbox list elements.
Added to this issue, I want to get sum of the selected checkbox list elements values.
I added DependencyProperty and bind it to view model property.
But, they dont fire each other.
CheckBoxList User Control Xaml
<ListBox x:Name="ItemsControl" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" IsChecked="{Binding IsSelected, Mode=TwoWay}"
Checked="CheckBox_Checked" Unchecked="CheckBox_Checked" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
CheckBoxList Code Behind
public partial class CheckBoxList : UserControl
{
public CheckBoxList()
{
InitializeComponent();
}
public static readonly DependencyProperty SelectedCheckBoxItemsValueProperty =
DependencyProperty.Register("SelectedCheckBoxItemsValue", typeof(int), typeof(CheckBoxList),
new FrameworkPropertyMetadata(
0,
new FrameworkPropertyMetadata(0, OnSelectedItemsChanged));
public int SelectedCheckBoxItemsValue
{
get { return (int)GetValue(SelectedCheckBoxItemsValueProperty); }
set { SetValue(SelectedCheckBoxItemsValueProperty, value); }
}
private static int GetSelectedCheckBoxItemsValue(DependencyObject obj)
{
return (int)obj.GetValue(SelectedCheckBoxItemsValueProperty);
}
private static void OnSelectedItemsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
CheckBoxList checkboxList = obj as CheckBoxList;
ObservableCollection<ISelectableItem> items = checkboxList.DataContext as ObservableCollection<ISelectableItem>;
foreach (var item in items)
{
item.IsSelected = (GetSelectedCheckBoxItemsValue(obj) & item.Value) != 0;
}
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBoxList checkboxList = sender as CheckBoxList;
ObservableCollection<ISelectableItem> coll = ItemsControl.DataContext as ObservableCollection<ISelectableItem>;
if (coll == null) return;
int count = 0;
foreach (var item in coll)
{
if (item.IsSelected)
{
count += item.Value;
}
}
SelectedCheckBoxItemsValue = count;
}
}
SelectableItem Class
public interface ISelectableItem : INotifyPropertyChanged
{
bool IsSelected { get; set; }
string Text { get; set; }
int Value { get; set; }
string GroupName { get; set; }
}
public class SelectableItem : ISelectableItem
{ ....
ViewModel Property
public int SelectedCheckBoxEnumItemsValue
{
get
{
return _selectedCheckBoxEnumItemsValue;
}
set
{
_selectedCheckBoxEnumItemsValue = value;
NotifyOfPropertyChange("SelectedCheckBoxEnumItemsValue");
}
}
At Binder Class
string selectedItemPropertyName = "Selected" + viewModelProperty.Name + "Value";
var property = viewModelProperties.FirstOrDefault(p => p.Name.Contains(selectedItemPropertyName));
if (property != null)
{
var selectedItemOrValueBinding = new Binding(property.Name)
{
Mode = property.CanWrite ? BindingMode.TwoWay : BindingMode.OneWay,
ValidatesOnDataErrors = Attribute.GetCustomAttributes(property, typeof(ValidationAttribute), true).Any()
};
BindingOperations.SetBinding(control, CheckBoxList.SelectedCheckBoxItemsValueProperty, selectedItemOrValueBinding);
}
Below code solves your problem..
Please Note the segrgation of view models.
<StackPanel>
<TextBlock Text="{Binding Count}"></TextBlock>
<ListBox x:Name="ItemsControl" ItemsSource="{Binding CheckList}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="item" Content="{Binding Text}" IsChecked="{Binding IsSelected, Mode=TwoWay}" Command="{Binding CheckboxCheckedCommand}" CommandParameter="{Binding IsChecked, ElementName=item}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MasterViewModel();
}
}
public class MasterViewModel : INotifyPropertyChanged
{
private List<CheckBoxItem> checkList;
private int count;
public int Count
{
get
{
return count;
}
set
{
count = value;
OnPropertyChanged("Count");
}
}
public List<CheckBoxItem> CheckList
{
get
{
return checkList;
}
set
{
checkList = value;
OnPropertyChanged("CheckList");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MasterViewModel()
{
checkList = new List<CheckBoxItem>();
for (int i = 0; i < 5; i++)
{
CheckBoxItem item = new CheckBoxItem();
item.Text = i.ToString();
item.IsSelected = false;
item.CheckboxCheckedCommand = new RelayCommand(new Action<object>(ExecuteCheckCommand));
checkList.Add(item);
}
}
private void ExecuteCheckCommand(object parameter)
{
if (parameter.GetType() == typeof(bool))
{
bool value = bool.Parse(parameter.ToString());
int val = count;
if (value)
{
val++;
}
else
{
val--;
}
Count = val;
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
public class CheckBoxItem : INotifyPropertyChanged
{
private bool isSelected;
private string text;
public string Text
{
get
{
return text;
}
set
{
text = value;
OnPropertyChanged("Text");
}
}
public bool IsSelected
{
get
{
return isSelected;
}
set
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
public ICommand CheckboxCheckedCommand
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
public class RelayCommand : ICommand
{
private Action<object> executeCommand;
public RelayCommand(Action<object> executeCommand)
{
this.executeCommand = executeCommand;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
executeCommand(parameter);
}
}