Reputation: 3381
I'm slightly confused about how to set up a CheckBox
with a binding that ensures that my ViewModel is populated with all the checked fields. I have provided some of the code and a description at the bottom.
My Xaml file let's call it TreeView.xaml
:
<TreeView x:Name="availableColumnsTreeView"
ItemsSource="{Binding Path=TreeFieldData, Mode=OneWay, Converter={StaticResource SortingConverter}, ConverterParameter='DisplayName.Text'}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate x:Uid="HierarchicalDataTemplate_1" ItemsSource="{Binding Path=Children, Mode=OneWay, Converter={StaticResource SortingConverter}, ConverterParameter='DisplayName.Text'}">
<CheckBox VerticalAlignment="Center" IsChecked="{Binding IsSelected, Mode=TwoWay}">
<TextBlock x:Uid="TextBlock_1" Text="{Binding DisplayName.Text, Mode=OneWay}" />
</CheckBox>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The "code behind" TreeView.xaml.cs
public partial class MultipleColumnsSelectorView : UserControl
{
public MultipleColumnsSelectorView()
{
InitializeComponent();
}
private MultipleColumnsSelectorVM Model
{
get { return DataContext as MultipleColumnsSelectorVM; }
}
}
The ViewModel (tried to include only the relevant stuff) MultipleColumnsSelectorVM
:
public partial class MultipleColumnsSelectorVM : ViewModel, IMultipleColumnsSelectorVM
{
public ReadOnlyCollection<TreeFieldData> TreeFieldData
{
get { return GetValue(Properties.TreeFieldData); }
set { SetValue(Properties.TreeFieldData, value); }
}
public List<TreeFieldData> SelectedFields
{
get { return GetValue(Properties.SelectedFields); }
set { SetValue(Properties.SelectedFields, value); }
}
private void AddFields()
{
//Logic which loops over SelectedFields and when done calls a delegate which passes
//the result to another class. This works, implementation hidden
}
The model TreeFieldData
:
public class TreeFieldData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public IEnumerable<TreeFieldData> Children { get; private set; }
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsSelected"));
}
}
}
The Problem:
The behaviour that I want is when the user checks a checkbox, it should set the IsSelected
property of TreeField
(it does that right now) but then I want to go back to the ViewModel and make sure that this specific TreeField
is added to SelectedFields
. I don't really understand what the PropertyChangedEvent.Invoke
does and who will receive that event? How can I make sure that SelectedFields
gets populated so when AddFields()
is invoked it has all the TreeField
data instances which were checked?
Upvotes: 1
Views: 1380
Reputation: 4535
The subscriber of the PropertyChanged event is the view, so that if you change IsSelected programmatically the view knows it needs to update.
To insert the selected TreeField into your list you would add this code to your setter.
Also, you could define the following function which makes the notification much easier if you have many properties:
private void NotifyPropertyChange([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
The CallerMemberName attribute instructs the compiler to automatically insert the name of the property calling the method. The ? after PropertyChanged is a shorthand to your comparison to not null.
The setter of IsSelected can then be changed to
set
{
_isSelected = value;
if (value) { viewModel.SelectedFields.Add(this); }
else { viewModel.SelectedFields.Remove(this); }
NotifyPropertyChange();
}
Of course you would need to provide the TreeFieldData with the ViewModel instance, e.g. in the constructor.
I don't know if SelectedFields is bounded/shown in your view. If yes and you want the changes made to the list to be shown, you should change List to ObservableCollection.
Upvotes: 0
Reputation: 169160
You could iterate through the TreeFieldData
objects in the TreeFieldData
collection and hook up an event handler to their PropertyChanged
event and then add/remove the selected/unselected items from the SelectedFields
collection, e.g.:
public MultipleColumnsSelectorVM()
{
Initialize();
//do this after you have populated the TreeFieldData collection
foreach (TreeFieldData data in TreeFieldData)
{
data.PropertyChanged += OnPropertyChanged;
}
}
private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsSelected")
{
TreeFieldData data = sender as TreeFieldData;
if (data.IsSelected && !SelectedFields.Contains(data))
SelectedFields.Add(data);
else if (!data.IsSelected && SelectedFields.Contains(data))
SelectedFields.Remove(data);
}
}
Upvotes: 2