2

I have a Dependency Property for items on a legend. It looks like:

    public List<LegendItem> LegendItems
    {
        get { return (List<LegendItem>)GetValue(LegendItemsProperty); }
        set { SetValue(LegendItemsProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LegendItemsProperty =
        DependencyProperty.Register("LegendItems", typeof(List<LegendItem>), typeof(TipScoreboard), new PropertyMetadata());

where LegendItem is a UserControl.

I am able to set it in XAML like this with no problem.

<local:TipScoreboard.LegendItems>
    <local:LegendItem ItemFillBrush="{DynamicResource GreenStatusBrush}"
                      Label="{x:Static res:TipLabels.GoodScan}"/>
    <local:LegendItem ItemFillBrush="{DynamicResource RedStatusBrush}"
                      Label="{x:Static res:TipLabels.BadScan}"/>
</local:TipScoreboard.LegendItems>

However when I go to set this property in a setter like this:

<Setter Property="LegendItems">
     <Setter.Value>
         <local:LegendItem ItemFillBrush="{DynamicResource GreenStatusBrush}"
                           Label="{x:Static res:TipLabels.GoodScan}"/>
         <local:LegendItem ItemFillBrush="{DynamicResource YellowStatusBrush}"
                           Label="{x:Static res:TipLabels.LowConfidence}"/>
      </Setter.Value>
 </Setter>

I get the The property "Value" is set more than once. for the setter. I'm not sure how to wrap this or what to do. I've tried just wrapping a List around it but that didn't seem to work.

EDIT: I tried created a custom wrapped collection and everything complies and run but the content is blank.

public class LegendItemCollection : Collection<LegendItem>
{
}

public LegendItemCollection LegendItems
{
    get { return (LegendItemCollection)GetValue(LegendItemsProperty); }
    set { SetValue(LegendItemsProperty, value); }
}

 public static readonly DependencyProperty LegendItemsProperty =
        DependencyProperty.Register("LegendItems", typeof(LegendItemCollection), typeof(TipScoreboard), new PropertyMetadata());

and the XAML:

<Setter Property="LegendItems">
    <Setter.Value>
       <local:LegendItemCollection>
           <local:LegendItem ItemFillBrush="{DynamicResource GreenStatusBrush}"
                             Label="{x:Static res:TipLabels.GoodScan}"/>
           <local:LegendItem ItemFillBrush="{DynamicResource YellowStatusBrush}"
                             Label="{x:Static res:TipLabels.LowConfidence}"/>
       </local:LegendItemCollection>
    </Setter.Value>
</Setter>
Brian Triplett
  • 3,462
  • 6
  • 35
  • 61

1 Answers1

6

One way to handle this is to change the dependency property's type to a class that wraps List<LegendItem>:

public class LegendItemCollection : List<LegendItem>
{
}

public LegendItemCollection LegendItems
{
    get { return (LegendItemCollection)GetValue(LegendItemsProperty); }
    set { SetValue(LegendItemsProperty, value); }
}
public static readonly DependencyProperty LegendItemsProperty =
    DependencyProperty.Register("LegendItems", typeof(LegendItemCollection), typeof(TipScoreboard), 
        new PropertyMetadata((sender, args) => {
            System.Diagnostics.Debug.WriteLine("LegendItems property set");
        }));

Then you can set the property straightforwardly in XAML:

<Setter Property="LegendItems">
     <Setter.Value>
         <local:LegendItemCollection>
             <local:LegendItem ItemFillBrush="{DynamicResource GreenStatusBrush}"
                               Label="{x:Static res:TipLabels.GoodScan}"/>
             <local:LegendItem ItemFillBrush="{DynamicResource YellowStatusBrush}"
                               Label="{x:Static res:TipLabels.LowConfidence}"/>
         </local:LegendItemCollection>
      </Setter.Value>
 </Setter>

I believe that, in theory, you can achieve the same thing without needing the wrapper collection class, declaring a List<T> in XAML by using x:TypeArguments. I can't seem to get this to work, though (related question). According to the docs the declaration would look something like this:

<Setter Property="LegendItems">
     <Setter.Value>
         <scg:List xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib"
                   x:TypeArguments="local:LegendItem">
             <local:LegendItem ItemFillBrush="{DynamicResource GreenStatusBrush}"
                               Label="{x:Static res:TipLabels.GoodScan}"/>
             <local:LegendItem ItemFillBrush="{DynamicResource YellowStatusBrush}"
                               Label="{x:Static res:TipLabels.LowConfidence}"/>
         </scg:List>
      </Setter.Value>
 </Setter>

However, the above code gives me a compiler error:

The name 'List' does not exist in the namespace 'clr-namespace:System.Collections.Generic;assembly=mscorlib'


Edit

Per the comments, the solution to the edit in the OP is to use SetCurrentValue instead of assigning a property directly (the latter wipes out any styling on the property due to dependency property value precedence):

this.SetCurrentValue(LegendItemsProperty, new LegendItemCollection());
Community
  • 1
  • 1
McGarnagle
  • 101,349
  • 31
  • 229
  • 260
  • I tried the custom wrapper and it ran but didn't seem to work. Just shows up blank. I got the same error as you when trying the List approach. – Brian Triplett May 12 '14 at 21:05
  • @BrianTriplett are you sure you updated the DP type to "LegendItemCollection" in all 3 spots? That's the only thing I can think of right now. You could also check for binding errors in the output window... – McGarnagle May 12 '14 at 21:20
  • 1
    I did check and they are all set correctly. What about the initialization? In the constructor for the view I have `this.LegendItems = new LegendItemCollection(); InitializeComponent();` – Brian Triplett May 13 '14 at 13:10
  • 1
    @BrianTriplett why would you instantiate LegendItems in the view? For your code snippet ``, I got the impression that you wanted to set LegendItems using a style (or is it a trigger?) – McGarnagle May 13 '14 at 15:04
  • I need to set it in a style because there are triggers that change the items. – Brian Triplett May 13 '14 at 15:34
  • @BrianTriplett gotcha ... so, my concern is that, if you write `this.LegendItems = new LegendItemCollection()`, this sets a local value for the property. Therefore, any style on that property will not be applied, because the local value takes precedence. – McGarnagle May 13 '14 at 15:38
  • 2
    That was it! It worked if I set it directly in XAML but as soon as I put it in a style it did not work. It was because of value precedence. See http://stackoverflow.com/questions/4231478/binding-setting-property-but-ui-not-updating-can-i-debug-within-referenced-proj for the solution with `SetCurrentValue`. If you want to add that you your answer I'll accept since you've been very helpful. – Brian Triplett May 13 '14 at 16:02
  • @BrianTriplett interesting, I didn't know about SetCurrentValue. I edited your note into my answer. – McGarnagle May 13 '14 at 16:46