Reputation: 385
I want to display time information regarding to a specified time scale (for instance hours, minutes, seconds). This info is displayed within items of a listbox. To do this, I created a custom ControlTemplate for ListBoxItem-s like:
<ControlTemplate TargetType="ListBoxItem">
<Controls:ExtendedTextBlock x:Name="time" d:LayoutOverrides="Width">
<Controls:ExtendedTextBlock.Text>
<MultiBinding Converter="{StaticResource scaledDateToStringConverter}" FallbackValue="">
<Binding Path="Time"></Binding>
<Binding Path="TimelineScale"></Binding>
</MultiBinding>
</ControlTemplate>
Converter does the following:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var scale = (TimelineScale)values[1];
var timestamp = (DateTime)values[0];
string convertedTimestamp;
if (timestamp == default(DateTime))
{
convertedTimestamp = "LIVE";
}
else
{
switch (scale)
{
case TimelineScale.Seconds:
convertedTimestamp = timestamp.ToString("T", CultureInfo.CurrentCulture);
break;
case TimelineScale.Minutes:
convertedTimestamp = timestamp.ToString("t", CultureInfo.CurrentCulture);
break;
case TimelineScale.Hours:
convertedTimestamp = timestamp.AddMinutes(-timestamp.Minute).AddSeconds(-timestamp.Second).ToString("t", CultureInfo.CurrentCulture);
break;
default:
convertedTimestamp = timestamp.ToString("T", CultureInfo.CurrentCulture);
break;
}
}
return convertedTimestamp;
}
The problem is that for some items, Convert method is invoked more then one time and at first invocation, instead of actual time value -DependencyProperty.UnsetValue is passed as a parameter, causing InvalidCastException during unboxing the value from an input array. How can I achieve convertation only once and (at least)with valid parameters?
There are some interesting remarks about this issue: The value of time is defenitely defined. This code is working for a long time, since .NET ver. 4.0 but the problem appeared only now (at least it started constantly reproducing by customers and our testing machines)
Upvotes: 0
Views: 1017
Reputation: 2204
Most likely the converter is evaluating as each of the bound properties is set. Therefore, whichever property (Time
or TimelineScale
) is set first, the MultiBinding evaluates and the other will have the unset value since it has not been evaluated yet.
There are a couple of ways to handle this.
Return UnsetValue
Add the following to the top of your Convert
method:
if (!(values[1] is TimelineScale) || !(values[0] is DateTime))
{
return DependencyProperty.UnsetValue;
}
Use default values
var scale = (values[1] as TimelineScale?).GetValueOrDefault();
var timestamp = (values[0] as DateTime?).GetValueOrDefault();
In either case, once the binding evaluates a second time, Convert
will have the parameters it needs and your MultiBinding will update correctly.
Edit: It is possible that you have an upstream binding that is incorrect. I have a co-worker that recently ran into an issue with UnsetValue appearing on a value with a binding where the binding path was misspelled. It is possible this is a new behavior introduced in .NET/WPF 4.0.
Upvotes: 1