alex2k8
alex2k8

Reputation: 43214

MarkupExtension as computed property in Template

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?

Upvotes: 4

Views: 1102

Answers (2)

Thomas Levesque
Thomas Levesque

Reputation: 292675

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.

Upvotes: 5

Phil Wright
Phil Wright

Reputation: 22946

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.

Upvotes: 2

Related Questions