Click here to Skip to main content
15,881,882 members
Articles / Desktop Programming / WPF

Shadow Panel Inheriting from a Panel in C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (9 votes)
21 Nov 2015CPOL3 min read 13.9K   259   8   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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
QuestionThanks for the work Pin
CapGroupAccess29-Nov-18 5:49
CapGroupAccess29-Nov-18 5:49 
My vote of 5

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.