FlowLayoutControl unfortunately doesn’t support items binding. You can’t just provide a source and hope FlowLayoutControl will populate the content. But fear not, there is nothing attached properties can’t solve.
I’ve created an attached property ItemsSource that does all that for you. Here is its declaration:
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.RegisterAttached("ItemsSource", typeof(IEnumerable), typeof(FlowLayoutExtensions), new UIPropertyMetadata(null, new PropertyChangedCallback(OnItemsSourceChanged)));
It accepts an IEnumerable as an input.
And here is the relevant code when ItemsSource changes:
private static void OnItemsSourceChanged(DependencyObject o, IEnumerable oldValue, IEnumerable newValue) { FlowLayoutControl layout = o as FlowLayoutControl; if (layout != null) { NotifyCollectionChangedEventHandler collectionChanged = delegate(object s, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: AddItems(layout, e.NewItems); break; case NotifyCollectionChangedAction.Remove: RemoveItems(layout, e.OldItems); break; } }; // remove event implementation if (oldValue != null) { INotifyCollectionChanged oldIncc = oldValue as INotifyCollectionChanged; if (oldIncc != null) oldIncc.CollectionChanged -= collectionChanged; } layout.Children.Clear(); if (newValue != null) { AddItems(layout, newValue); INotifyCollectionChanged incc = newValue as INotifyCollectionChanged; if (incc != null) { incc.CollectionChanged += collectionChanged; } } } }
First it defines a delegate that gets called upon collection changes (when source is INotifyCollectionChanged) then it unsubscribes from CollectionChanged if it has previously subscribed. And finally it populates FlowLayoutControl with items and optionally subscribes to CollectionChanged event (when source supports it). Note that ObservableCollection<T> implements INotifyCollectionChanged.
Here is the code that adds or removes items:
private static void AddItems(FlowLayoutControl layout, IEnumerable source) { foreach (object item in source) { GroupBox box = new GroupBox { DataContext = item }; layout.Children.Add(box); } } private static void RemoveItems(FlowLayoutControl layout, IEnumerable source) { foreach (object item in source) { GroupBox match = (from gb in layout.Children.OfType<GroupBox>() where gb.DataContext == item select gb).FirstOrDefault(); if (match != null) layout.Children.Remove(match); } }
Add items adds an GroupBox instance for each new item and sets its DataContext to the item. While RemoveItems searches for a matching GroupBox (based on DataContext match) instance and removes it from the FlowLayoutControl's Children collection.
A bit of XAML is required as well. I control the GroupBox appearance through a Style, like this:
<Style TargetType="dxlc:GroupBox"> <Setter Property="MaximizeElementVisibility" Value="Visible"/> <Setter Property="MinimizeElementVisibility" Value="Visible"/> <Setter Property="Width" Value="150"/> <Setter Property="Header" Value="{Binding Caption}" /> <Setter Property="Content" Value="{Binding}" /> </Style>
Note the binding of the Content property (remember, I am assigning current item as DataContext). And here is the FlowLayoutControl instance declaration:
<dxlc:FlowLayoutControl loc:FlowLayoutExtensions.ItemsSource="{Binding}" />
There you go, a MVVM friendly approach.
Note that this is not a fully featured code but it is a good starting point.