Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Shadow Panel Inheriting from a Panel in C#

0.00/5 (No votes)
21 Nov 2015 1  
This article shows how to create a very simple control in C# that overrides the ArrangeOverride, GetVisualChild, and VisualChildrenCount to create a panel that shadows and disables contained controls.

Introduction

I created a WPF UI design that that put a dark shadow over a UI section when it was disabled, not just disabled the controls. This required a boarder who’s XAML that had to be after the definition of the controls. It was considered desirable to create a Control that provided the container for the controls to be disabled, and also had the Border that did the shadow. I decided that the best solution was to create the control in C#, inheriting form the base control, in this case two controls: a Border and a Grid. To do this I had instantiate a Border that would create the shadow and override the ArrangeOverride, GetVisualChild, and VisualChildrenCount. I had done something like this before to create a numeric up-down box, which was a lot more complex than this. Its simplicity makes it a much better example to demonstrate how to implement these methods. The only issue as far as this being a good example on how to implement these methods is that the size of the base Control is not adjusted, and the shadow Border is the same size as the base Control. Still it is a good starting example..

Implementation

The implementation of the overridden methods is as follows:

protected override Size ArrangeOverride(Size arrangeSize)
{
    Rect rect = new Rect(new Point(0, 0), arrangeSize);
    base.ArrangeOverride(arrangeSize);
    _shadowBorder.Arrange(rect);
    return arrangeSize;
}

protected override Visual GetVisualChild(int index)
{
   return index < base.VisualChildrenCount ? base.GetVisualChild(index) : _shadowBorder;
}

protected override int VisualChildrenCount => base.VisualChildrenCount + 1;

The Border defined in the Control’s constructor and the shadowing is controlled by setting this Border’s Visibility between Hidden and Visible. A DependencyProperty is with the name property “IsNotShadowed” is used to set this visibility along with setting the IsEnabled property of the control. The property is named “IsNotShadowed” so that it mirrors the IsEnabled true and false values. This is a personal preference, and also means that if a CheckBox is used to enable/shadow the Control can be used directly with standard on/off values. The DependencyProperty is defined as follows:

public bool? IsNotShadowed
{
   get { return (bool?)GetValue(IsNotShadowedProperty); }
   set { SetValue(IsNotShadowedProperty, value); }
}

public static readonly DependencyProperty IsNotShadowedProperty =
    DependencyProperty.Register("IsNotShadowed", typeof(bool?), typeof(ShadowGrid),
        new FrameworkPropertyMetadata(false,
            FrameworkPropertyMetadataOptions.AffectsRender, OnIsNotShadowedChanged));

private static void OnIsNotShadowedChanged(DependencyObject d,
           DependencyPropertyChangedEventArgs e)
{
    var control = (ShadowGrid)d;
    var enabled = (bool?)e.NewValue ?? true;
    control.IsEnabled = enabled;
    control._shadowBorder.Visibility = enabled ? Visibility.Hidden : Visibility.Visible;
}

Note that this dependency property is of type Nullable<bool>. That is because it is designed to interface to the CheckBox IsChecked property, which is also Nullable<bool>. However, the effect of the null value is the same as the true value. I had to make a number of changes to deal with Binding IsNotShadowed to an indeterminate value—the shadowing was not matching the bound IsChecked value of a CheckBox. Several clicks of the CheckBox were required have the values of both the IsEnabled and shadowed values correspond to the CheckBox when the Checkbox was not bound to a bool value. Also had to set the initial value of the IsNotShadowed to false in the constructor while the initial default value of the DependencyProperty is false.

What is interesting is that if the controls within the Panel were not disabled when the Panel is shadowed, they can be selected. This is very unlike the use of a Border to do the shadow. The nice thing is that at design time a specific Control can be selected, making navigation much easier.

Using the code

It is actually very simple to use this control once the namespace is defined:

<shadowedControlsExample:ShadowGrid
       IsNotShadowed="{Binding ElementName=GridCheckBox, Path=IsChecked}">
    <ToggleButton Width="80" Margin="3" Content="Button A"  />
</shadowedControlsExample:ShadowGrid>

History

2015/11/21: Initial version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here