1

Dependency properties can be inherited (see 10th point here), which is a nice feature:

<Grid TextBlock.FontSize="100">
... any TextBlock inside will inherit this value
</Grid>

To make this possible TextBlock have to use AddOwner() for an attached property TextElement.FontSize. Setting either attached property (TextBlock or TextElement) will do.

I want to achieve something like:

<Grid local:MyControl.IsEnabled="False">
... somewhere inside MyControl will get disabled
</Grid>

And I am not sure how to achieve it, because IsEnabled is not attached property (means I can't use above syntax), nor I want to disable all UIElements.

What is the right way to do it? Is it to make new attached property and in its callback change IsEnabled or something more convenient exists?

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • make non-attached DP in MyControl and bind IsEnabled of any child to that property. – ASh Oct 15 '19 at 08:15
  • @Ash, as a workaround? But I could simply traverse visual tree to disable `MyControl`. I thought inheritance is a nice alternative. Setting default value somewhere far away on a top level container is much more xaml-friendly and automatic. I am not sure how to achieve it, adding attached property is easy, but at which moment to set `IsEnabled`? – Sinatr Oct 15 '19 at 08:43

1 Answers1

1

It was easier than I thought, I have to add attached property to MyControl with FrameworkPropertyMetadataOptions.Inherits and the rest is done by WPF.

The MyControl should looks like this:

public class MyControl : Grid
{
    public static bool GetDisabled(DependencyObject obj) => (bool)obj.GetValue(DisabledProperty);
    public static void SetDisabled(DependencyObject obj, bool value) => obj.SetValue(DisabledProperty, value);

    public static readonly DependencyProperty DisabledProperty =
        DependencyProperty.RegisterAttached("Disabled", typeof(bool), typeof(MyControl),
        new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits,
        (d, e) =>
        {
            if (d is MyControl control)
                control.IsEnabled = !(bool)e.NewValue;
        }));
}

The callback is rised multiple time, so I have to check when it's rised on MyControl directly and simply set IsEnabled there. It works for all scenarios: adding child, changing value on parent, etc.

Usage is exactly like I wanted:

<Grid local:MyControl.Disabled="True">
    <local:MyControl/>
</Grid>
Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • can you explain what is the difference / advantage compared to ``? I don't see any except non-attached DP has less overhead – ASh Oct 15 '19 at 11:25
  • 1
    @Ash, it's useful if you don't have access to `MyControl` (e.g. it's inside some `UserControl` or datatemplate, etc.). Otherwise I'd set `IsEnabled` directly. Another solution would be to traverse visual tree down to find `Child`, then you don't need to change `MyControl` class, but then you have to do it everytime when: 1) child is added 2) container property changes, which was cumberstone and the reason I start looking into automatic inheritance, wpf cares about all such cases, allowing easily synchronize attached property value. – Sinatr Oct 15 '19 at 11:32
  • default style, declared in parent Resources, should be applied to nested MyControl elements as well: ``. it is more flexible than inherited DP, since it can have multiple triggers to change IsEnabled as well – ASh Oct 15 '19 at 11:35
  • @ASh, haven't thought about default styles, cool idea. I am falling into [xy problem](https://meta.stackexchange.com/q/66377/299295) again and again. – Sinatr Oct 15 '19 at 11:37
  • @ASh, as much as I like your idea, as much it doesn't want to work for me in real project. Took me some time to [figure out why](https://stackoverflow.com/q/58395780/1997232). I can't change `IsEnabled` of `MyControl` which is defined inside `ItemsControl.ItemsPanel` using styles. – Sinatr Oct 15 '19 at 13:23