Reputation: 3024
In my C# / WPF / .NET 4.5 project, I have an ObservableCollection<int>
which holds indexes. I also have a ListBox
with its ItemsPanelTemplate
set to a UniformGrid
(with 20 rows and 20 columns) and its ItemTemplate
set as a CheckBox
es.
I want the ObservableCollection<int>
to hold the indexes of the CheckBoxes
that are checked. This should be accomplished via a TwoWay
binding: When an int
is added or removed from the ObservableCollection
, CheckBoxes
in the UniformGrid
should become checked or unchecked; and when I check or uncheck a CheckBox
, its index should be added to or removed from the ObservableCollection
.
I tried to accomplish this using a converter in the following manner:
XAML:
<ListBox
x:Name="myListBox"
SelectionMode="Multiple"
ItemsSource="{Binding Cells, Converter={StaticResource cellsConverter}}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="20" Columns="20"></UniformGrid>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The class that contains the ObservableCollection<int>
:
public class Frame : INotifyPropertyChanged {
private ObservableCollection<int> _cells;
public ObservableCollection<int> Cells {
get { return _cells; }
set {
_cells = value;
OnPropertyChanged("Cells");
}
}
public Frame() {
Cells = new ObservableCollection<int>();
}
// Event handler for INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Converter:
[ValueConversion(typeof(ObservableCollection<int>), typeof(ObservableCollection<bool>))]
public class CellsConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
ObservableCollection<int> ints = (ObservableCollection<int>)value;
ObservableCollection<bool> bools = new ObservableCollection<bool> ();
for (int i = 0; i < 400; i++) {
bool b = new bool();
if (ints.Contains(i)) b = true;
else b = false;
bools.Add(b);
}
return bools;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
ObservableCollection<bool> bools = (ObservableCollection<bool>)value;
ObservableCollection<int> ints = new ObservableCollection<int>();
foreach (bool b in bools) {
if (b == true) ints.Add(bools.IndexOf(b));
}
return ints;
}
}
This is possibly not ideal method for accomplishing this, and in that case I would appreciate if anyone proposes a different method.
When I run this, I get a XamlParseException
saying that "Two-way binding requires Path or XPath." I guess the issue is similar to the one posted here, but I could not figure out how to apply the solutions there to my case where I'm using a converter.
Questions:
ListBox
?EDIT
For clarity, an illustration of what I want to achieve:
ParentOfMyListBox.DataContext = new Frame();
The appearance of myListBox:
indexes.Add(0);
indexes.Add(3);
indexes.Remove(0);
Then if I manually check one of the cells...
Now indexes
contains 3
and 8
.
Upvotes: 0
Views: 2112
Reputation: 26338
Remember, KISS - Keep it simple, stupid.
1) Create a wrapper class, that holds:
In XAML, bind against IsChecked property:
<ListBox
SelectionMode="Multiple"
ItemsSource="{Binding Cells}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="20" Columns="20"></UniformGrid>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now, if you need to get ObservableCollection; you can use simple LINQ query:
new ObservableCollection<int>(Cells.Where(x= x.IsChecked).Select(x => x.Value));
The converter hack that shouldn't be used, but can give you ideas to implement it without one:
private void UpdateUnderlyingCollection(
ObservableCollection<int> underlyingCollection ,
int element,
bool isChecked)
{
if (!isChecked)
underlyingCollection.Remove(element);
else if(!underlyingCollection.Contains(element))
underlyingCollection.Add(element);
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// todo; weak references
var underlyingCollection = (ObservableCollection<int>)value;
var lookup = underlyingCollection.ToLookup(x => x);
var presentableCollection = new ObservableCollection<Wrap>();
foreach (var i in Enumerable.Range(0, 400))
{
var damnLambdaYuNoCaptureByValue = i;
var wrap = new Wrap { IsChecked = lookup.Contains(i)};
wrap.PropertyChanged += (sender, args) =>
{
if(args.PropertyName == "IsChecked")
UpdateUnderlyingCollection(
underlyingCollection,
damnLambdaYuNoCaptureByValue,
wrap.IsChecked);
};
presentableCollection.Add(wrap);
}
return presentableCollection;
}
Upvotes: 3