Skip to content

Instantly share code, notes, and snippets.

@technicalmedia
Created December 26, 2011 11:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save technicalmedia/1520963 to your computer and use it in GitHub Desktop.
Save technicalmedia/1520963 to your computer and use it in GitHub Desktop.
Child Collection Conundrum
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
namespace SilverlightApplication12
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
IMyFactory controller = new MyFactory();
controller.CreateNewMyParent(parent =>
_synchronizationContext.Post(state =>
{
parent.ParentName = "Parent 1";
Timer t = new Timer(s =>
_synchronizationContext.Post(state2 =>
{
controller.CreateNewMyChild(child =>
this.Dispatcher.BeginInvoke((Action)(() =>
{
child.ChildName = "IMyChild Timer";
parent.MyChildCollection.Add(child);
})));
}, null),
null, 1000, 1000);
controller.CreateNewMyChild(child =>
_synchronizationContext.Post(state2 =>
{
child.ChildName = "Child 1 A";
parent.MyChildCollection.Add(child);
}, null));
controller.CreateNewMyChild(child =>
_synchronizationContext.Post(state2 =>
{
child.ChildName = "Child 1 B";
parent.MyChildCollection.Add(child);
}, null));
controller.CreateNewMyChild(child =>
_synchronizationContext.Post(state2 =>
{
child.ChildName = "Child 1 C";
parent.MyChildCollection.Add(child);
}, null));
this.DataContext = parent;
}, null));
}
}
}
using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Threading;
using System.Windows.Threading;
namespace SilverlightApplication12
{
public class MyParent : IMyParent
{
private readonly SynchronizationContext _synchronizationContext;
public MyParent()
{
_myChildBOCollection = new ObservableCollection<MyChild>();
_myChildCollection = new ChildBinder<IMyChild, MyChild>(_myChildBOCollection);
_synchronizationContext = SynchronizationContext.Current;
Timer t = new Timer(s =>
_synchronizationContext.Post(state =>
{
MyChild child = new MyChild();
child.ChildName = "MyChild Timer";
this.MyChildBOCollection.Add(child);
}, null),
null, 777, 777);
}
public string ParentName
{
get { return _parentName; }
set
{
if (_parentName != value)
{
_parentName = value;
OnPropertyChanged("ParentName");
}
}
}
private string _parentName;
public ObservableCollection<MyChild> MyChildBOCollection
{
get { return _myChildBOCollection; }
}
private ObservableCollection<MyChild> _myChildBOCollection;
public ObservableCollection<IMyChild> MyChildCollection
{
get { return _myChildCollection; }
}
private ObservableCollection<IMyChild> _myChildCollection;
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MyChild : IMyChild
{
public string ChildName
{
get { return _childName; }
set
{
if (_childName != value)
{
_childName = value;
OnPropertyChanged("ChildName");
}
}
}
private string _childName;
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MyFactory : IMyFactory
{
public MyFactory() { }
public void CreateNewMyParent(Action<IMyParent> callback)
{
callback(new MyParent());
}
public void CreateNewMyChild(Action<IMyChild> callback)
{
callback(new MyChild());
}
}
public interface IMyParent : INotifyPropertyChanged
{
string ParentName { get; set; }
ObservableCollection<IMyChild> MyChildCollection { get; }
}
public interface IMyChild : INotifyPropertyChanged
{
string ChildName { get; set; }
}
public interface IMyFactory
{
void CreateNewMyParent(Action<IMyParent> callback);
void CreateNewMyChild(Action<IMyChild> callback);
}
public class ChildBinder<IT, T> : ObservableCollection<IT>
where T : IT
{
private readonly ObservableCollection<T> _bindTo;
public ChildBinder(ObservableCollection<T> bindTo)
{
_bindTo = bindTo;
_bindTo.CollectionChanged += new NotifyCollectionChangedEventHandler(_bindTo_CollectionChanged);
this.CollectionChanged += new NotifyCollectionChangedEventHandler(ChildBinder_CollectionChanged);
}
void ChildBinder_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (T t in this.OfType<T>())
if (!_bindTo.Contains(t))
_bindTo.Add(t);
break;
case NotifyCollectionChangedAction.Remove:
foreach (T t in _bindTo.OfType<T>())
if (_bindTo.Contains(t))
_bindTo.Remove(t);
break;
}
}
void _bindTo_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (T t in _bindTo)
if (!this.Contains(t))
this.Add(t);
break;
case NotifyCollectionChangedAction.Remove:
foreach (T t in _bindTo)
if (this.Contains(t))
this.Remove(t);
break;
}
}
}
}
<UserControl x:Class="SilverlightApplication12.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding ParentName}" />
<ScrollViewer Margin="30,10,10,10" Grid.Row="1">
<ItemsControl ItemsSource="{Binding MyChildCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ChildName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</UserControl>
@technicalmedia
Copy link
Author

Here's an attempt to capture the essence of the interface design goals:

I want the app code to know only of the IMyFactory, IMyParent, and IMyChild interfaces, not the concrete underlying classes
I want the app code to create new IMyParent and IMyChild objects using IMyFactory
I want to expose the strongly-typed child collection (of IMyChild objects) through my IMyParent interface. 
I want the app code to be able to add a factory-created IMyChild to the IMyParent-exposed strongly-typed child collection (of IMyChild objects)
I want the app code to be able to bind to the IMyParent-exposed strongly-typed child collection (of IMyChild objects) and pick up collection changed events so the view updates when items are added or removed from the child collection
I want the IMyFactory, IMyParent, and IMyChild have no dependency on CSLA or other framework that creates MyParent and MyChild classes, but I usually these frameworks create ObservableCollection<MyChild> for the child collection
I want the implementation to run in Silverlight & in WPF

So I just hand the app code an IMyFactory and an IMyParent and it goes about its work...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment