'How can I give a WPF element a rectangular flat 3D border?

I would like to create a rectangular 'flat 3D' look for one of my control templates. In it's most simple version this means having a line at the bottom that is darker than that at the top, and maybe some variation between the left and right lines too.

A more complex version would allow me to provide on or more brushes so that gradients could be applied.

The default <Border> element in WPF lets you specify a different thickness per edge, but I can't find a way to specify multiple brushes.

So, how can I produce the effect I want as simply as possible?

EDIT it's been suggested that I post an example of how I want to use this. Personally I'd be happy to have a style or a user control. The user control might be used thus:

<FourSidedBorder LeftSideBrush="#00f" RightSideBrush="#0f0" ... />

Or perhaps even simpler:

<FourSidedBorder BorderBrush="#00f,#0f0,#f00,#fff"
                 BorderThickness="1,2,3,4" ... />

These are just ideas. Any sensible, concise solution is welcome.



Solution 1:[1]

I've done something like this just by placing multiple borders directly on top of one another. E.g.:

<Border 
  x:Name="TopAndLeft" 
  BorderThickness="3,3,0,0" 
  BorderBrush="{x:Static SystemColors.ControlLightBrush}">
<Border 
  x:Name="BottomAndRight" 
  BorderThickness="0,0,3,3" 
  BorderBrush="{x:Static SystemColors.ControlDarkBrush}">
    <ContentPresenter ... />
</Border>
</Border>

This provides the added advantage of all the other features that border provides-- corner radius and such.

Solution 2:[2]

Honestly probably the easiest way would be to use layering techniques. For instance create a grid like this:

  <Grid Width="50" Height="50">  
     <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
     </Grid.RowDefinitions>
     <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
     </Grid.ColumnDefinitions>

     <!-- Top Border -->
     <Border Height="3" Background="LightGray" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" />

     <!-- Right Border -->
     <Border Width="3" Background="DarkGray" Grid.Column="2" Grid.Row="0" Grid.RowSpan="3" />

     <!-- Content -->
     <Border Background="Gray" Grid.Row="1" Grid.Column="1" />

     <!-- Left Border -->
     <Border Width="3" Background="LightGray" Grid.Row="1" Grid.Column="0" Grid.RowSpan="2" />

     <!-- Bottom Border -->
     <Border Height="3" Background="DarkGray" Grid.Row="2" Grid.Column="1" />

  </Grid>

I think you get the idea. This is probably the easiest way of doing it. You could set this up as a template and use it like this:

<Template x:Key="My3DBorder" TargetType="ContentControl">
    <!-- Put the Grid definition in here from above -->
</Template>

<ContentControl Template="{StaticResource My3dBorder}">
   <!-- My Content Goes Here -->
</ContentControl>

Solution 3:[3]

Needed a classic 3D 'inset' border. Based on @GregD's answer (thank you!):

    <Border BorderThickness="0,0,1,1" BorderBrush="{x:Static SystemColors.ControlLightLightBrush}">
        <Border BorderThickness="1,1,0,0" BorderBrush="{x:Static SystemColors.ControlDarkBrush}">
            <Border BorderThickness="0,0,1,1" BorderBrush="{x:Static SystemColors.ControlLightBrush}">
                <Border BorderThickness="1,1,0,0" BorderBrush="{x:Static SystemColors.ControlDarkDarkBrush}">
                ..
                </Border>
            </Border>
        </Border>
    </Border>

To make it an 'outset', revert the order of 1st and 2nd element pairs.

Solution 4:[4]

you can reference the PresentationFramework.Classic assembly and then use

<Window xmlns:mwt="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Classic">
...

<Grid Width="50" Height="50">
    <mwt:ClassicBorderDecorator BorderThickness="3,3,3,3"/>
</Grid>

I was using this technique for looking at control templates to see how default buttons were styled

Solution 5:[5]

My two cents for cool approach that I prefer - using shadows too (as a style, so you can use It anywhere):

  <Style x:Key="border_style" TargetType="{x:Type Border}">
        <Style.Resources>
            <DropShadowEffect x:Key="dropShadowEffect" BlurRadius="8" ShadowDepth="1" Color="#FF2686AA" RenderingBias="Quality"/>
         </Style.Resources>

        <Setter Property="CornerRadius" Value="2" />
        <Setter Property="Effect" Value="{StaticResource dropShadowEffect}"/>
        <Setter Property="BorderBrush" Value="Gray"/>
        <Setter Property="BorderThickness" Value="1.2,1.2,0.3,0.3"/>
    </Style>

Usage (when you register your styles):

<Border Style="{StaticResource border_style}">

 </Border>

Position of 3D border effect Is done with BorderThickness. And you can play with colors, ShadowDepth,BlurRadius, CornerRadius etc.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Greg D
Solution 2
Solution 3 Astrogator
Solution 4 Glen
Solution 5 LuckyLuke82