Skip to main content
Home Forums Silverlight Programming Silverlight Controls and Silverlight Toolkit Custom control : databound ListBox with checkboxes
7 replies. Latest Post by meykih on January 9, 2009.
(0)
RomainP
Member
39 points
44 Posts
01-06-2009 7:11 PM |
Hi,
I'm trying to create a custom CheckListBox control, by inheriting from ListBox. The CheckListBox class is for the moment minimal, but will contain methods such as GetCheckedItems().
I have defined its style in Generics.xaml, but I am stuck with the binding. The style sets the ItemTemplate as follows:
<Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <CheckBox Content="Tmp" IsChecked="false" /> </DataTemplate> </Setter.Value> </Setter>
This works fine, and I can use this new class as:
<usercontrols:CheckListBox x:Name="myList" ItemsSource="{Binding}" />
myList.DataContext = this.userList; // a List<User>
The resulting list has the right number of items (databound), but of course every item displays "Tmp". How can I make the binding available to the calling page? I was thinking something along the lines of:
<usercontrols:CheckListBox x:Name="myList" ItemsSource="{Binding}" BindingField="Username" />
Thanks for any help or hints you can provide, and of course if you know of any CheckListBox control that's already available, let me know! The only requirement is that:
Romain
01-06-2009 8:26 PM |
Hi again,
I just want to say that I made some progress. I didn't know we could use a "default" binding:
<CheckBox Content="{Binding}" IsChecked="false" />
With this change, the checkbox now displays the object's ToString(). This isn't ideal but at least we're displaying something useful.Is there any way we can specify which field to bind in the calling page?
Otherwise, the rest of the control is ready, and can be used like this:
List<object> checkedItems = this.myList.GetCheckedItems();
I might post it somewhere since it could be quite useful and I haven't seen any CheckListBox yet... I just have to check how stable it is, especially if it doesn't get lost if we set the DataSource several times.
meykih
Participant
876 points
213 Posts
01-07-2009 3:34 AM |
Hi RomainP,
I don't know if I really got exactly what you are trying but if I'm right you have the following:
1. structure called "User" with some properties (let's say in this case "username" and "password")
2. List<User> which is set to ItemsSource of ListBox.
And now you want to bind the "username" property to the checkbox content, right? Then you only have to set your binding to this property in your DataTemplate:
<CheckBox Content="{Binding Path=username}" IsChecked="false" />
That should be working.
01-07-2009 5:11 PM |
Thanks for your answer meykih - this is exactly what I want to do. The only problem is that my CheckBoxList is a custom control, so its style is defined in generics.xaml.
Since the whole project will be using the CheckBoxList, I cannot hardcode the binding to "username". I don't even know what class it will be bound to. I want to know if there is a way to parameterize the binding, so that the caller of CheckBoxList can do something like:
The custom control would then contain something like this - which unfortunately doesn't work.
<CheckBox Content="{Binding {TemplateBinding BindingField}}" IsChecked="false" />
To achieve the same idea, I tried setting the binding in C#. However, the checkboxes are inside the ItemTemplate (one for each row), so they cannot be accessed from OnApplyTemplate(). Do you know what method I can override to get access to them? I can get to the items with GetContainerForItemOverride() but apparently the checkboxes haven't been created yet.
Of course any other solution that will give the same result (even if completely different) is more than welcome!Thanks again,
01-08-2009 2:57 AM |
Ok, got something for you.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
First make an instance of ListBox like this:
public class CheckListBox : ListBox { public static readonly DependencyProperty ListBoxDataContextProperty = DependencyProperty.Register("ListBoxDataContext", typeof(IEnumerable), typeof(CheckListBox), new PropertyMetadata(new PropertyChangedCallback(ListBoxDataContextChanged))); public IEnumerable ListBoxDataContext { get { return (IEnumerable)base.GetValue(ListBoxDataContextProperty); } set { base.SetValue(ListBoxDataContextProperty, value); } } public static void ListBoxDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { List<CheckBox> dataList = new List<CheckBox>(); CheckListBox control = d as CheckListBox; CheckBox box; Binding binding; IEnumerable list = e.NewValue as IEnumerable; if (list != null) { foreach (object item in list) { box = new CheckBox(); Type typeofitem = item.GetType(); PropertyInfo[] itemproperties = typeofitem.GetProperties(); foreach (PropertyInfo pi in itemproperties) { if (pi.Name == control.CheckBoxContentProperty) { binding = new Binding(control.CheckBoxContentProperty); binding.Source = item; binding.Mode = BindingMode.OneWay; box.SetBinding(CheckBox.ContentProperty, binding); } } dataList.Add(box); } } control.ItemsSource = dataList; } public string CheckBoxContentProperty { get; set; } }
Then use that control like the following:
DataStructure:
public class User { public string username { get; set; } public string password { get; set; } }
Page.xaml:
<this:CheckListBox x:Name="myCheckList" CheckBoxContentProperty="username"></this:CheckListBox>
Page.xaml.cs:
public Page() { InitializeComponent(); List<User> dataList = new List<User>(); User user; user = new User(); user.username = "user1"; dataList.Add(user); user = new User(); user.username = "user2"; dataList.Add(user); user = new User(); user.username = "user3"; dataList.Add(user); this.myCheckList.ListBoxDataContext = dataList; }
If you need more explanation tell me.
01-08-2009 3:07 AM |
And the best thing with that solution is that you can work with your checkboxes at runtime (after binding is set) cause they are elements of the itemssource!
01-08-2009 6:32 PM |
Binding the list to the code-created checkboxes... that's brilliant!
Thanks a lot meykih, this is exactly what I was looking for. It is also much better than before, because instead of calling VisualTreeHelper to access the CheckBoxes, we can now get to them fast and easy.
I only had to make one change though: with the binding you had, I couldn't find a way to access the actual databound object. The "content" property of the checkbox would just be set to the string representation of the field.
Binding binding = new Binding(control.CheckBoxContentProperty); binding.Source = item; binding.Mode = BindingMode.OneWay; checkbox.SetBinding(CheckBox.ContentProperty, binding);
Did I miss something? Instead I now use the following:
checkbox.DataContext = item; Binding binding = new Binding(control.CheckBoxContentProperty); binding.Mode = BindingMode.OneWay; checkbox.SetBinding(CheckBox.ContentProperty, binding);
This way I can iterate over the checkboxes and get the object they are bound to.I use it in the GetCheckedItems() method:
foreach (CheckBox checkbox in this.Items) { if (checkbox.IsChecked.Value) { // here the databound object is checkbox.DataContext } }
What do you think of the change?Thanks again for your help, I guess we finally have a generic CheckBoxList control!
01-09-2009 2:40 AM |
Hey Romain!
I don't think you missed something. If that works, it's fine. And the fact to be able to get the bounded object of each checkbox directly seems to be a really good advancement to.
Glad that I was able to help you.