Stop Messing With The UI... It's Just Data!!!
Personally, I think the easiest way to do this is using a CompositeCollection
(or a custom enumerator). The advantage of this way of thinking is it properly separates this as data, which is what it is, rather than messing with custom UI nonsense/bindings/relative sources, etc.
I'll explain.
Consider you are trying to show 'x' number of dynamically-generated colors stored in a myColors
collection, followed by something that means 'no color' (your box with the line in it.)
First, define a 'no color' token somewhere in your app, like so...
class NoColorToken{}
Then define a DataTemplate
targeting that class, like so...
<DataTemplate DataType="{x:Type ns:NoColorToken}">
<TextBlock Text="Replace with template representing 'no color'" />
</DataTemplate>
You could even make it more generic calling it a NoSelectionToken
to use with any type of list. Just make sure to scope the DataTemplate to that specific location's usage (i.e. no color in this example.)
Then in your code, just stuff your colors into a CompositeCollection
followed by an instance of the NoColorToken
class, like so:
var colorsAndToken = new CompositeCollection();
colorsAndToken.Add(new CollectionContainer(myColors));
colorsAndToken.Add(new NoColorToken());
itemsControl.ItemsSource = colorsAndToken;
Changes to MyColors (if observable) will automatically update the UI.
Things can be made even easier if they don't need to be observable (i.e. No individual adds or removals) by simply writing an enumerator function (essentially the simplified basics of what CompositeCollection
does internally.)
IEnumerable ColorsWithToken(IEnumerable colors){
foreach (var color in colors)
yield return color;
yield return new NoColorToken();
}
itemsControl.ItemsSource = ColorsWithToken(myColors);
Again though, the custom enumerator approach won't track changes to myColors
. If myColors
changes, you have to re-assign ItemsSource
. However, if you go the route of the CompositeCollection
, it handles updates automatically, just at the expense of a new object, the CompositeCollection
, but that's what it's there for.
By the way, you can also wrap the above in a converter that handles either approach for you, returning either the enumerator, or a CompositeCollection
for a pure XAML approach regardless of which ItemsControl.ItemsSource
you're applying it to. I've actually done exactly that with an AddNoSelectionPlaceholder
converter.)
Again, the reason I prefer this is it treats the items, including the 'no color' item as data, which is what it is. Even better, since it is data, it lets you easily change things around. Want the 'no color' item to be first? Just switch the order you added them.
colorsAndToken.Add(new NoColorToken());
colorsAndToken.Add(new CollectionContainer(myColors));
or
yield return new NoColorToken();
foreach (var color in colors)
yield return color;
Again, it's just data now. Nothing 'clever' needs to be done in the data template or control, bindings or anywhere else. Even better, it's now also fully unit-testable. No UI needed.
RelativeSource PreviousData
like this answer – Pagandom