-
Notifications
You must be signed in to change notification settings - Fork 2
14 Add dynamic property
In previous workshops, you used a master detail in MainWindow with employees and customers using a ComboBox for employees.
Now imagine that instead of picking the customers for one employee, you want to pick customers for many employees.
To do this, you will use a CheckedListBox like this
Now the question is: how to manage the CheckedListBox binding with MVVM.
The "normal" way to do it is to use a class with two properties: the employee and a boolean.
So it means creating a new class. Then having a collection of this class but then, when you want to add or delete an employee, you have to synchronize this collection too.
So WAQS provides a way based on PropertyDescriptor to dynamically add a new bindable property to a type or some instances.
After updating MainWindow.xaml like this, add a collection of Employee in MainWidowViewModel.cs
private HashSet<Employee> _selectedEmployees = new HashSet<Employee>();
Then update the LoadCustomerAsync method to use it
public async Task LoadCustomersAsync()
{
Customers = null;
var selectedEmployees = _selectedEmployees.ToList();
if (selectedEmployees.Any())
{
var customers = await (from c in _context.Customers.AsAsyncQueryable()
from e in _context.Employees.AsAsyncQueryable()
where selectedEmployees.Contains(e)
where e.AlreadySoldTo(c)
let totalSpent = c.TotalSpent
orderby totalSpent descending
select new CustomerInfo
{
Id = c.Id,
Name = c.FullName,
TotalSpent = totalSpent
}).ExecuteAsync();
if (selectedEmployees.Count == _selectedEmployees.Count && selectedEmployees.All(e => _selectedEmployees.Contains(e)))
{
Customers = customers;
}
}
}
Finally, add the PropertyDescriptor in the constructor:
_context.AddProperty<Employee, bool>("IsSelected",
e => _selectedEmployees.Contains(e),
(e, value) =>
{
if (value)
{
_selectedEmployees.Add(e);
}
else
{
_selectedEmployees.Remove(e);
}
Customers = null;
LoadCustomersAsync().ConfigureAwait(true);
});
Note how easy it is. You define the type it applies to, the result type, the name, the get and the set.
And then, you can bind on it:
<ListBox ItemsSource="{Binding Employees}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" />
<TextBlock Text="{Binding FullName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can download the result here.