Peter Wone
Peter Wone

Reputation: 18805

Markup extensions and object creation

Currently I have markup like this

<TextBlock>
  <TextBlock.Text>
    <Binding Path="Value" ElementName="window" ConverterParameter="M">
      <Binding.Converter>
        <local:DatePartValueConverter />
      </Binding.Converter>
     </Binding>
  </TextBlock.Text>
</TextBlock>

I'd like to shorten it to something like this

<TextBlock Text="{Binding Path=Value,ElementName=window,
  ConverterParameter=M,Converter={local:DatePartValueConverter}}" />

But the compiler barfs because DatePartValueConverter isn't derived from MarkupExtension. Is there no other way to get the short form markup to create an instance of DatePartValueConverter?

Incidentally I tried deriving from MarkupExtension and it does solve the problem. My implementation of ProvideValue looked like this

public override object ProvideValue(IServiceProvider serviceProvider)
{
  return new DatePartValueConverter();
}

and it works, but I remain hazy on the origin and nature of serviceProvider and what one might be expected to do with it.

Interestingly, when I used the Visual Studio 2012 binding editor on a binding that used this markup extension it promptly expanded it again, making the whole markup extension support thing a bit pointless.

I should probably mention that I need a private instance for each binding because it maintains internal state - it needs to know the whole DateTime value to set some aspect, like this

public object ConvertBack(object value, Type targetType, 
  object parameter, System.Globalization.CultureInfo culture)
{
  culture = System.Globalization.CultureInfo.CurrentCulture;
  var strValue = value as string;
  int y = _value.Year, M = _value.Month, d = _value.Day,
    H = _value.Hour, m = _value.Minute, s = _value.Second;
  if (strValue == null)
    return null;
  else
  {
    string p = parameter as string;
    switch (p)
    {
      case "yyyy":
        y = int.Parse(strValue); break;
      case "yy":
        y = (strValue.Length == 4) ?
          int.Parse(strValue) :
          int.Parse(DateTime.Now.Year.ToString().Substring(0, 2) + strValue);
        break;
      case "M":
      case "MM":
        M = int.Parse(strValue); break;
      ...
    }
  }
  return new DateTime(y, M, d, H, m, s);
}

Upvotes: 0

Views: 295

Answers (2)

Cameron MacFarland
Cameron MacFarland

Reputation: 71936

You can create an instance of DatePartValueConverter as a resource, then use it.

<Window.Resources>
  <local:DatePartValueConverter x:Key="datePartValueConverter" />
</Window.Resources>

<TextBlock Text="{Binding Path=Value, ElementName=window,
  ConverterParameter=M, Converter={StaticResource datePartValueConverter}}" />

EDIT If you did want to make the converter a MarkupExtension then you can do so. You can either return a new instance in ProvideValue or you can return the current instance with return this;

By returning the current instance you can have properties in your converter, and allows you to do things like this.

public class DatePartValueConverter : MarkupExtension, IValueConverter {
    public string ParseType { get; set; }
    // other methods
}

<TextBlock Text="{Binding Path=Value, ElementName=window,
  Converter={local:DatePartValueConverter ParseType=M}}" />

As for IServiceProvider see MarkupExtension.ProvideValue — Is the IServiceProvider actually used?

Upvotes: 3

Anton Tykhyy
Anton Tykhyy

Reputation: 20086

The only way other than deriving your converter from MarkupExtension that I know of is creating your own custom binding class (take a look at DelayBinding).

Upvotes: 1

Related Questions