tags:

views:

106

answers:

2

I have an existing item which is a child of a canvas. The item's style is contained in a resource dictionary that contains the various brushes that can be used to color the item.

When an instance of the item is created it is given the default coloring. I am currently not able to directly interact with the Fill property as in:

item.Fill = BrushBlue;

For this reason I began looking at the XAML for each instance of the item.

The XAML I am interested in for the item after an instance is created is:

<d:Item IsSelected="True" Width="78" Height="65" Panel.ZIndex="0" Canvas.Left="233"     Canvas.Top="352.54">
    <Path ToolTip="Process">
        <Path.Style>
             <Style TargetType="Path">
                 <Style.BasedOn>
                     <Style TargetType="Path">
                         <Style.Resources>
                            <ResourceDictionary />
                         </Style.Resources>
                         <Setter Property="Shape.Fill">
                             <Setter.Value>
                                 <DynamicResource ResourceKey="ItemBrush" />
                             </Setter.Value>
                         </Setter>
                     </Style>
                 </Style.BasedOn>
             </Style>
        </Path.Style>
    </Path>
.
.
.
</d:Item>

I would ideally like to be able to tie the Shape.Fill value to a property of the instance of the item, or at least be able to change that value based on user selection in the application.

I am editing the XAML of the item to change its fill color. To accomplish this I am using the following code, based on the button clone sample from MSDN:

string savedItem = XamlWriter.Save(this) as String;

string newItem = savedItem.Replace(GetFillBrush(this), "BrushBlue");
SetFillBrush(this, "BrushBlue");

StringReader stringReader = new StringReader(newItem);
XmlReader xmlReader = XmlReader.Create(stringReader);

thisCanvas.Children.Add((Item)XamlReader.Load(xmlReader));

I am getting the XAML, updating the brush resource name and then generating a new item.

This approach was a proof of concept. I want to be able to update the XAML of the existing item without having to create a new item on the canvas.

What is the best way to accomplish this "in-place" update of the item's fill property?

+2  A: 

I assume that the brush is being used in more than one place, which is why you can't just set the Fill property:

item.Fill = Brushes.Blue;

In that case what you should do is encapsulate the item into a UserControl or custom control, with a dependency property of type Brush. (Depending on the use of the brush, you might also call this Fill, or it might be more specific, e.g. AccentFill, OuterBorderFill, etc.) Then in your XAML, bind all those multiple occurrences of the brush to that property. Now you do have a single place where you can set the property, and through the bindings it will propagate to all the places it is needed:

item.AccentFill = Brushes.Blue;

In any event, the key thing is not to think about "updating the XAML of the existing item." XAML is a serialisation format. Your existing item is already a .NET object in memory and you can set properties on it directly rather than needing to reload from a serialised form.

itowlson
The brush is currently only used in one place, however I am currently unable to access the property directly. I added more information about the current setup of the item's style.
amarcy
The binding approach should work with your code from what I can see. Declare a Fill dependency property on the Item class, and change your setter to <Setter Property="Shape.Fill" Value="{Binding Fill, RelativeSource={RelativeSource AncestorType {x:Type d:Item}}}" />. (Note the RelativeSource to make the Path style look for the Fill property on the enclosing Item.) Then you can set item.Fill in your code, and the binding will detect the change.
itowlson
When I change the setter to the above value it throws the following error: " 'AncestorType {x:Type d:Item}' string cannot be converted to object of type 'System.Windows.Data.RelativeSourceMode'. AncestorType {x:Type d:Item} is not a valid value for RelativeSourceMode."Any advice?
amarcy
Mea culpa. There should be another = sign in there, between AncestorType and the {x:Type} markup. Thus: RelativeSource={RelativeSource AncestorType={x:Type d:Item}}. Sorry about that.
itowlson
That worked perfectly. Thank you so much.
amarcy
A: 

Unless I am misunderstanding your question, you are really doing it the hard way. Why wouldn't this work?

this.Fill = Brushes.Blue;

or if it is important to use a resource name:

this.Fill = (Brush)FindResource("BlueBrush")

Maybe you need to clarify your question to explain why you can't just set the value and be done with it.

Ray Burns
I agree that dealing with the XAML is a difficult proposition. I added clarification about the item's style which I think is what prevents me from interacting with the property directly.
amarcy
You could name it and use FindName(), but for your situation I recommend a binding. See itowlson's answer for details.
Ray Burns