Reputation: 1074
in short: I don't want to repeat myself by implementing a dependency property HighlightBrush
each time I write a custom control.
Here are 2 examples of nasty code duplication:
public class MyButton : Button
{
public static readonly DependencyProperty HighlightBrushProperty = DependencyProperty.Register("HighlightBrush", typeof(Brush), typeof(MyButton), new PropertyMetadata(default(Brush)));
public Brush HighlightBrush
{
get { return (Brush) GetValue(HighlightBrushProperty); }
set { SetValue(HighlightBrushProperty, value); }
}
}
public class MyTextBox : TextBox
{
public static readonly DependencyProperty HighlightBrushProperty = DependencyProperty.Register("HighlightBrush", typeof(Brush), typeof(MyTextBox), new PropertyMetadata(default(Brush)));
public Brush HighlightBrush
{
get { return (Brush)GetValue(HighlightBrushProperty); }
set { SetValue(HighlightBrushProperty, value); }
}
}
For the sake of completeness, usage of HighlightBrush
inside the Generic.xaml
:
<Style TargetType="{x:Type custom:MyButton}">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type custom:MyButton}">
<Border x:Name="Border" Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="{Binding HighlightBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type custom:MyTextBox}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type custom:MyTextBox}">
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{Binding HighlightBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Question: How can I avoid code duplication on dependency properties? I cannot introduce another base class because there's no multiple inheritance. I cannot use composition because the dependency properties need the concrete type to be created with. Maybe attached dependency properties help but how interact with them inside control class and XAML?
(I guess TextBlock.Text
and TextBox.Text
is very similar to my problem.)
Upvotes: 2
Views: 429
Reputation: 27605
You can use AttachedProperties and interact with them in XAML, it just requires a bit more work and you getting used with the syntax.
For example, I wanted arbitrary control types to have a Geometria
(Geometry) property, so I created an AttachedProperty class to contain it (names in Portuguese):
public static class PropriedadeAnexada
{
public static readonly DependencyProperty GeometriaProperty
= DependencyProperty.RegisterAttached(
"Geometria",
typeof(Geometry),
typeof(PropriedadeAnexada),
new FrameworkPropertyMetadata(
default(Geometry),
FrameworkPropertyMetadataOptions.AffectsRender
| FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
)
);
public static Geometry GetGeometria (DependencyObject element)
{
return (Geometry)element.GetValue(GeometriaProperty);
}
public static void SetGeometria (DependencyObject element, Geometry value)
{
element.SetValue(GeometriaProperty, value);
}
}
Then, in XAML, I set this property like this:
<Button
controls:PropriedadeAnexada.Geometria="{StaticResource ÍconeNovoExame}"
Style="{StaticResource BotãoGeometria}"
/>
Note that here I only make this property available in my control by setting it, and access it in my style, which in turn uses a Template
like this:
<ControlTemplate
x:Key="ControleGeometriaTemplate"
TargetType="{x:Type Control}"
>
<Grid x:Name="root">
<Path
x:Name="ícone"
Data="{Binding Path=(local:PropriedadeAnexada.Geometria), RelativeSource={RelativeSource TemplatedParent}}"
/>
</Grid>
</ControlTemplate>
Upvotes: 2