views:

186

answers:

2

Having such MarkupExtension

public class Extension1 : MarkupExtension
{
    private static int _counter = 0;

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return string.Format("Item {0}", _counter++);
    }
}

and this XAML

<ListBox>
  <ListBoxItem Content="{my:Extension1}"></ListBoxItem>
  <ListBoxItem Content="{my:Extension1}"></ListBoxItem>
  <ListBoxItem Content="{my:Extension1}"></ListBoxItem>
</ListBox>

I get such list:

Item 1
Item 2
Item 3

Now I try to generate the same list using this Style

<Style TargetType="ListBoxItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBoxItem">
                <TextBox Text="{my:Extension1}"></TextBox>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

And with such XAML

<ListBox ItemsSource="{StaticResource data}"></ListBox>

I get

Item 0
Item 0
Item 0

So {my:Extension1} evaluated only once. Can I create a computed property that will be evaluated for every item?

+1  A: 

You are then making the assumption that every time a new list box item is created the control item template definition is going to be processsed afresh. For performance reasons this is not the case. Much quicker to create it the first time around and then just clone it each subsequent time. Hence your not getting the result you wanted. The result of calling the extension is being cached and reused.

To get around this you need to return something dynamic instead of static. Try returning an object from ProvideValue instead of a string. The returned object will itself contain a counter and when ToString is called on that object it returns the string version of the counter.

Phil Wright
A: 

Try returning an object from ProvideValue instead of a string

Phil was on the right track... actually, you need to return this from ProvideValue if your markup extension is called from a template. This will cause the markup extension to be evaluated for each control generated by the template. To determine if the call to ProvideValue is from a template, you need to check the target object : in a template, it will be of type System.Window.SharedDp. I wrote a blog post about that.

Thomas Levesque