I want to select a UI control to be clicked and gray out the rest of the desktop screen semi-transparently. I was thinking bitmap painting the whole screen but it is very slow process. I guess someone out there know in WPF how to do that. I don't have experiance in WPF. I wanted to do this on Windows 7.
-
Like the UAC pop-up in vista/7 ? – Catalin DICU Apr 28 '11 at 22:01
-
ya. Like the snipping tool of Windows 7 – Deku Apr 28 '11 at 22:52
3 Answers
Basically you want to show a top-level full screen transparent window which is not focusable and doesn't respond to input. You can then use this window to draw the overlay manually. I think the easiest way would be to override OnRender on the Window and to draw a rectangle that fills the whole window but uses a clipping mask (via drawingContext.PushClip) to exclude the area you want to leave uncovered.
EDIT:
Here is an example:
The Window should probably be set up like this:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
AllowsTransparency="True"
WindowStyle="None"
WindowState="Maximized"
Topmost="False"
Background="{x:Null}"
IsHitTestVisible="False">
</Window>
The WindowStyle
and WindowState
settings will cause the window to be maximized and overlapping the task bar.
Topmost
set to true will cause the window to be on top of all other windows and IsHitTestVisible
will cause the mouse clicks to 'fall through'.
Warning: If you set this you will be stuck with a topmost window that you can't close since it doesn't listen to keyboard commands. You will need to manually close the window somewhere in your code. Likely you want to create a global mouse hook to listen to mouse up and a keyboard hook to listen to ESC or something.
To save people from themselves I have set TopMost to False in the above example. Only change it to true if you have figured out how/when to close the window in code somewhere.
The Background
is set to null so that you can use your custom drawing in code behind.
the code behind similar to this:
public MainWindow()
{
InitializeComponent();
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var screenGeometry = new RectangleGeometry(new Rect(0, 0, ActualWidth, ActualHeight));
var excludeRectangle = new RectangleGeometry(new Rect(200, 200, 150, 150));
drawingContext.PushClip(CombinedGeometry.Combine(screenGeometry,excludeRectangle, GeometryCombineMode.Exclude,null));
drawingContext.PushOpacity(.8);
drawingContext.DrawRectangle(Brushes.Black, null, new Rect(0, 0, ActualWidth, ActualHeight));
drawingContext.Pop();
drawingContext.Pop();
}
where you would use the proper location and size for the excludeRectangle.
If you run this code you will see the screen grayed out except for a little rectangle in the top left.

- 14,056
- 13
- 71
- 118
-
This is cool. Thanks. Is it possible to add the Xaml in c# code or the whole code-behind file in Xaml with OnRender event. The reason i needed that is i am trying to add this into powershell using Add-type – Deku May 02 '11 at 22:21
-
I don´t know about powershell but you can easily do this in C# without XAML. simple create a class inheriting from Window (System.Windows.Window - you'll need to reference PresentationFramework.dll) and override OnRender there. Then set the properties I set in XAML in C# and show the window manually via Show()... – Patrick Klug May 02 '11 at 23:25
-
and since you are new here make sure to accept this as an answer if it answered your question. - welcome to stackoverflow. – Patrick Klug May 02 '11 at 23:27
-
Overriding render is a bit overkill if all you want is the windows snip tool experience ... it works by taking a screen-shot of the window and covering the whole screen with that. – Jaykul May 03 '11 at 01:49
-
depends on the use case. He didn't say that he wants to create a screen shot taking application and I think for anything but a screen taking app showing a static screen shot is not desirable. you wouldn't see any updates in the background (unless you update the screen shot) but it seems overkill to go through all that trouble and to deal with the higher memory consumption and image management when you can easily write a ten liner in OnRender instead. – Patrick Klug May 03 '11 at 02:16
If you want to blur the MainWindow and focus on new Popup window, you can check this:
System.Windows.Media.Effects.BlurEffect objBlur = new System.Windows.Media.Effects.BlurEffect();
((MainWindow)App.Current.MainWindow).Effect = objBlur;
mainFrame.Navigate(new PopUp_page1());
You can also use Window instead of Popup window.
To remove the effect:
((MainWindow)App.Current.MainWindow).Effect = null;

- 1,290
- 10
- 10
I used this code. But it looks like it is not using the OnRender. It just shows White Window.
namespace GrayOutTEST
{
class MainWindow1 : Window
{
private Window window;
public MainWindow1() { }
public void CreateWindow()
{
window = new Window();
window.Title = "WIndow Title";
window.Height = Screen.PrimaryScreen.Bounds.Height;
window.Width = Screen.PrimaryScreen.Bounds.Width;
window.Topmost = false;
window.IsHitTestVisible = false;
window.AllowsTransparency = true;
window.WindowStyle = WindowStyle.None;
window.WindowState = WindowState.Maximized;
// window.Background = null;
window.Show();
}
public void CloseWindow()
{
window.Close();
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var screenGeometry = new RectangleGeometry(new Rect(0, 0, ActualWidth, ActualHeight));
var excludeRectangle = new RectangleGeometry(new Rect(200, 200, 150, 150));
drawingContext.PushClip(CombinedGeometry.Combine(screenGeometry, excludeRectangle, GeometryCombineMode.Exclude, null));
drawingContext.PushOpacity(.8);
drawingContext.DrawRectangle(System.Windows.Media.Brushes.Black, null, new Rect(0, 0, ActualWidth, ActualHeight));
drawingContext.Pop(); drawingContext.Pop();
}
[STAThread]
static void Main(string[] args)
{
MainWindow1 w = new MainWindow1();
w.CreateWindow();
Console.Read();
}
}
}

- 129
- 4
- 11
-
you are showing a new Window instead of a MainWindow1. you have to show the MainWindow1 as this is where you implemented the OnRender method. also set the background to null. – Patrick Klug May 04 '11 at 00:25
-
you could take the whole CreateWindow thing and do the same things on your w variable which is of type MainWindow1 and uncomment the background=null; bit and it should work. – Patrick Klug May 04 '11 at 00:26