WPF Window Blur + Drop shadow - c#

I've done some searching but I can't seem to find a way to use both a drop shadow on my window, and have the window's background blurred.
I'm currently using https://github.com/riverar/sample-win32-acrylicblur (all blur code in MainWindow.xaml.cs) to blur the background, but since the dropshadow requires some padding in the window to render the dropshadow in, the space of the dropshadow gets the blur applied to it too.
I tried using an OpacityMask, but that didn't seem to help. In fact, even when setting the Window's Opacity property to 0, the blur still showed, so I fear that it's not possible with this method of blurring.
One of the packages I already am using is Microsoft.Windows.Shell, which I need to rebuild the default buttons I lose after applying the drop shadow, perhaps this has something helpful.
TLDR:
Is there a way to use an Aero-style blurred Window and a drop shadow together? Ideally without installing extra packages, but if there's no other way I'll have to bite the bullet.
I'm on the latest versions of .Net etc. as of 03/08/2018

Do you mean the effect shown below?
If so, you can write the XAML code to get it.
<Window x:Class="Walterlv.Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"
AllowsTransparency="True" WindowStyle="None" Background="{x:Null}">
<Grid>
<Rectangle x:Name="ShadowShape" Fill="White" Margin="8">
<Rectangle.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" />
</Rectangle.Effect>
</Rectangle>
<Border x:Name="BackgroundBorder" Margin="8" ClipToBounds="True">
<Rectangle x:Name="BlurringShape" Margin="-32">
<Rectangle.Fill>
<ImageBrush ImageSource="Sample.jpg"/>
</Rectangle.Fill>
<Rectangle.Effect>
<BlurEffect KernelType="Gaussian" Radius="32" />
</Rectangle.Effect>
</Rectangle>
<Border.CacheMode>
<BitmapCache />
</Border.CacheMode>
</Border>
</Grid>
<Grid>
<!-- Write your own content here... -->
</Grid>
</Window>
Notes:
I write three UIElement to implement that effect:
The BlurringShape renders a bitmap and blur itself. It blurs at the radius 32, so it should set a -32 margin to avoid the transparent blur.
The BackgroundBorder clips the BlurringShape so that the blur will not spill over.
Because we have clipped the BackgroundBorder, so if we set a DropShadowEffect on it, it will be clipped. We should create another shape to render the DropShadowEffect. That is the ShadowShape.
If you want your style more reusable, you can take this code below:
<Window x:Class="Walterlv.Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Title="MainWindow" Height="450" Width="800">
<Window.Background>
<ImageBrush ImageSource="High+Sierra.jpg"/>
</Window.Background>
<Window.Style>
<Style TargetType="Window">
<Setter Property="AllowsTransparency" Value="True" />
<Setter Property="WindowStyle" Value="None" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<Grid>
<Rectangle Fill="White" Margin="8">
<Rectangle.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" />
</Rectangle.Effect>
</Rectangle>
<Border x:Name="BackgroundBorder" Margin="8" ClipToBounds="True">
<Rectangle Margin="-32" Fill="{TemplateBinding Background}">
<Rectangle.Effect>
<BlurEffect KernelType="Gaussian" Radius="32" />
</Rectangle.Effect>
</Rectangle>
<Border.CacheMode>
<BitmapCache />
</Border.CacheMode>
</Border>
<ContentPresenter Margin="8" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Style>
<Grid>
<!-- Write your own content here ... -->
</Grid>
</Window>

Related

Force transparent background on custom UserControl

I want to create a new UserControl and reroute the Background property to use it elsewhere than in the UserControl.Background property (like it is done on the checkbox for example).
Here is a simple custom usercontrol:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Controls"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
x:Class="Controls.HexagonalTile"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="300">
<Grid>
<ed:RegularPolygon Fill="{Binding ElementName=LayoutRoot, Path=Background}" StrokeThickness="5" Stroke="Black"/>
</Grid>
And I want to use it like this:
<Controls:HexagonalTile HorizontalAlignment="Left" Height="100" Width="100" Background="Aqua" />
But when I do this, the corner of my user control, outside of the hexagone, take the background color too. I want them to stay transparent.
Thank's for your help.
The reason why this is happening is because the default ControlTemplate for a UserControl has a Border with a TemplateBinding to the Background property.
However, you can re-template the control like this to achieve your goal:
<UserControl x:Class="WpfApp4.HexagonalTile"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing">
<UserControl.Template>
<ControlTemplate TargetType="{x:Type UserControl}">
<Grid>
<ContentPresenter />
</Grid>
</ControlTemplate>
</UserControl.Template>
<Grid>
<ed:RegularPolygon
Fill="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Stroke="Black"
StrokeThickness="5" />
</Grid>
</UserControl>
I hope this helps!

WPF Custom Toggle button with ContentControl

It seems very hard to achieve something rather trivial in WPF...
I need to design a toggle button with a specific look (and feel). I made a small project to demonstrate the problem.
"ToggleButton user control" :
<UserControl x:Class="WpfApp4.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"
Name="Bla">
<UserControl.Resources>
<Style TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ToggleButton
Width="300" Height="300">
<ContentControl Content="{Binding ElementName=Bla, Path=MainContent}"/>
</ToggleButton>
</UserControl>
Dependency property:
public static DependencyProperty MainContentProperty = DependencyProperty.Register(
"MainContent",
typeof(object),
typeof(UserControl1),
null);
public object MainContent
{
get => GetValue(MainContentProperty);
set => SetValue(MainContentProperty, value);
}
The way I want to use the control:
<local:UserControl1>
<TextBlock>Whatever</TextBlock>
</local:UserControl1>
When I run the program, the textbox appears "Whatever", but the style is not applied, the ellipse won't show.
What's the correct way of doing this?
=== Update ===
OK, getting somewhere... finally...
Now I got this as user control:
<UserControl x:Class="WpfApp4.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:wpfApp4="clr-namespace:WpfApp4"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"
Name="Bla">
<UserControl.Resources>
<Style TargetType="wpfApp4:UserControl1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton>
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter Content="{Binding ElementName=Bla, Path=MainContent}" />
</Grid>
</ToggleButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ContentPresenter/>
</UserControl>
And this is how I use it:
<local:UserControl1>
<local:UserControl1.MainContent>
<TextBlock>Whatever</TextBlock>
</local:UserControl1.MainContent>
</local:UserControl1>
That finally gives me a toggle button with the style applied (the ellipse shows up) and the textbox is shown as well.
So, this works. Is this the way you mean it should work? Or can it be simplified?
It should be more like
<local:UserControl1>
<local:UserControl1.MainContent>
<TextBlock>Whatever</TextBlock>
</local:UserControl1.MainContent>
</local:UserControl1>
But you should look forward overriding ContentControl which would be more adequate rather then using UserControl.
By the way why did you put a ContentControl inside ToggleButton? ToggleButton by itself is a ContentControl it has it's own Content property.
Update:
All depends on what you whant to do. If it is only change the visual of the toggle button, then just create a toggle button style like this:
<ToggleButton>
<TextBlock>Whatever</TextBlock>
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Ellipse Width="300" Height="300" Fill="Yellow"/>
<ContentPresenter Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
Ofcorse if you want to use your style across the application it is better to define the style in a resource dictionnary (for exemple in App.xaml), give it a key and call it on each toggle button using {StaticResource key}.
If on the other hand, you want to add some logic, you have to create a control class inheriting from ToggleButton and add the logic inside.

Rotating Controls within a StackPanel

I am trying to create my own expandable/collapsible menu (creatively called ExpandingMenu) in my first WPF project. I already have one user control consisting of a 2-row Grid (row 1 is a button to collapse and expand the control, row 2 is a StackPanel for rotating ToggleButtons, which is where I'm currently stuck). For my rotating buttons, I have just decided to make them their own UserControls as well.
The Buttons (I'm calling them ExpandingMenuButtons) are no more than a ToggleButton in a 1x1 Grid (I'm thinking of doing it this way since I may want to add some extra custom logic to these buttons after I get the standard behavior sorted out). I can add them to my menu control successfully, I can even get them to rotate via a RenderTransform on the Grid.
However, as you can probably tell, they swing up when they rotate. This causes them to not only be too high, but they also likely extend to far up.
This is what I currently have, before attempting rotations, it is behaving as expected.
This is what I'm trying to accomplish (edited using the magic of paint). I can get this correct behavior in my Menu control (tan areas), but I've mangled the expand/contract event in the meantime for testing purposes...
What I can do when I rotate 1 button (Like I mentioned earlier, I've mangled some behavior for my testing purposed, so each button is set to rotate on click, not all at once like you may expect). As you can see, this button has swung out from where it originally was. buttons higher up will swing partially/completely out of view. Instead, I would like them to rotate into the proper place. once I get one to work right, I assume it will be simple to get the rest behaving the same way, which I why I'm trying things this way..
My button code is below:
<UserControl x:Class="App.Controls.ExpandingMenuButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:App.Controls"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="100">
<Grid Name="ButtonGrid" Height="30">
<ToggleButton Name="MenuButton" Background="Aqua" BorderThickness="0 0 0 1" Click="MenuButton_Click" Content="TEST"></ToggleButton>
</Grid>
</UserControl>
the only "real" code in ExpandingMenuButton.xaml.cs so far:
private void MenuButton_Click(object sender, RoutedEventArgs e)
{
//I know this is not practical, it is used for quick testing.
ButtonGrid.RenderTransform = new RotateTransform(-90);
}
My menu code so far:
<UserControl x:Class="App.Controls.ExpandingMenu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:App.Controls"
mc:Ignorable="d"
Name="ucExpandingMenu"
MinWidth="32"
d:DesignHeight="300" d:DesignWidth="100">
<UserControl.Resources>
<SolidColorBrush x:Key="BackColor" Color="PeachPuff"/>
<!-- This style is used for buttons, to remove the WPF default 'animated' mouse over effect. http://stackoverflow.com/a/4182205/2957232 -->
<Style x:Key="ExpandButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border"
BorderThickness="0 0 0 1"
BorderBrush="Black"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Name="MenuPanel" Width="100" HorizontalAlignment="Left" Background="{DynamicResource BackColor}" Grid.Row="1">
<!--Contents will go here-->
</StackPanel>
<Button Style="{StaticResource ExpandButtonStyle}" Width="100" Height="32" FontSize="18" VerticalAlignment="Center" HorizontalAlignment="Stretch" Panel.ZIndex="1" Background="{DynamicResource BackColor}" BorderThickness="0" Click="Button_Click" Content="ยป"></Button>
<Button Name="DummyFocus" Panel.ZIndex="0" Height="0" Width="0"></Button>
</Grid>
</UserControl>
And again, the only "real" code in this class so far:
private void Button_Click(object sender, RoutedEventArgs e)
{
MenuPanel.Children.Add(new ExpandingMenuButton("TEST ITEM"));
}
Please excuse my lack of WPF knowledge, I'm trying to come from a world of Winforms, where even there I have a lack of knowledge when it comes to this sort of thing. I know the code looks kinda funny, but hopefully the images show what I'm after here. So far I'm just testing this in a dummy window with only a grid and the HorizontalAlignment set to "Left". Nothing fancy.
Use LayoutTransform. Using RenderTransform does not affect how other controls (including parent) are being rendered/laid out; LayoutTransform does.
Shift the transform to the whole UserControl. You can probably specify directly in XAML instead.
Example:
<UserControl.LayoutTransform>
<RotateTransform Angle="-90" />
</UserControl.LayoutTransform>

WPF - Adding a ObjectDataProvider breaks my designer

Perhaps i'm not using the ObjectDataProvider correctly, but im following the MSDN examples so im not sure whats going wrong.
Goal: When i click a button, it will close the window by calling a method "exitButtonMethod" which simple does this.Close();.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" WindowStyle="None" AllowsTransparency="True"
WindowStartupLocation="CenterScreen" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="254" Width="438" Opacity="1" Background="{x:Null}">
<Window.Resources>
<ObjectDataProvider ObjectType="{x:Type local:Window1}"
MethodName="exitButtonMethod" x:Key="callExitButtonMethod">
</ObjectDataProvider>
<Style x:Key="ExitButtons" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="exitButtonTemplate" TargetType="Button">
<Grid>
<Ellipse x:Name="exitButtonEllipse" Fill="#597E0000"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="exitButtonEllipse" Property="Fill" Value="#897E0000" />
<Binding Source="{StaticResource callExitButtonMethod}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Width="400" Height="200" Opacity="1">
<Rectangle Height="200" HorizontalAlignment="Left" VerticalAlignment="Top" Name="rectangle1" Stroke="#FF7E0000" Width="400" RadiusX="40" RadiusY="40" Fill="#64860000" StrokeThickness="3" />
<Button Style="{StaticResource ExitButtons}" Content="X" Height="25" Width="25" Margin="359,16,16,0" VerticalAlignment="Top" Focusable="True" FontSize="15" FontFamily="PMingLiU" Foreground="#FF7E0000" Opacity="1"/>
</Grid>
The error is that it simply breaks my designer and gives me the following error in the designer:
System.Runtime.Remoting.RemotingException
[228940] Designer process terminated unexpectedly!
What the purpose of ObjectDataProvider is to create objects in XAML that you can bind to. You can also use it to bind to the results of calling a method on that object, which is what you are trying to do.
Here you are making an new object with the type of Window1 and binding to the method callExitButtonMethod. So you are unintentionally making a new window inside your window.
<ObjectDataProvider ObjectType="{x:Type local:Window1}"
MethodName="exitButtonMethod" x:Key="callExitButtonMethod">
</ObjectDataProvider>
Now that new window, when created, also has a window inside of it... etc. and you get an infinite loop of making windows.
This is why you are getting a stack overflow.
What you are trying to do is much simpler than what you are currently doing. In order to call a method on a button when you click it, you can simply do:
<Button Click="NameOfMethodHere" />
In your case, just add the Click parameter to your button and get rid of the ObjectDataProvider.
EDIT: Also to set events from a Style, see EventSetter.

Trying to WPF Tab header, need to make 3 adjustments

I want to alter the styling of some WPF tab headers. I would like to keep all the original styling of the tab headers, except for these three things -
Increase the height of the headers
Make the heights of each header the same. Normally the selected tab has a bigger height, I need the heights of both selected and unselected tabs to be the same.
Add a picture above the text on each header
Here is a before and after image of what I am looking to do -
Anyone know how to do this?
There you go, you can replace Stack panel with your nice images.
Update 1- in order to remove sizing effect when seelcting a tab you'll need to alter the TabItem style (header template is too light for it). Just get a StyleSnooper (http://blog.wpfwonderland.com/2007/01/02/wpf-tools-stylesnooper/) open it with VS2010 recompile it for .NET4, launch, navigate to TabItem and search for:
<Setter Property="FrameworkElement.Margin">
<Setter.Value>
<Thickness>
2,2,2,2</Thickness>
</Setter.Value>
</Setter>
<Setter Property="FrameworkElement.Margin" TargetName="Content">
<Setter.Value>
<Thickness>
2,2,2,2</Thickness>
</Setter.Value>
margins are the values you want to change to fix your 2. Then just put the modified version into the resources, so the app can pick it up. The style contains a lot of handy stuff you can tweak.
<Window x:Class="Immutables.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl TabStripPlacement="Left" x:Name="AreasTabControl" Margin="1">
<TabItem x:Name="AttributesTab">
<TabItem.HeaderTemplate>
<DataTemplate>
<Grid Width="100" Height="40">
<Border BorderThickness="1" BorderBrush="Gray" HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel Orientation="Horizontal">
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="White" />
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="Blue" />
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="Red" />
</StackPanel>
</Border>
<TextBlock Margin="0,20,0,0">Go Russia!</TextBlock>
</Grid>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
</TabControl>
</Grid>
</Window>

Categories