R – Create the same border type around all the controls


I tried to create my own Border class and then insert it in my controls but then it seems I cannot assign names to everything inside the borders:

        <StackPanel Name="ifBlock" Background="#E0E8FF" />

How can I get around this? Can I use templating somehow for that?

Sorry that I was unclear. Yes I subclassed Border with my own XAML file and got this compiler error with the code above:

Error 2 Cannot set Name attribute
value 'ifBlock' on element
'StackPanel'. 'StackPanel' is under
the scope of element 'ElementBorder',
which already had a name registered
when it was defined in another scope.

The contents of my ElementBorder class aren't very interesting but I'll post it anyway:

<Border x:Class="DVPE.ElementBorder"

Best Solution

By introducing code-behind you have created an additional NameScope. You can remove this extra NameScope at runtime by clearing the NameScope after calling InitializeComponent():

public ElementBorder()
  NameScope.SetNameScope(this, null);

Although this will work, it is not your best solution. You would be better off creating a style that a UserControl.

There are two ways to do this: With a subclass and without.

With a subclass

Create your ElementBorder subclass and override the default style. Do not call InitializeComponent():

public class ElementBorder
  static ElementBorder
    DefaultStyleKeyProperty.OverrideMetadata(typeof(ElementBorder), new FrameworkPropertyMetadata(typeof(ElementBorer)));
  // any additional implementation

In your xaml file, instead of including a tag, create a ResourceDictionary containing a style with the settings you want:

<ResourceDictionary xmlns=... >

  <Style TargeType="{x:Type my:ElementBorder}">
    <Setter Property="BorderThickness" Value="4" />

Merge this ResourceDictionary into the one defined in app.xaml or themes/Generic.xaml using a <MergeDictionary> tag

Note that you can just put the style in the ResourceDictionary in your app.xaml instead of creating a separate file.

Using this is identical to using your original ElementBorder:

  <StackPanel  ...

Without a subclass

This requires less code. Just put a Style in a ResourceDictionary as before, except give it a x:Key and use Border as it's target type:


  <Style x:Key="ElementBorderStyle" TargeType="{x:Type Border}">
    <Setter Property="BorderThickness" Value="4" />

This can be used as follows:

 <Border Style="{StaticResource ElementBorderStyle}">
   <StackPanel ...

If you want all of your borders to have the new style, it is even easier. Just omit the x:Key and use Border as your TargetType:


  <Style TargeType="{x:Type Border}">
    <Setter Property="BorderThickness" Value="4" />

This will cause all borders to get the style, so you can just write:

  <StackPanel ...

Answer to additional question asked in comment below

To set the BorderBrush the same as the Background:

  <Style TargeType="{x:Type Border}">
    <Setter Property="BorderBrush" Value="{Binding Background, RelativeSource={RelativeSource Self}}" />

To set the child's Background to the Border's background: Generally not necessary since as long as child's background color isn't set, the border's background color will show through. The only exception is in negative-Margin or RenderTransform situations where the child doesn't render entirely inside the containing border. In that case you will need to bind the child's Background to the Border's Background. This can't be done in a style applied to the BorderBrush without using an attached property. But if you're ok doing it without a style:

<Border x:Name="myBorder">  <!-- Style applied here -->
  <StackPanel Background="{Binding Background, ElementName=myBorder}" ...

this can also be done with an unnamed border with {Binding Background, RelativeSource={RelativeSource FindAncestor,Border,1}.

If you want to do it entirely in the style, you'll have to add some code. Basically, create a class with an attached property named something like "MyBindingTools.BindChildBackgroundToMyBackground". Add a PropertyChangedCallback so that when that property is set to "true", a binding is created on the child, basically:

BindingOperations.SetBinding(border, BackgroundProperty, new Binding("Background") { Source = this });

In addition you will need to monitor the Border's Child property so that when it changes the binding can be added to the new Child and removed from the old Child (if any).

I wouldn't recommend you do any of this unless you really need to, though. In your specific situation you can probably either bind the child manually or create a template that contains both the Border and the child control. Either of these would be better solutions that creating the attached property as I described.