patterncsharpMinor
Composable custom control that supports binding and property inheritance
Viewed 0 times
controlcomposablesupportsinheritancecustompropertythatbindingand
Problem
This is an experiment to investigate how to wire up binding and inheritance in a composable, custom control complex.
Custom Control Structure
There is a custom parent (
Composability
The
Property Inheritance
The parent has an Attached Property (
Test Page
The View includes of some
`
Name
Command
CommandParameter
Parent String
Child String
Custom Control Structure
There is a custom parent (
TestParent) with multiple custom child controls plugged into a ObservableCollection, DP on the parent. The Parent derives from Label and the children from FrameworkElement and the Label Content is updated by a Refresh method on the parent, based on property change notifications (just for experimental purposes).Composability
The
ObservableCollection DP on the parent control provides a site for attaching the child elements. This is supported by some glue logic to wire up the Logical relationships, between parent and child, that are required to support binding and implicit inheritance. The ObservableCollection needs to be initialised in the parent, instance constructor in order to provide a unique collection for each instance.Property Inheritance
The parent has an Attached Property (
AttachedString) that is inherited by the child elements. On the parent and on one of the child elements, this property is bound to a ComboBox. As per normal inheritance behaviour, when the value of the AP on the parent is changed, the unbound children will dynamically inherit the new value. The child class also has a property that does not inherit from the parent. Test Page
The View includes of some
ComboBox elements to provide changeable sources to test binding to the custom control complex at parent and child levels. The aim is to check that binding will work at both levels and that an attached property on the parent can be implicitly inherited onto its children.`
Name
Command
CommandParameter
Parent String
Child String
Solution
- You can drop equality checks in
OnPropertyChangedsincePropertyChangedcallback only triggers when dependency property actually changes.
- Your properties can use a better naming.
AttachedStringdoesn't tell me anything. I can already see that the property returnsstringand I can see that it is an attached property. What I do not know is its purpose. Use property name to tell me that.
-
Your class wont work if I set
MyItems from code. This is counter-intuitive. Either make the setter private or make it work.-
You are leaking event handlers. Which might not be an issue for your current use case, but it can quickly become one, once stuff gets serious. Either use weak events or unsubscribe manually.
-
Label is a really poor choice for a base class. There are two main approaches when it comes to controls, that host multiple children:-
Panel approach. Panels are responsible for arranging Visuals in available space.-
ItemsControl approach. ItemsControl is responsible for assigning templates to arbitrary items and then hosting those templates in specified Panel.Note, that in neither of those approaches parent is responsible for actually rendering children. You are having so much trouble updating your parent precisely because you've neglected this principle.
The problem is that I do not know how many wheels you want to reinvent. The most straightforward way to implement the behavior you want is to declare an attached property:
static class Attached
{
public static readonly DependencyProperty StringProperty = DependencyProperty.RegisterAttached(
"String", typeof (string), typeof (Attached), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
public static void SetString(DependencyObject element, string value)
{
element.SetValue(StringProperty, value);
}
public static string GetString(DependencyObject element)
{
return (string) element.GetValue(StringProperty);
}
}...and that's it. The rest can be implemented using regular controls and bindings. For example:
From here you can take it into different directions depending on how deep you want to go.
- You can define a special
Labeltemplate, that would automatically change depending on the value of your attached property.
- You can extend
Labelclass and write a custom control (for child elements), which would update label's content depending on the values of some other custom properties, including attached ones.
- You can extend
Controlclass and write a custom template for it.
- You can extend
FrameworkElementclass, and write your own rendering logic on low level usingDrawingContext.
- Etc.
I don't think you are going to need a special
TestPanel for any of those options, simple StackPanel (or any other panel), should work just fine. But it is hard to tell for sure since your code is rather hypothetical-ish. I do not know the actual use case or an actual problem you are trying to solve (if any).Code Snippets
static class Attached
{
public static readonly DependencyProperty StringProperty = DependencyProperty.RegisterAttached(
"String", typeof (string), typeof (Attached), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
public static void SetString(DependencyObject element, string value)
{
element.SetValue(StringProperty, value);
}
public static string GetString(DependencyObject element)
{
return (string) element.GetValue(StringProperty);
}
}<StackPanel Orientation="Vertical"
Attached.String="{Binding SelectedValue, ElementName=ParentString}">
<StackPanel.Resources>
<Style TargetType="Label">
<Setter Property="Content"
Value="{Binding (Attached.String), RelativeSource={RelativeSource Self}}"/>
</Style>
</StackPanel.Resources>
<Label Content="{Binding SelectedValue, ElementName=UnattachedChildString}"/>
<Label Attached.String="{Binding SelectedValue, ElementName=ChildString}"/>
<Label />
</StackPanel>Context
StackExchange Code Review Q#150477, answer score: 2
Revisions (0)
No revisions yet.