I hope it is not too late for answering to your question. The solution for obtaining the result that you wish, in my opinion, is represented by the ShaderEffect class.
My approach has some limitations, but I guess it can be improved for workarounding them.
First of all let's see the result:

My idea is to create a custom ShaderEffect
, that I called BlurRectEffect
. For understanding this class your can read this very good tutorial.
public class RectBlurEffect : ShaderEffect
{
private static PixelShader pixelShader = new PixelShader();
private static PropertyInfo propertyInfo;
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(RectBlurEffect), 0);
public static readonly DependencyProperty UpLeftCornerProperty =
DependencyProperty.Register("UpLeftCorner", typeof(Point), typeof(RectBlurEffect),
new UIPropertyMetadata(new Point(0, 0), PixelShaderConstantCallback(0)));
public static readonly DependencyProperty LowRightCornerProperty =
DependencyProperty.Register("LowRightCorner", typeof(Point), typeof(RectBlurEffect),
new UIPropertyMetadata(new Point(1, 1), PixelShaderConstantCallback(1)));
public static readonly DependencyProperty FrameworkElementProperty =
DependencyProperty.Register("FrameworkElement", typeof(FrameworkElement), typeof(RectBlurEffect),
new PropertyMetadata(null, OnFrameworkElementPropertyChanged));
static RectBlurEffect()
{
pixelShader.UriSource = Global.MakePackUri("RectBlurEffect.ps");
propertyInfo = typeof(RectBlurEffect).GetProperty("InheritanceContext",
BindingFlags.Instance | BindingFlags.NonPublic);
}
public RectBlurEffect()
{
PixelShader = pixelShader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(UpLeftCornerProperty);
UpdateShaderValue(LowRightCornerProperty);
}
public Brush Input
{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
public Point UpLeftCorner
{
get { return (Point)GetValue(UpLeftCornerProperty); }
set { SetValue(UpLeftCornerProperty, value); }
}
public Point LowRightCorner
{
get { return (Point)GetValue(LowRightCornerProperty); }
set { SetValue(LowRightCornerProperty, value); }
}
public FrameworkElement FrameworkElement
{
get { return (FrameworkElement)GetValue(FrameworkElementProperty); }
set { SetValue(FrameworkElementProperty, value); }
}
private FrameworkElement GetInheritanceContext()
{
return propertyInfo.GetValue(this, null) as FrameworkElement;
}
private void UpdateEffect(object sender, EventArgs args)
{
Rect underRectangle;
Rect overRectangle;
Rect intersect;
FrameworkElement under = GetInheritanceContext();
FrameworkElement over = this.FrameworkElement;
Point origin = under.PointToScreen(new Point(0, 0));
underRectangle = new Rect(origin.X, origin.Y, under.ActualWidth, under.ActualHeight);
origin = over.PointToScreen(new Point(0, 0));
overRectangle = new Rect(origin.X, origin.Y, over.ActualWidth, over.ActualHeight);
intersect = Rect.Intersect(overRectangle, underRectangle);
if (intersect.IsEmpty)
{
UpLeftCorner = new Point(0, 0);
LowRightCorner = new Point(0, 0);
}
else
{
origin = new Point(intersect.X, intersect.Y);
origin = under.PointFromScreen(origin);
UpLeftCorner = new Point(origin.X / under.ActualWidth,
origin.Y / under.ActualHeight);
LowRightCorner = new Point(UpLeftCorner.X + (intersect.Width / under.ActualWidth),
UpLeftCorner.Y + (intersect.Height / under.ActualHeight));
}
}
private static void OnFrameworkElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
RectBlurEffect rectBlurEffect = (RectBlurEffect)d;
FrameworkElement frameworkElement = args.OldValue as FrameworkElement;
if (frameworkElement != null)
{
frameworkElement.LayoutUpdated -= rectBlurEffect.UpdateEffect;
}
frameworkElement = args.NewValue as FrameworkElement;
if (frameworkElement != null)
{
frameworkElement.LayoutUpdated += rectBlurEffect.UpdateEffect;
}
}
}
This class computes the intersection between the effected control and the overlapper one. Then it sends those informations to the so called "pixel shader" file (a .fx file, that will be compiled into a .ps file).
Now we need to create the RectBlueEffect.fx file. It uses the HLSL - (i.e. High Level Shading Language). Here its content:
sampler2D rectBlurEffect : register(S0);
float2 upperLeftCorner : register(C0);
float2 lowerRightCorner : register(C1);
float Angle : register(C2);
float BlurAmount : register(C3);
float PI = 3.14159265358979323846;
float EPSILON = 0.0001;
float ComputeGaussian(float n)
{
float theta = 2.0f + EPSILON; //float.Epsilon;
return theta = (float)((1.0 / sqrt(2 * PI * theta)) *
exp(-(n * n) / (2 * theta * theta)));
}
float4 gaussianblur(float2 texCoord: TEXCOORD0) : COLOR
{
float SampleWeights[7];
float2 SampleOffsets[15];
// The first sample always has a zero offset.
float2 initer = { 0.0f, 0.0f };
SampleWeights[0] = ComputeGaussian(0);
SampleOffsets[0] = initer;
// Maintain a sum of all the weighting values.
float totalWeights = SampleWeights[0];
// Add pairs of additional sample taps, positioned
// along a line in both directions from the center.
for (int i = 0; i < 7 / 2; i++)
{
// Store weights for the positive and negative taps.
float weight = ComputeGaussian(i + 1);
SampleWeights[i * 2 + 1] = weight;
SampleWeights[i * 2 + 2] = weight;
totalWeights += weight * 2;
float sampleOffset = i * 2 + 1.5f;
float2 delta = { (1.0f / 512), 0 };
delta = delta * sampleOffset;
// Store texture coordinate offsets for the positive and negative taps.
SampleOffsets[i * 2 + 1] = delta;
SampleOffsets[i * 2 + 2] = -delta;
}
// Normalize the list of sample weightings, so they will always sum to one.
for (int j = 0; j < 7; j++)
{
SampleWeights[j] /= totalWeights;
}
float4 color = 0.0f;
for (int k = 0; k < 7; k++)
{
color += tex2D(rectBlurEffect,
texCoord + SampleOffsets[k]) * SampleWeights[k];
}
return color;
}
float4 directionalBlur(float2 uv : TEXCOORD) : COLOR
{
float4 c = 0;
float rad = Angle * 0.0174533f;
float xOffset = cos(rad);
float yOffset = sin(rad);
for (int i = 0; i < 12; i++)
{
uv.x = uv.x - BlurAmount * xOffset;
uv.y = uv.y - BlurAmount * yOffset;
c += tex2D(rectBlurEffect, uv);
}
c /= 12;
return c;
}
float4 main(float2 uv : TEXCOORD) : COLOR
{
if (uv.x < upperLeftCorner.x || uv.y < upperLeftCorner.y || uv.x > lowerRightCorner.x || uv.y > lowerRightCorner.y)
{
return tex2D(rectBlurEffect, uv);
}
return gaussianblur(uv);
}
As you can see, if a pixel is outside of a rectangle (the intersection between the two controls areas) no effect is applied. Otherwise the main
method uses an effect (i.e. a gaussian blur).
You can find here an implementation of the method that I called gaussianblur
. Just take a look to the last answer.
Now we have to compile the RectBlueEffect.fx
file. You can find some instructions either in the previous link (the tutorial) or here.
The XAML is the last part of my solution:
<Grid>
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<Border Background="Brown" BorderThickness="0" Name="tb">
<TextBlock Text="Hello World!" Margin="4" Padding="10"
FontSize="30" FontWeight="Bold" Foreground="Yellow"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
TextAlignment="Center" />
<Border.Effect>
<local:RectBlurEffect FrameworkElement="{Binding ElementName=Border, Mode=OneTime}" />
</Border.Effect>
</Border>
<TextBlock Background="Khaki" Text="I should be partially blurred!" Margin="4" Padding="10"
Foreground="DarkGreen" FontSize="30" TextWrapping="Wrap" FontFamily="Cambria"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
TextAlignment="Center">
<TextBlock.Effect>
<local:RectBlurEffect FrameworkElement="{Binding ElementName=Border, Mode=OneTime}" />
</TextBlock.Effect>
</TextBlock>
</StackPanel>
<Border Name="Border" Width="200" Height="260" BorderThickness="0"
Background="Black" Opacity=".6" Panel.ZIndex="20">
<TextBlock Text="Pretend I'm a border dumped over a grid" TextWrapping="Wrap"
HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="16"
Foreground="AntiqueWhite" Background="Transparent"
Margin="8" />
</Border>
</Grid>
As you can suppose my solution allows to overlap just rectangles (or squares), but it does not work for ellipses, circles or irregular polygons. Of course everything depends on HLSL code that you write in the .fx file.