Reputation: 620
There is a custom control that extends from Canvas
and can only accept instances of the class Shape
as its children. Consider the code below:
public class SvgGroup : Canvas
{
// ...
public Brush Fill
{
// Retuns the fill brush value of all the shape children, if they are all the same. Otherwise, the default value of Brush is returned
get
{
Brush rtn = default(Brush);
for (int i = 0; i < ShapeChildren.Count; i++)
{
Shape shape = ShapeChildren[i];
if (i == 0) // First loop
{
rtn = shape.Fill;
}
else if (rtn != shape.Fill) // Children shapes have different Fill value
{
return default(Brush);
}
}
return rtn;
}
// Sets the fill brush value of all the shape children
set
{
foreach (Shape shape in ShapeChildren)
{
shape.Fill = value;
}
}
}
// ...
}
The problem is when setting the Fill property in XAML, nothing happens. However setting the Fill in code-behind works.
I was thinking of dependency properties, but the implementation in this scenario could be quite tricky.
Upvotes: 0
Views: 745
Reputation: 128136
You could declare an Attached Property with Property Value Inheritance behavior.
When the property is set on any parent element, its value is inherited by all child elements. There is a PropertyChanged callback that checks if the element is a Shape
and eventually applies the inherited Brush to the Shape's Fill
property.
public static class ChildFillEx
{
public static readonly DependencyProperty ChildFillProperty =
DependencyProperty.RegisterAttached(
"ChildFill", typeof(Brush), typeof(ChildFillEx),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.Inherits,
ChildFillPropertyChanged));
public static Brush GetChildFill(DependencyObject obj)
{
return (Brush)obj.GetValue(ChildFillProperty);
}
public static void SetChildFill(DependencyObject obj, Brush value)
{
obj.SetValue(ChildFillProperty, value);
}
private static void ChildFillPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var shape = obj as Shape;
if (shape != null)
{
shape.Fill = (Brush)e.NewValue;
}
}
}
You would use it like this:
<Canvas local:ChildFillEx.ChildFill="Red">
<Rectangle Width="100" Height="100" />
</Canvas>
Upvotes: 0
Reputation: 3629
I think you should define two dependency properties and you should keep one of them updated:
public class SvgGroup : Canvas
{
public Brush Fill
{
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public static readonly DependencyProperty FillProperty
= DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(SvgGroup),
new FrameworkPropertyMetadata(Brushes.Red, OnFillPropertyChanged)
);
private static void OnFillPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SvgGroup svg = (SvgGroup)d;
if (e.NewValue != null && !e.NewValue.Equals(e.OldValue))
{
foreach (Shape shape in d.ShapeChildren)
{
shape.Fill = (Brush)e.NewValue;
}
d.OnShapeBrushChanged(); // Note that you should call this method in some other places too.
}
}
public Brush FillDifferentBrush
{
get { return (Brush)GetValue(IsFillDifferentProperty); }
}
public static readonly DependencyProperty FillDifferentProperty
= DependencyProperty.Register(
"FillDifferentBrush",
typeof(Brush),
typeof(SvgGroup),
new PropertyMetadata(null)
);
void OnShapeBrushChanged()
{
Brush rtn = default(Brush);
for (int i = 0; i < ShapeChildren.Count; i++)
{
Shape shape = ShapeChildren[i];
if (i == 0) // First loop
{
rtn = shape.Fill;
}
else if (rtn != shape.Fill) // Children shapes have different Fill value
{
SetValue(FillDifferentProperty, default(Brush));
}
else
SetValue(FillDifferentProperty, rtn);
}
}
}
You should call OnShapeBrushChanged()
properly (for example when you add new Shapes or when you change their Brush individually, or when you call Fill property) to keep it updated (Something like HasItems property of an ItemsControl).
Upvotes: 1