How to imitate OuterGlowBitmapEffect using WPF effects? - c#

I tried using DropShadowEffect but it's "glow" strength weakens when you increase BlurRadius. I want to have strong outer glow like the image below.
If I stack the same effect a dozen times I am able to get this, but the performance tanks after this. Is this possible to do using WPF with a single effect?

Depending on how large you want the radius of the blur to be and how smooth the result needs to be, you could stack different effects like gradient stops instead of repeatedly stacking the same effect.
Like you pointed out, the DropShadowEffect strength gets weaker as the BlurRadius increases.
<TextBox Text="DropShadowEffect">
<TextBox.Effect>
<DropShadowEffect BlurRadius="50"
ShadowDepth="0"
Color="Blue"
Opacity="1"/>
</TextBox.Effect>
</TextBox>
Additionally, applying effects directly to the TextBox impacts the rendering quality of the text. The proposed solution to the linked question (setting TextOptions.TextFormattingMode="Display" on the Window) also has layout implications. Instead, you can draw a Rectangle with a BlurEffect behind your element.
<Rectangle Fill="Blue"
Height={Binding ElementName=MyTextBox, Path=ActualHeight}"
Width={Binding ElementName=MyTextBox, Path=ActualWidth}">
<Rectangle.Effect>
<BlurEffect Radius="50"/>
</Rectangle.Effect>
</Rectangle>
<TextBox x:Name="MyTextBox" Text="Rectangle with BlurEffect"/>
You can then add an additional Rectangle for each gradient stop. Here there are two: one at 50 to define the overall size of the blur, and one at 30 to strengthen the glow around the control.
<Rectangle Fill="Blue"
Height={Binding ElementName=MyTextBox, Path=ActualHeight}"
Width={Binding ElementName=MyTextBox, Path=ActualWidth}">
<Rectangle.Effect>
<BlurEffect Radius="50"/>
</Rectangle.Effect>
</Rectangle>
<Rectangle Fill="Blue"
Height={Binding ElementName=MyTextBox, Path=ActualHeight}"
Width={Binding ElementName=MyTextBox, Path=ActualWidth}">
<Rectangle.Effect>
<BlurEffect Radius="30"/>
</Rectangle.Effect>
</Rectangle>
<TextBox x:Name="MyTextBox" Text="Two Rectangles with BlurEffect"/>
You asked about the perceived sharpness around the corners of the TextBox and I must admit I don't have a good solution. I initially considered rounding the corners of the blurred elements behind your control by using a Border instead of a Rectangle, but I honestly don't see much of a difference.
<!-- Remove the CornerRadius property for square corners. -->
<Border CornerRadius="10" Background="Blue">
<Border.Effect>
<BlurEffect Radius="50"/>
</Border.Effect>
</Border>
<Border CornerRadius="10" Background="Blue">
<Border.Effect>
<BlurEffect Radius="30"/>
</Border.Effect>
</Border>
Of course, you could always make the background objects larger than your control. Here they are in the same cell of a Grid but there is extra space for the Border to grow because the TextBox has a Margin. A smaller top/bottom margin and larger left/right margin means the glow will be more uniform around the control.
<!-- These items should be in the same cell of a Grid -->
<Border CornerRadius="10" Background="Blue">
<Border.Effect>
<BlurEffect Radius="50"/>
</Border.Effect>
</Border>
<Border CornerRadius="10" Background="Blue">
<Border.Effect>
<BlurEffect Radius="30"/>
</Border.Effect>
</Border>
<TextBox Text="TextBox has an 8px, 4px margin" Margin="8 4"/>

Related

Fit Border around arbitrary Path

I have a Path based on a group of rectangle/ellipse Geometry. When I put an auto-sized Border around it, the border shrinks to fit the RectangleGeometry but does something weird with the EllipseGeometry, as shown below. Anyone know how to fix it so the Border (shown in blue) fits snugly around the whole thing?
Ultimately, I want a Border or a Panel that fits an arbitrary, runtime-generated Path (including the stroke thickness), and I need the geometry size maintained.
Sample code:
<Border BorderBrush="Blue" BorderThickness="2"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Stroke="Orange" StrokeThickness="5" Fill="Red">
<Path.Data>
<GeometryGroup>
<EllipseGeometry Center="-10,-10" RadiusX="12" RadiusY="12" />
<EllipseGeometry Center="70,0" RadiusX="70" RadiusY="10">
<EllipseGeometry.Transform>
<RotateTransform Angle="-25" CenterX="50" CenterY="0" />
</EllipseGeometry.Transform>
</EllipseGeometry>
<RectangleGeometry Rect="0,0,100,100" />
</GeometryGroup>
</Path.Data>
</Path>
</Border>
You may set the Path's Stretch property and bind its Width to the Width of the Bounds of the Geometry:
<Path ... Stretch="Uniform"
Width="{Binding Data.Bounds.Width, RelativeSource={RelativeSource Self}}">
...
</Path>
As the Geometries's Bounds do not contain the rendered Path's StrokeThickness, you may alternatively put the Border into a Canvas (which does not resize its child elements at all), and don't set the Path's Width. This may may however lead to problems when you want to center the Canvas in an outer Grid. It's actual size appears to be zero.
<Canvas>
<Border BorderBrush="Blue" BorderThickness="2">
<Path ... Stretch="Uniform">
...
</Path>
</Border>
</Canvas>

Resizable circle button xaml

I'm developping a universal app for Windows in XAML/C# and I can't manage to create a circle button that I can resize. I use an Ellipse with uniform stretch so as to make it circle and a ContentPresenter.
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Stretch="Uniform">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
The problem is a uniform ellipse is automatically aligned top, left, and it's impossible to make it stretch the grid. When I resize the button, the ContentPresenter stays in the center while the ellipse stays in the top left corner. I'd like to be able to resize the button and that the text stays in the center of the circle.
Thanks for help!
You may use a Path with a circular EllipseGeometry:
<ControlTemplate TargetType="Button">
<Grid>
<Path Stretch="Uniform" ...>
<Path.Data>
<EllipseGeometry RadiusX="1" RadiusY="1"/>
</Path.Data>
</Path>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
You would however have to explicitly set the Width or Height of the Button, otherwise it would take up all available space.
I've also found another solution which is to use a ViewBox:
<ControlTemplate TargetType="Button">
<ViewBox>
<Grid>
<Ellipse Stretch="Uniform" Width="50" Height="50">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ViewBox>
</ControlTemplate>
The ViewBox automatically scales everything when resized. You have to set Width and Height, but it's only to set proportions. Very useful when using the user control with both Windows and Windows Phone.
Thanks for your answers!

Outer Glow Effect and rotation On A Label in WPF

I need to create an outer glow effect on a label and make it rotate a little (by about 20 degrees). I'm using the following code, but it's not working the way I want it to:
<Label Height="106" Margin="80,57,36,0" Name="lblHeading" FontSize="35">
Brian's 15th Birthday Party
<Label.Effect>
<DropShadowEffect BlurRadius="100" ShadowDepth="0" Opacity="1"
Color="White"/>
</Label.Effect>
</Label>
Is it possible to add some text somewhere in the Window and add an outer glow effect and rotation to it? It would be great if anyone can help me out with adding the same effect on a label or any other way to do so without using a label control.
I tried the following, too, but it's not helping. Maybe I don't know how to use it because it's just causing an error:
<OuterGlowBitmapEffect GlowColor="Blue" GlowSize="30" Noise="1" Opacity="0.4" />
You'll probably want to use a smaller BlurRadius, setting it to 100 will make the effect close to invisible. I suggest 10.
Set the RenderTransformOrigin to the point you want the text to rotate around (0.5, 0.5 means rotate around the center).
Add a RotateTransform inside Label.RenderTransform.
The complete code should look close to this:
<Label Height="106" Margin="80,57,36,0" Name="lblHeading" FontSize="35"
RenderTransformOrigin="0.5, 0.5">
Brian's 15th Birthday Party
<Label.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="0" Opacity="1"
Color="White"/>
</Label.Effect>
<Label.RenderTransform>
<RotateTransform Angle="20"/>
</Label.RenderTransform>
</Label>
This is how you can rotate your label:
<Label>
<Label.LayoutTransform>
<RotateTransform Angle="20"/>
</Label.LayoutTransform>
  <Label.Content>text</Label.Content>
</Label>
This can be done through modification in ControlTemplate.
The key point is using two ContentPresenter to display your text, and attach BlurEffect to one ContentPresenter.
The code:
<Label Content="TestContent" Foreground="White" FontSize="20">
<Label.Template>
<ControlTemplate TargetType="Label">
<Border>
<Grid>
<ContentPresenter TextBlock.Foreground="{TemplateBinding Foreground}"/>
<ContentPresenter TextBlock.Foreground="{TemplateBinding Foreground}">
<ContentPresenter.Effect>
<BlurEffect Radius="5"/>
</ContentPresenter.Effect>
</ContentPresenter>
</Grid>
</Border>
</ControlTemplate>
</Label.Template>
</Label>
How it looks

Blurry text in WPF even with ClearTypeHinting enabled?

I have a grid with this template and styles in WPF/XAML:
<Setter Property="TextOptions.TextFormattingMode" Value="Display" />
<Setter Property="RenderOptions.ClearTypeHint" Value="Enabled" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter x:Name="CellContent" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RenderOptions.ClearTypeHint="Enabled" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="CellContent" Property="TextOptions.TextFormattingMode" Value="Display" />
<Setter TargetName="CellContent" Property="RenderOptions.ClearTypeHint" Value="Enabled" />
<Setter TargetName="CellContent" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="2" BlurRadius="2" Color="Black" RenderingBias="Quality" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
The DropShadowEffect I have when you select a grid row, seems to make the text rendering blurry (gray anti-aliasing):
When I remove the drop shadow effect, it looks clear because it now uses ClearType and not gray sub-pixel anti-aliasing:
I have tried applying RenderOptions.ClearTypeHint="Enabled" to the ContentPresenter as seen above, but it does not help.
How do I force WPF to render the text that gets displayed with drop shadow effect to retain Cleartype anti-aliasing, instead of that ugly blurry gray sub-pixel anti-aliasing?
Some believe it's blurry because of the drop shadow -- this is not true. It's blurry only because ClearType is not used. This is how it looks like in Firefox when shadow AND ClearType:
ClearType enabled text is colorful -- but that blurry text is not, because it does not use ClearType -- it uses gray sub-pixel anti-aliasing and that's not how ClearType works: http://en.wikipedia.org/wiki/ClearType
The question is: how do I enable ClearType for this text?
How about setting TextOptions.TextFormattingMode to Display as well as RenderOptions.BitmapScalingMode to NearestNeighbor? The latter is new in WPF 3.5 SP1 and I normally use it to remove the blur. :)
<TextBlock Text="Hello world" TextOptions.TextFormattingMode="Display"
RenderOptions.BitmapScalingMode="NearestNeighbor"
HorizontalAlignment="Center" TextWrapping="Wrap"
VerticalAlignment="Center" Foreground="White" FontFamily="Microsoft Sans Serif">
<TextBlock.Effect>
<DropShadowEffect ShadowDepth="2" BlurRadius="2" Color="Black"
RenderingBias="Quality"/>
</TextBlock.Effect>
</TextBlock>
Below is how it looks like.
And this is how it looks like in FireFox.
The DropShadowEffect object cannot work with ClearType. This is stated on the MSDN page How to: Create Text with a Shadow:
These shadow effects do not go through the Windows Presentation
Foundation (WPF) text rendering pipeline. As a result, ClearType is
disabled when using these effects.
After all, DropShadowEffect is a bitmap effect, not a text effect.
To achieve a similar result without using an effect, you can render the text twice, once slightly offset from the other:
<Grid>
<TextBlock Text="Here is some sample text" Foreground="Black" Margin="1,1,0,0"/>
<TextBlock Text="Here is some sample text" Foreground="White"/>
</Grid>
This yields the desired result:
You could also encapsulate this into a control (called ShadowTextBlock, perhaps) so that you don't have to go repeating yourself everywhere.
What about combining the two ideas. Draw the text with DropShadowEffect, and overlay it with the same text drawn without effect, like shown in the third line here:
Still not perfect, and i find it a little bold. But perhaps something you could live with. The XAML:
<StackPanel Background="LightSteelBlue" RenderOptions.ClearTypeHint="Enabled" SnapsToDevicePixels="True" >
<Grid Margin="5">
<TextBlock Foreground="Black" Text="Here is some sample text" Margin="1"/>
<TextBlock Foreground="White" Text="Here is some sample text"/>
</Grid>
<TextBlock Margin="5" Foreground="White" Text="Here is some sample text">
<TextBlock.Effect>
<DropShadowEffect ShadowDepth="2" BlurRadius="2" Color="Black" RenderingBias="Quality"/>
</TextBlock.Effect>
</TextBlock>
<Grid Margin="5">
<TextBlock Foreground="White" Text="Here is some sample text">
<TextBlock.Effect>
<DropShadowEffect ShadowDepth="2" BlurRadius="2" Color="Black" RenderingBias="Quality"/>
</TextBlock.Effect>
</TextBlock>
<TextBlock Foreground="White" Text="Here is some sample text"/>
</Grid>
</StackPanel>
The reason this doesn't work, and the reason you won't be able to get it to work, has to do with ClearType's sensitivity to what it's rendering on top of. For ClearType to look correct it essentially needs to do per-component alpha blending. That is, a separate alpha value for red, green, and blue (normally alpha applies to all 3). What this means is that ClearType absolutely must be rendered into a bitmap that is already opaque (all alpha values are 255) (you'll notice that window title bars still have ClearType, but they use some super secret tricks to do this).
The next step to understanding this is that WPF effects first render into an off-screen bitmap, and are then combined with what's beneath (in this case, solid white, or maybe blue if it's selected).
So, the text is first rendered into a clear, transparent bitmap. Since it has no idea what will eventually be below it, it must render using grayscale instead of ClearType. Then, the effect is applied to that bitmap. Then the bitmap is drawn to where you expect it to be on-screen, and there's no chance of getting ClearType even if it's on top of a solid color with no transparency.
As a possible workaround, try using 2 copies of the text. First, apply the effect to the "lower" version of the text (by "lower" I mean it should have a lower Z-index value, and that is the case if it is "first" in the XAML). Then, draw normal text on top of it (which will get ClearType). I think this will work but I haven't tried it, and you'll probably have to experiment until you get the desired visual outcome.
I know this post is old but if anyone is having problems with blurry text, a border drop shadow could be the culprit. I have custom panels with a drop shadow and only when the panels were placed side by side was the text blurry. I found the answer from this Why everything in WPF is blurry? post. The solution for me was ecreif's answer and not the accepted answer. Add this to your control
UseLayoutRounding="True"
RenderOptions.BitmapScalingMode="NearestNeighbor"
SnapsToDevicePixels="True
RenderOptions.ClearTypeHint="Enabled"

Advanced Effects in WPF?

I've been trying to recreate this GUI purely in WPF, and I'm having problems:
The shadow. The shadow on the main element doesn't show inside the fill - when using DropShadowEffect in WPF, it does. As of yet I've found no solution to this.
Background images - the main element has a slight pattern in it, a diagonal one. But in WPF, I can't use background images like this. Chances are I'm just missing something.
The slight inset, in the main element (at the top) - not easily replicated.
So anyway, I guess my question is, how can I accomplish these kinds of effects in WPF?
Hopefully this snippet will point you in the right direction:
<Grid>
<Border x:Name="Blur"
BorderThickness="5"
CornerRadius="5"
BorderBrush="#7F000000">
<Border.Effect>
<BlurEffect Radius="8" />
</Border.Effect>
</Border>
<Border x:Name="Outter"
BorderBrush="#CCD3D3D3"
BorderThickness="1"
CornerRadius="5"
Margin="2">
<Border.Background>
<ImageBrush Viewbox="0,0,45,38"
ViewboxUnits="Absolute"
Viewport="0,0,45,38"
ViewportUnits="Absolute"
TileMode="Tile"
ImageSource="<SomeImageThatIsATileOfThePattern>"
Opacity="0.3"
Stretch="Fill" />
</Border.Background>
</Border>
<Border x:Name="Inner"
BorderThickness="0,1,0,0"
CornerRadius="5"
Margin="2,4,2,2"
BorderBrush="#7FD3D3D3" />
<ItemsControl Background="HotPink"
Margin="11"
Height="21" />
</Grid>
The result is:
[I used a generic image, just to show the repetition. The original image is W50xH38 in size - hope the repetition is noticeable]
Play with the values for Viewbox and Viewport to adjust to your image.
Of course, the ItemsControl should not have the pink background and its height should not be a constant, it was done for demo purposes.

Categories

Resources