tag:blogger.com,1999:blog-36528408259207930412024-02-19T11:57:03.047+06:00Extended .NETMy experiences in c# development: WPF, MVVM, RX, etc.Pavelhttp://www.blogger.com/profile/02817525652449108788noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-3652840825920793041.post-84651088315475349392012-12-30T23:48:00.001+07:002012-12-30T23:49:51.343+07:00My notebook won’t charge on Sundays!<p>Hell, yeah, no matter how weird it sounds, it seems true.</p> <p>The laptop is a HP Envy 14 Spectre ultrabook running Windows 8 Pro. It’s a great device, well-made and pleasant to use. It’s new and runs some 4-5 hours on fully-charged battery while surfing. Originally it came with Windows 7. I’ve upgraded to W8, and this has probably caused the issue – HP doesn’t officially support W8 on this device.</p> <p>When I noticed the battery was not charging for the first time, I thought the device was broken. I had already decided to take it to the service centre, but I found it fully charged when came home the next evening. The issue has repeated several times after that, and I've noticed it occurs only on Sundays. It literally turns off at 12 PM on Sunday and turns on at 12 PM on Monday. <img class="wlEmoticon wlEmoticon-confusedsmile" style="border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none" alt="Confused smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiECsYwVVbHIbVUeWb2G383ckKFUwkxJ2H9VKzhOf4xKEd8n7D61-BU3TONEyGvcoyaiouYhFZpcKzgI2QHUNgF1Uky1Vh3CjTWt7-1rEQVbyXWt8ZlHhwErxvnlpPoV_3z6HY85hfcF_w/?imgmax=800"></p> <p>I’ve searched for a solution, but I’ve only found out that either my battery is dead (it is not for sure), or there might be an issue with Windows power management drivers. I’ve followed <a href="http://forums.cnet.com/7726-19681_102-3211649.html">the suggestions</a> <a href="http://www.pcworld.com/article/225265/battery_not_charging.html">to reinstall</a> the <em>Microsoft ACPI-Compliant Control Method Battery</em>, but it doesn’t help.</p> <p>So far I cannot find a similar issue description. I don’t know if this is unique to my machine or reproduces on other devices.</p> <p>I haven’t contacted HP support yet, but I probably will do. Or maybe I’d better contact the HP notebook battery labor union and negotiate a 7-day workweek.</p> Pavelhttp://www.blogger.com/profile/02817525652449108788noreply@blogger.com4tag:blogger.com,1999:blog-3652840825920793041.post-65189911646700298422011-10-20T12:29:00.002+07:002011-10-20T12:32:40.534+07:00Convert .NET DateTime.Ticks to T-SQL datetime<div dir="ltr" style="text-align: left;" trbidi="on">
It's not very uncommon to store DateTime values as UTC Ticks count in a bigint SQL Server column (or something equal on other DB engines). This gives total control over timezone shifts, DST, UTC/Local time problem, etc. Ticks are ticks.<br />
While it's rather easy to work with this in .NET code:<br />
<pre class="brush:c#;">var valueToStoreInDb = dateTimeVariable.ToUniversal().Ticks;
var dateTimeValue = new DateTime(ticksFromDb, DateTimeKind.Utc);
</pre>
Accessing the data from SQL (e.g. in a simple SELECT query written by a DBA) is painful, because huge numbers are absolutely not human-readable.<br />
<br />
The obvious way to solve the problem is creating a User-Defined Function on DB server. I tried to search the internet for such functions and have found only limited-precision solutions, like <a href="http://www.semdendoncker.com/blog/?p=463">this one which is accurate to the minute</a>. I don't know why the author did not implemented a full-precision solution. Perhaps, it was due to the fact that minute is the lowest unit the number passed from the 1900 to today can be stored in an <i>int</i> variable. However, it's not difficult to write a function with top possible precision for <i>datetime</i> type.<br />
<br />
<a name='more'></a><br />
<br />
The idea for the solution comes from the MSDN documentation for the <a href="http://msdn.microsoft.com/en-us/library/ms187819(v=SQL.90).aspx">SQL Server 2005 datetime type</a>. Since they store <i>datetime</i> as two integers, one for the day and the other for the time of day, we can split the .Net ticks count into corresponding parts. Here is the code:<br />
<pre class="brush:sql;">CREATE FUNCTION NetFxUtcTicksToDateTime
(
@Ticks bigint
)
RETURNS datetime
AS
BEGIN
-- First, we will convert the ticks into a datetime value with UTC time
DECLARE @BaseDate datetime;
SET @BaseDate = '01/01/1900';
DECLARE @NetFxTicksFromBaseDate bigint;
SET @NetFxTicksFromBaseDate = @Ticks - 599266080000000000;
-- The numeric constant is the number of .Net Ticks between the System.DateTime.MinValue (01/01/0001) and the SQL Server datetime base date (01/01/1900)
DECLARE @DaysFromBaseDate int;
SET @DaysFromBaseDate = @NetFxTicksFromBaseDate / 864000000000;
-- The numeric constant is the number of .Net Ticks in a single day.
DECLARE @TimeOfDayInTicks bigint;
SET @TimeOfDayInTicks = @NetFxTicksFromBaseDate - @DaysFromBaseDate * 864000000000;
DECLARE @TimeOfDayInMilliseconds int;
SET @TimeOfDayInMilliseconds = @TimeOfDayInTicks / 10000;
-- A Tick equals to 100 nanoseconds which is 0.0001 milliseconds
DECLARE @UtcDate datetime;
SET @UtcDate = DATEADD(ms, @TimeOfDayInMilliseconds, DATEADD(d, @DaysFromBaseDate, @BaseDate));
-- The @UtcDate is already useful. If you need the time in UTC, just return this value.
-- Now, some magic to get the local time
RETURN @UtcDate + GETDATE() - GETUTCDATE();
END
GO
</pre>
<br />
This function converts a 64-bit integer to a <i>datetime</i> value with millisecond precision in server local time.</div>Pavelhttp://www.blogger.com/profile/02817525652449108788noreply@blogger.com4tag:blogger.com,1999:blog-3652840825920793041.post-37974008803318905572010-12-09T19:11:00.000+06:002010-12-09T19:11:27.547+06:00Migrating TFS 2008->2010: Where are my links?!I've recently been asked to write a simple tool that checks if all links between TFS WorkItems and ChangeSets have migrated successfully from TFS 2008 to TFS 2010, and migrates missing links if necessary.<br />
<br />
The interesting thing was why did that task even arise. At that moment, it seemed that the tool that migrates projects from TFS 2008 to 2010 somehow looses all links. Yes, the tool said migration was completed, but there were no links. Later, by the time I completed the tool, the links had somehow got to the destination. Autopsy has shown that after the tool had completed, it started a Windows service, which apparently completed the migration in background.<br />
<br />
Those guys in TFS team... are they mad?Pavelhttp://www.blogger.com/profile/02817525652449108788noreply@blogger.com0tag:blogger.com,1999:blog-3652840825920793041.post-74860810879597306862010-12-02T15:06:00.001+06:002012-12-30T01:13:32.453+07:00WPF: Color your items with data-bindingI've recently run into a tricky task while implementing a Windows Eplorer-like browser feature in the project I work at. I have a collection of data items (in my View Model) that have a 'Type' property of enumeration type, and I want to display them in a ListBox. The tough requirement is that items should be colored depending on their Type property value, including custom Foreground and Background of selected items (for various combinations of IsSelected, IsSelectionActive and IsEnabled properties).<br />
<br />
<br />
<a name='more'></a><span class="Apple-style-span" style="font-size: large;">Requirements to solution</span><br />
<ol><li>Coloring has to be data-bound and work like a binding or a data trigger.</li>
<li>Leave the default ListBoxItem template as is, namely <br />
<pre class="brush: xml; highlight:[12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28]"><Style targettype="{x:Type ListBoxItem}" x:key="ListBoxItemStyle1">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<Controltemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<Contentpresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<Multitrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</pre>This would also keep UI-related triggers and ViewModel-related triggers separated from each other. </li>
<li>Keep brush combinations grouped by the same Type value.</li>
<li>Take some of the brushes from the application's ResourceDictionary.</li>
</ol><span class="Apple-style-span" style="font-size: large;">Possible solution</span><br />
The best solution is to override SystemColor.* brushes and swap their combinations according to the type of the item. In simple cases this could be done by adding brushes to some control's resources like this:<br />
<pre class="brush: xml;"><UserControl.Resources>
<SolidColorBrush Color="Red"
x:Key="{x:Static SystemColors.HighlightTextBrushKey}"/>
</UserControl.Resources>
</pre>But in my case, things are much more complicated:<br />
<ol><li>I need to put several styles to some resource dictionary, each having several SystemColor brushes overrides.</li>
<li>I have to use StaticResourceExtensions to create aliases for the brushes defined in my application's resources.</li>
<li>In order for DynamicResourceExtension from ListBoxItem's default template to work fine, brushes must be added to the resources of the ListBoxItems themselves or their styles/templates. (Since their parent is the ListBox)</li>
<li>Style selection logic has to be situated on the ListBoxItems.</li>
<li>The only built-in way to inject something into ItemsControl's containers in XAML is to use ItemContainerStyle, ItemContainerStyleSelector properties or implicit styling.</li>
</ol><div>So the brushes should be organized like this:</div><pre class="brush:xml;"><UserControl.Resources>
<Style x:Key="style1">
<Style.Resources>
<StaticResourceExtension ResourceKey="Type1HighlightTextBrush"
x:Key="{x:Static SystemColors.HighlightTextBrushKey}"/>
<StaticResourceExtension ResourceKey="Type1HighlightBrush"
x:Key="{x:Static SystemColors.HighlightBrushKey}"/>
</Style.Resources>
</Style>
<Style x:Key="style2">
<Style.Resources>
<StaticResourceExtension ResourceKey="Type2HighlightTextBrush"
x:Key="{x:Static SystemColors.HighlightTextBrushKey}"/>
<StaticResourceExtension ResourceKey="Type2HighlightBrush"
x:Key="{x:Static SystemColors.HighlightBrushKey}"/>
</Style.Resources>
</Style>
...
</UserControl.Resources>
</pre><div>And we need some way to inject style selection logic (binding with converter or triggers) into the ListBoxItems.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Here come the troubles</span></div><div>Unfortunately, WPF has several disappointing restrictions that make the task very difficult:<br />
<ol><li>Since I want to swap styles of containers, I cannot place selection logic into ItemContainerStyle or implicit ListBoxItem style - WPF doesn't allow an object's style to change that object's Style property value at the same time.</li>
<li>ItemContainerStyleSelector would work fine even with UI virtualization, but it will fail to update if the type of a data item changes after the container for the item has been generated and applied.</li>
<li>ItemContainerStyleSelector cannot set bindings on Style property - they would be removed by the ItemsControl when it would apply the style returned by the selector.</li>
<li>WPF doesn't support StaticResourceExtensions in a Style's Resources. I suppose, this is a XAML parser's bug, since it doesn't return any reasonable error message, but it unconditionally fails with strange exceptions in run-time if there is a StaticResourceExtension in a Style.Resources tag.</li>
</ol><div><span class="Apple-style-span" style="font-size: large;">The Solution</span></div><div><span class="Apple-style-span" style="font-size: x-small;">(partial)</span></div></div><div>To solve the task, I created several simple classes:</div><div><ol><li><b>DictionaryValueConverter </b>- an implementation of IValueConverter that uses an IDictionary to map raw binding values to something, namely styles in my case. Raw values play Key roles. In essence, the class is as simple as the following code: <pre class="brush:c-sharp">public class DictionaryValueConverter : IValueConverter
{
public IDictionary Dictionary { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return this.Dictionary[value];
}
}
</pre>although the final implementation involves some additional details, such as exception handling, etc.<br />
This converter allows me create a resource implementing IDictionary, which stores all required styles keyed by corresponding data item Type property value, and then write a simple binding from data item to container Style which uses the converter. In other words, I can declaratively map data item types to item container styles, which is rather elegant.</li>
<li><b><a href="http://extendeddotnet.blogspot.com/2010/12/wpf-itemcontainerstyleselector.html">DynamicStyleInjector</a> </b>- a class that tracks ItemsControl item containers generation and sets bindings to their Style property on the fly. This class solves style selection issue (troubles 1-3). In fact, this is the cornerstone of the solution, because it allows me set binding to ListBox's item containers' Style property.</li>
</ol><div>I haven't solved the StaticResourceExtension and Style integration issue yet, so for now I have to create copies of brushes. Perhaps, the issue will be solved in a couple of weeks.<br />
<br />
For now, the solution is set up in the following way:<br />
<pre class="brush:xml;"><Window.Resources>
<Style x:Key="defaultStyle">
<Setter Property="TextElement.Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Border.Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
</Style>
<!--Define all possible styles, each style's key is the type of an item-->
<Collections:Hashtable x:Key="styleMap">
<Style BasedOn="{StaticResource defaultStyle}" x:Key="a">
<Style.Resources>
<ResourceDictionary>
<SolidColorBrush Color="Brown" x:Key="{x:Static SystemColors.WindowTextBrushKey}"/>
<SolidColorBrush Color="Red" x:Key="{x:Static SystemColors.ControlTextBrushKey}"/>
<SolidColorBrush Color="Green" x:Key="{x:Static SystemColors.ControlBrushKey}"/>
<SolidColorBrush Color="Blue" x:Key="{x:Static SystemColors.HighlightTextBrushKey}"/>
<SolidColorBrush Color="Yellow" x:Key="{x:Static SystemColors.HighlightBrushKey}"/>
</ResourceDictionary>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource defaultStyle}" x:Key="b">
<Style.Resources>
<SolidColorBrush Color="Green" x:Key="{x:Static SystemColors.ControlTextBrushKey}"/>
<SolidColorBrush Color="Brown" x:Key="{x:Static SystemColors.ControlBrushKey}"/>
<SolidColorBrush Color="Lime" x:Key="{x:Static SystemColors.HighlightTextBrushKey}"/>
<SolidColorBrush Color="Maroon" x:Key="{x:Static SystemColors.HighlightBrushKey}"/>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource defaultStyle}" x:Key="c">
<Style.Resources>
<SolidColorBrush Color="Blue" x:Key="{x:Static SystemColors.ControlTextBrushKey}"/>
<SolidColorBrush Color="Red" x:Key="{x:Static SystemColors.ControlBrushKey}"/>
<SolidColorBrush Color="Orange" x:Key="{x:Static SystemColors.HighlightTextBrushKey}"/>
<SolidColorBrush Color="Black" x:Key="{x:Static SystemColors.HighlightBrushKey}"/>
</Style.Resources>
</Style>
<Style BasedOn="{StaticResource defaultStyle}" x:Key="d">
<Style.Resources>
<SolidColorBrush Color="Yellow" x:Key="{x:Static SystemColors.ControlTextBrushKey}"/>
<SolidColorBrush Color="Green" x:Key="{x:Static SystemColors.ControlBrushKey}"/>
<SolidColorBrush Color="Blue" x:Key="{x:Static SystemColors.HighlightTextBrushKey}"/>
<SolidColorBrush Color="Yellow" x:Key="{x:Static SystemColors.HighlightBrushKey}"/>
</Style.Resources>
</Style>
</Collections:Hashtable>
<src:DictionaryValueConverter x:Key="mapper" Dictionary="{StaticResource styleMap}"/>
</Window.Resources>
<DockPanel>
<!--Items are taken from DataContext's property Items.
Each of the items has Key property which denotes the item's type.
ListBox is capable of tracking items addition/removal,
and each item updates its style dynamically when Key property value changes.-->
<ListBox ItemsSource="{Binding Items}"
src:DynamicStyle.ItemContainerStyleBinding="{Binding Key, Converter={StaticResource mapper}}"
DisplayMemberPath="Title" SelectionMode="Extended">
</ListBox>
</DockPanel>
</pre>Sources of the sample demonstrating all this put together can be found here: <a href="http://www.filefactory.com/file/b481cd4/n/DynamicItemsControlItemStyle.zip">http://www.filefactory.com/file/b481cd4/n/DynamicItemsControlItemStyle.zip</a><br />
<br />
<span class="Apple-style-span" style="font-size: large;">About my original task and real life applications</span><br />
I must note, that my original task, described in the intro section, did not require such complex solution. In fact, a StyleSelector that uses an IDictionary to choose styles would have been absolutely anough, because my view model items had constant Type property value (requirement #1 didn't exist for me).<br />
<br />
But I can give a sample task that would fit to all the requirements. Imagine that you have to display a list of, say, stock tickers, styled depending on their price or daily growth/fall. You want it to be real-time, because it is a part of a stock trading software. In this case using the given two classes would allow you create a beautiful loosely-coupled solution.</div></div>Pavelhttp://www.blogger.com/profile/02817525652449108788noreply@blogger.com1tag:blogger.com,1999:blog-3652840825920793041.post-34578040916264151952010-12-01T16:38:00.001+06:002010-12-01T23:02:10.633+06:00WPF Hyperlink default styleTried to find the default style of a Hyperlink and discovered that neither Google, nor Expression Blend or ShowMeTheTemplate have it. So I had to disassemble WPF assembly with .NET Reflector+BAML viewer. Here is the style for Aero NormalColor theme, just for reference purposes:
<br />
<pre class="brush:xml;"> <Style x:Key="{x:Type Hyperlink}" TargetType="{x:Type Hyperlink}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="Red" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static GrayTextBrush}}" />
</Trigger>
<Trigger Property="IsEnabled" Value="true">
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</Style.Triggers>
<Setter Property="Foreground" Value="Blue" />
<Setter Property="TextDecorations" Value="Underline" />
</Style>
</pre>
Oh, a slight surprise for me was that the hyperlink colors are not bound to any SystemColor members. No, they are just Red and Blue nailed in the style. Why?....Pavelhttp://www.blogger.com/profile/02817525652449108788noreply@blogger.com6tag:blogger.com,1999:blog-3652840825920793041.post-1812601773081513702010-12-01T10:24:00.005+06:002010-12-02T15:09:01.098+06:00WPF ItemContainerStyleSelector unlimited: going data-bound<span class="Apple-style-span" style="font-size: large;">Use case</span><br />
You have a View Model which contains a collection of items. In View, items are presented in an <i>ItemsControl</i>, say <i>ListBox</i>, and containers for each item should have different styles. Styles are selected depending on some property of view model items, e.g. <i>Type</i> property. An example of such situation is when each <i>Type</i> has its own combination of colors.<br />
At this point, the task can be easily solved using a <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.styleselector.aspx">StyleSelector</a> which encapsulates the selection logic. It's rather simple, so I won't discuss it.<br />
To make task really interesting, let's require the selection logic to track changes in data items like <i>Bindings</i> do, i.e. if the value of the <i>Type</i> property changes, <i>Style</i> should be changed in the moment.<br />
<a name='more'></a><br />
<br />
<span class="Apple-style-span" style="font-size: large;">Designing solution</span><br />
There's actually no problem in setting up data-bound Style for a control that is created in XAML:<br />
<pre class="brush:xml"><UserControl>
<UserControl.Resources>
<StyleSelectingConverter x:Key="mapper"/>
</UserControl.Resources>
<TextBox Style="{Binding Type, Converter={StaticResource mapper}}"/>
</UserControl>
</pre>
But in case of ItemsControl's containers, we cannot set their properties directly - containers are generated dynamically.<br />
We also can't use ItemContainerStyle or implicit style with relevant TargetType, because WPF doesn't allow a Style change the value of the Style property of the object that Style is applied to: neither by setting a Binding, nor by a Trigger.<br />
ItemContainerStyleSelector won't fit because its output is applied to the container once and doesn't track the data item's property changes (although it respects virtualization). We also can't set Binding from the StyleSelector.SelectStyle method, because it will be reset when the ItemsControl applies selector output to the container.<br />
<br />
Thus, we have to write some class that will track item container generation and set bindings on Style property once a container is created by the generator. Let me introduce, the<br />
<br />
<span class="Apple-style-span" style="font-size: large;">DynamicStyleInjector</span><br />
First of all, we need some way to assign the binding to be injected into containers:<br />
<pre class="brush:c#;"> public class DynamicStyle
{
#region ItemContainerStyleBinding attached dependency property
public static BindingExpressionBase GetItemContainerStyleBinding(ItemsControl obj)
{
return (BindingExpressionBase)obj.GetValue(ItemContainerStyleBindingProperty);
}
public static void SetItemContainerStyleBinding(ItemsControl obj,
BindingExpressionBase value)
{
obj.SetValue(ItemContainerStyleBindingProperty, value);
}
// Using a DependencyProperty as the backing store for ItemContainerStyleBinding. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemContainerStyleBindingProperty =
DependencyProperty.RegisterAttached("ItemContainerStyleBinding",
typeof(BindingExpressionBase), typeof(DynamicStyle),
new UIPropertyMetadata(null, OnItemContainerStyleBindingChanged));
...
</pre>
With this attached property we can apply data-bound style to containers in any ItemsControl. The property has BindingExpressionBase type in order to allow us pass bindings created in XAML. It's because the XAML parser automatically calls ProvideValue method on any markup extension, which in case of BindingBase returns a BindingExpressionBase instance, and then passes the result to the DependencyObject.SetValue method. The latest will apply binding to the target property unleast the type of the property is BindingExpressionBase - in that case it will simply store the expression as property value.<br />
<br />
If the value changes to non-null, a DynamicStyleInjector object is created and stored in a private attached property:<br />
<pre class="brush:c#"> static void OnItemContainerStyleBindingChanged(ItemsControl control,
BindingExpressionBase newBindingExpressionBase,
BindingExpressionBase oldBindingExpressionBase)
{
Debug.Assert(control != null, "control != null");
if (oldBindingExpressionBase != null)
{
var injector = GetInjector(control);
Debug.Assert(injector != null, "injector != null");
injector.Detach();
SetInjector(control, null);
}
if (newBindingExpressionBase != null)
{
SetInjector(control, new DynamicStyleInjector(control));
}
}
</pre>
We need this property to be able to dispose off an active injector if the binding changes (oh, I know that having this binding change on-the-fly would be a really mad scenario, but it's a good habbit to make a feature able to switch off and besides it's not difficult).<br />
<br />
Once created, DynamicStyleInjector gets the current value of the ItemContainerStyleBinding attached property and assigns this binding to all existing containers in the control:<br />
<pre class="brush:c#"> void AttachBinding(BindingExpressionBase bindingExpression)
{
Debug.Assert(binding != null, "binding != null");
//filter out only those containers that do not yet have correct value (optimizing performance)
foreach (var container in this.GetAllExistingContainers()
.Where(x => bindingExpression != BindingOperations.GetBindingExpressionBase(x, FrameworkElement.StyleProperty)))
{
container.SetBinding(FrameworkElement.StyleProperty,
bindingExpression.ParentBindingBase);
}
}
private IEnumerable<FrameworkElement> GetAllExistingContainers()
{
var observedControl = this._ObservedControl;
Debug.Assert(observedControl != null, "observedControl != null");
var generator = observedControl.ItemContainerGenerator;
Debug.Assert(generator != null, "generator != null");
return (from object item in observedControl.Items
let container = generator.ContainerFromItem(item)
where container != null
select container).OfType<FrameworkElement>().ToArray();
}
</pre>
We have to refer to the ParentBindingBase property of the bindingExpression in the AttachBinding method, because a BindingExpressionBase can only be set to one object, and if we use BindingBase and SetBinding, a new BindingExpressionBase is created for each target, so everything works fine.<br />
<br />
Finally, the injector attaches to the StatusChanged event of the observed control's ItemContainerGenerator and in the event handler updates Style property binding depending on the active ItemContainerStyleBinding property value:<br />
<pre class="brush:c#"> void generator_StatusChanged(object sender, EventArgs e)
{
var observedControl = this._ObservedControl;
Debug.Assert(observedControl != null, "observedControl != null");
var generator = observedControl.ItemContainerGenerator;
Debug.Assert(generator != null, "generator != null");
Debug.Assert(sender == generator, "sender == generator");
switch (generator.Status)
{
//this status means that several items were likely to be generated
case GeneratorStatus.ContainersGenerated:
var bindingExpression = DynamicStyle.GetItemContainerStyleBinding(observedControl);
if (bindingExpression != null) this.AttachBinding(bindingExpression);
else this.DetachBinding();
break;
//still need to wait before generation process completes
case GeneratorStatus.GeneratingContainers:
case GeneratorStatus.NotStarted:
case GeneratorStatus.Error:
break;
default:
Debug.Assert(false, String.Format("Unexpected GeneratorStatus: {0}", generator.Status));
break;
}
}
</pre>
With this handler, the injector applies binding to dynamically generated containers (in fact, it won't work without this, because there are usually no existing containers on the ItemsControl when the ItemContainerStyle property value is set during XAML processing).<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Usage</span><br />
Now we can set any binding (simple or multi-binding) to an ItemsControl's containers' Style property like this:<br />
<pre class="brush:xml;highlight:[2]"><ListBox ItemsSource="{Binding Items}"
src:DynamicStyle.ItemContainerStyleBinding="{Binding Key, Converter={StaticResource mapper}}"
DisplayMemberPath="Title" SelectionMode="Extended">
</ListBox>
</pre>
The technique allows us dynamically swap containers' styles as data item's source property ('Key') changes, even if the change happens after the ListBox has been shown. Is also supports dynamic modifications of source items collection, as well as UI virtualization.
<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Full Sample</span><br />
Complete source code of described classes, together with a sample WPF application can be found at <a href="http://www.filefactory.com/file/b481cd4/n/DynamicItemsControlItemStyle.zip">http://www.filefactory.com/file/b481cd4/n/DynamicItemsControlItemStyle.zip</a><br />
<br />
A real world task that requires the described technique is described in <a href="http://extendeddotnet.blogspot.com/2010/12/wpf-color-your-items-with-data-binding.html">another post</a>.Pavelhttp://www.blogger.com/profile/02817525652449108788noreply@blogger.com0