How to create a semi transparent form in WPF - wpf

I have to develop a semi-transparent form in WPF, but controls should not be transparent.
I have tried different things like setting opacity=0.5 but no result.
I know that AllowTransparency can be set to True only if WindowStyle set to None, but I need to show Border as well
UPDATE:
Pavlo Glazkov, What is your opinion for this solution
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" Opacity="1" AllowsTransparency="True" WindowStyle="None" Background="Transparent">
<Grid Background="Transparent">
<Border Margin="2,2,12,34" Name="border1" BorderBrush="Lavender" BorderThickness="5" CornerRadius="20,0,20,0"></Border>
<Button Height="23" Margin="93,101,110,0" Name="button1" VerticalAlignment="Top" Background="CadetBlue" Foreground="White">Hello WPF</Button>
<Button Height="24" Margin="0,8,20,0" Name="button2" VerticalAlignment="Top" HorizontalAlignment="Right" Width="21" Click="button2_Click">X</Button>
</Grid>
</Window>

First, you need to set AllowTransperency to True. Then, you can set the background of the window to a transparent (to desired extent) brush:
<Window x:Class="MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStyle="None"
AllowsTransparency="True"
Background="{DynamicResource WindowBackground}">
<Window.Resources>
<SolidColorBrush x:Key="WindowBackground"
Color="White"
Opacity="0.5"/>
</Window.Resources>
...
</Window>
Please note that AllowTransperency can be set to True only if WindowStyle set to None.
Update:
If you don't want to set WindowStyle to None and would like to keep standart border and window buttons there is an alternative that will work only on Windows Vista/7 with Windows Aero theme.
The trick is that you can extend the "Glass" area to the whole window using the following code:
public static class WindowUtils
{
/// <summary>
/// Extends the glass area into the client area of the window
/// </summary>
/// <param name="window">Window to extend the glass on.</param>
/// <param name="thikness">Thickness of border to extend.</param>
public static void ExtendGlass(this Window window, Thickness thikness) {
try {
int isGlassEnabled = 0;
Win32.DwmIsCompositionEnabled(ref isGlassEnabled);
if (Environment.OSVersion.Version.Major > 5 && isGlassEnabled > 0) {
// Get the window handle
var helper = new WindowInteropHelper(window);
var mainWindowSrc = HwndSource.FromHwnd(helper.Handle);
if (mainWindowSrc != null) {
if (mainWindowSrc.CompositionTarget != null) {
mainWindowSrc.CompositionTarget.BackgroundColor = Colors.Transparent;
}
// Get the dpi of the screen
System.Drawing.Graphics desktop =
System.Drawing.Graphics.FromHwnd(mainWindowSrc.Handle);
float dpiX = desktop.DpiX / 96;
float dpiY = desktop.DpiY / 96;
// Set Margins
var margins = new MARGINS {
cxLeftWidth = (int)(thikness.Left * dpiX),
cxRightWidth = (int)(thikness.Right * dpiX),
cyBottomHeight = (int)(thikness.Bottom * dpiY),
cyTopHeight = (int)(thikness.Top * dpiY)
};
window.Background = Brushes.Transparent;
Win32.DwmExtendFrameIntoClientArea(mainWindowSrc.Handle, ref margins);
}
}
else {
window.Background = SystemColors.WindowBrush;
}
}
catch (DllNotFoundException) {
}
}
}
public class Win32
{
[DllImport("dwmapi.dll")]
public static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
[DllImport("dwmapi.dll")]
public static extern int DwmIsCompositionEnabled(ref int en);
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int X, int Y);
[DllImport("User32", EntryPoint = "ClientToScreen", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern int ClientToScreen(IntPtr hWnd, [In, Out] POINT pt);
}
[StructLayout(LayoutKind.Sequential)]
public struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
[StructLayout(LayoutKind.Sequential)]
public class POINT
{
public int x = 0;
public int y = 0;
}
To extend the glass to the whole window you need to call the ExtendGlass extension method in a SizeChanged event handler of the window and pass a Thickness that covers whole window:
public MyWindow() {
InitializeComponent();
SizeChanged += OnSizeChanged;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e) {
double horisontalThickness = Width / 2;
double verticalThickness = Height / 2;
var glassThickness = new Thickness(horisontalThickness, verticalThickness, horisontalThickness, verticalThickness);
this.ExtendGlass(glassThickness);
}

You could try this, it will create a Glass Background for your Window (looks like the Vista and Windows7 transparency effect)
Here is some further explanation from Microsoft.

Related

Binding between a template-generated object and its parent's property

The title of this question might be wrong, I am not sure how to phrase it. I am trying to implement a very simple dashboard in which users can drag controls around inside a Canvas control. I wrote a MoveThumb class that inherits Thumb to achieve this. It is working well enough. Now, I want to make sure that the user cannot move the draggable control outside the Canvas. It is simple enough to write the logic itself to limit the drag boundaries inside this MoveThumb class:
Public Class MoveThumb
Inherits Thumb
Public Sub New()
AddHandler DragDelta, New DragDeltaEventHandler(AddressOf Me.MoveThumb_DragDelta)
End Sub
Private Sub MoveThumb_DragDelta(ByVal sender As Object, ByVal e As DragDeltaEventArgs)
Dim item As Control = TryCast(Me.DataContext, Control)
If item IsNot Nothing Then
Dim left As Double = Canvas.GetLeft(item)
Dim top As Double = Canvas.GetTop(item)
Dim right As Double = left + item.ActualWidth
Dim bottom As Double = top + item.ActualHeight
Dim canvasWidth = 450
Dim canvasHeight = 800
If left + e.HorizontalChange > 0 Then
If top + e.VerticalChange > 0 Then
If right + e.HorizontalChange < canvasWidth Then
If bottom + e.VerticalChange > canvasHeight Then
Canvas.SetLeft(item, left + e.HorizontalChange)
Canvas.SetTop(item, top + e.VerticalChange)
End If
End If
End If
End If
End If
End Sub
End Class
And the XML:
<Window x:Class="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"
xmlns:local="clr-namespace:diagramDesigner"
xmlns:s="clr-namespace:diagramDesigner"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Canvas x:Name="Canvas1">
<Canvas.Resources>
<ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type s:MoveThumb}">
<Rectangle Fill="Transparent"/>
</ControlTemplate>
<ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<s:MoveThumb Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
</Grid>
</ControlTemplate>
</Canvas.Resources>
<ContentControl Name="DesignerItem"
Width="100"
Height="100"
Canvas.Top="100"
Canvas.Left="100"
Template="{StaticResource DesignerItemTemplate}">
<Ellipse Fill="Blue" IsHitTestVisible="False"/>
</ContentControl>
</Canvas>
</Grid>
</Window>
Problem is, I am explicitly stating the width and height of the canvas inside that MoveThumb class, which means that if my window changes size, and the Canvas changes size, the drag boundaries will remain the same. Ideally, I want to bind canvasWidth and canvasHeight to the actualWidth and actualHeight of the Canvas.
I am not sure what is the best way to achieve it. Acquiring the actualWidth and actualHeight values inside the MoveThumb_DragDelta function with something like actualWidth = mainWindow.Canvas1.ActualWidth would be quick and simple, but very bad coding practice.
Ideally, I imagine it would be best to pass the limits as arguments to the constructor of MoveThumb and stored as global field/property, but I don't see a way to do it, since this class is used as a template in the XML code, and not generated from code-behind. I am not exactly sure if that would work at all, because the MoveThumb might be instantized only once (during the creation of the control), so it won't work when the Canvas changes it's size afterwards.
So I probably should do some kind of one-way binding between the actualWidth of Canvas1 and canvasWidth (declared as global property) of MoveThumb. But again, I have no idea how to access it, since MoveThumb is used as a TargetType of ControlTemplate inside Canvas.Resources.
I am still pretty new to WPF, and it feels like there should be some very simple way to achieve this, but I'm not seeing it. Can anyone help?
Example using Dependency Properties:
public partial class MoveThumb : Thumb
{
private double privateCanvasWidth = double.NaN, privateCanvasHeight = double.NaN;
private static readonly Binding bindingActualWidth = new Binding()
{
Path = new PropertyPath(ActualWidthProperty),
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Canvas), 1)
};
private static readonly Binding bindingActualHeight = new Binding()
{
Path = new PropertyPath(ActualHeightProperty),
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Canvas), 1)
};
public MoveThumb()
{
DragDelta += MoveThumb_DragDelta;
SetBinding(CanvasWidthProperty, bindingActualWidth);
SetBinding(CanvasHeightProperty, bindingActualHeight);
}
static MoveThumb()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MoveThumb), new FrameworkPropertyMetadata(typeof(MoveThumb)));
}
private static void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
MoveThumb thumb = (MoveThumb)sender;
//FrameworkElement item = thumb.MovableContent;
//if (item == null)
//{
// return;
//}
double left = Canvas.GetLeft(thumb);
double top = Canvas.GetTop(thumb);
double right = left + thumb.ActualWidth;
double bottom = top + thumb.ActualHeight;
double canvasWidth = thumb.privateCanvasWidth;
if (double.IsNaN(canvasWidth))
canvasWidth = 450;
double canvasHeight = thumb.privateCanvasHeight;
if (double.IsNaN(canvasHeight))
canvasWidth = 800;
left += e.HorizontalChange;
top += e.VerticalChange;
right += e.HorizontalChange;
bottom += e.VerticalChange;
if (left > 0 &&
top > 0 &&
right < canvasWidth &&
bottom < canvasHeight)
{
Canvas.SetLeft(thumb, left);
Canvas.SetTop(thumb, top);
}
}
}
// DependecyProperties
[ContentProperty(nameof(MovableContent))]
public partial class MoveThumb
{
/// <summary>Canvas Width.</summary>
public double CanvasWidth
{
get => (double)GetValue(CanvasWidthProperty);
set => SetValue(CanvasWidthProperty, value);
}
/// <summary><see cref="DependencyProperty"/> for property <see cref="CanvasWidth"/>.</summary>
public static readonly DependencyProperty CanvasWidthProperty =
DependencyProperty.Register(nameof(CanvasWidth), typeof(double), typeof(MoveThumb), new PropertyMetadata(double.NaN, CanvasSizeChanged));
/// <summary>Canvas Height.</summary>
public double CanvasHeight
{
get => (double)GetValue(CanvasHeightProperty);
set => SetValue(CanvasHeightProperty, value);
}
/// <summary><see cref="DependencyProperty"/> for property <see cref="CanvasHeight"/>.</summary>
public static readonly DependencyProperty CanvasHeightProperty =
DependencyProperty.Register(nameof(CanvasHeight), typeof(double), typeof(MoveThumb), new PropertyMetadata(double.NaN, CanvasSizeChanged));
// Property change handler.
// The code is shown as an example.
private static void CanvasSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MoveThumb thumb = (MoveThumb)d;
if (e.Property == CanvasWidthProperty)
{
thumb.privateCanvasWidth = (double)e.NewValue;
}
else if (e.Property == CanvasHeightProperty)
{
thumb.privateCanvasHeight = (double)e.NewValue;
}
else
{
throw new Exception("God knows what happened!");
}
MoveThumb_DragDelta(thumb, new DragDeltaEventArgs(0, 0));
}
/// <summary>Movable content.</summary>
public FrameworkElement MovableContent
{
get => (FrameworkElement)GetValue(MovableContentProperty);
set => SetValue(MovableContentProperty, value);
}
/// <summary><see cref="DependencyProperty"/> for property <see cref="MovableContent"/>.</summary>
public static readonly DependencyProperty MovableContentProperty =
DependencyProperty.Register(nameof(MovableContent), typeof(FrameworkElement), typeof(MoveThumb), new PropertyMetadata(null));
}
In the Project add the theme "Themes\Generic.xaml":
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls"
xmlns:s="clr-namespace:Febr20y">
<Style TargetType="{x:Type s:MoveThumb}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type s:MoveThumb}">
<ContentPresenter Content="{TemplateBinding MovableContent}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<Grid>
<Canvas x:Name="Canvas1">
<s:MoveThumb x:Name="DesignerItem"
Width="100"
Height="100"
Canvas.Top="100"
Canvas.Left="100">
<Ellipse Fill="Blue"/>
</s:MoveThumb>
</Canvas>
</Grid>
Video YouTube
Source Code

WPF 2D Game: Making a Camera that follows an object on a Canvas

I am trying to make a simple 2D Game in WPF, and I've come across a problem I can't solve.
Let's say I have a player on a 700x700 Canvas, but my MainWindow's Width and Height are set to 400.
I want to be able to have a camera like feature, that follows a rectangle object on the canvas (this object symbolises the player) and shows the corresponding portion of the canvas whenever the player moves.
In theory how could I implement a feature like this?
Here's a rough sample of how to do that with a ScrollViewer. Use the arrow keys to move the player around while keeping it in the view of the "camera". The canvas' background is set to a radial-brush, to see the camera moving.
MainWindow.xaml
<Window x:Class="WpfApp6.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"
Background="#222"
Loaded="Window_Loaded"
SizeToContent="WidthAndHeight"
PreviewKeyDown="Window_PreviewKeyDown">
<Grid>
<ScrollViewer x:Name="CanvasViewer"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden">
<Canvas x:Name="Canvas"
IsHitTestVisible="False">
<Canvas.Background>
<RadialGradientBrush>
<GradientStop Offset="0"
Color="Orange" />
<GradientStop Offset="1"
Color="Blue" />
</RadialGradientBrush>
</Canvas.Background>
</Canvas>
</ScrollViewer>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
double _playerSize;
Rectangle _playerRect;
Vector _playerPosition;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
InitializeSizes();
InitializePlayerRect();
}
#region initialize
private void InitializeSizes()
{
_playerSize = 50;
Canvas.Width = 700;
Canvas.Height = 700;
CanvasViewer.Width = 400;
CanvasViewer.Height = 400;
}
private void InitializePlayerRect()
{
_playerRect = new Rectangle
{
Fill = Brushes.Lime,
Width = _playerSize,
Height = _playerSize,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top
};
Canvas.Children.Add(_playerRect);
}
#endregion
#region move player
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Left: MovePlayerLeft(); break;
case Key.Up: MovePlayerUp(); break;
case Key.Right: MovePlayerRight(); break;
case Key.Down: MovePlayerDown(); break;
}
}
private void MovePlayerLeft()
{
var newX = _playerPosition.X - _playerSize;
_playerPosition.X = Math.Max(0, newX);
UpdatePlayerPositionAndCamera();
}
private void MovePlayerUp()
{
var newY = _playerPosition.Y - _playerSize;
_playerPosition.Y = Math.Max(0, newY);
UpdatePlayerPositionAndCamera();
}
private void MovePlayerRight()
{
var newX = _playerPosition.X + _playerSize;
_playerPosition.X = Math.Min(Canvas.Width - _playerSize, newX);
UpdatePlayerPositionAndCamera();
}
private void MovePlayerDown()
{
var newY = _playerPosition.Y + _playerSize;
_playerPosition.Y = Math.Min(Canvas.Height - _playerSize, newY);
UpdatePlayerPositionAndCamera();
}
#endregion
#region update player and camera
private void UpdatePlayerPositionAndCamera()
{
UpdatePlayerPosition();
UpdateCamera();
}
private void UpdatePlayerPosition()
{
// move the playerRect to it's new position
_playerRect.Margin = new Thickness(_playerPosition.X, _playerPosition.Y, 0, 0);
}
private void UpdateCamera()
{
// calculate offset of scrollViewer, relative to actual position of the player
var offsetX = _playerPosition.X / 2;
var offsetY = _playerPosition.Y / 2;
// move the "camera"
CanvasViewer.ScrollToHorizontalOffset(offsetX);
CanvasViewer.ScrollToVerticalOffset(offsetY);
}
#endregion
}
WPF isn't really the right technology for games, you're much better off using something like MonoGame.
To answer your question, though, you can wrap your canvas in a ScrollViewer:
<ScrollViewer x:Name="theScrollView" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" CanContentScroll="True">
<Canvas x:Name="theCanvas" Width="5000" Height="5000" />
</ScrollViewer>
Then in code you scroll the view to whatever position your camera is at:
theScrollView.ScrollToHorizontalOffset(100);

WPF - how to create click through semi transparent layer

I want something like this for a screen recording software.
My sample wpf window looks like this
<Window x:Class="WpfTestApp.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"
xmlns:local="clr-namespace:WpfTestApp"
mc:Ignorable="d"
ShowInTaskbar="False" WindowStyle="None" ResizeMode="NoResize"
AllowsTransparency="True"
UseLayoutRounding="True"
Opacity="1"
Cursor="ScrollAll"
Topmost="True"
WindowState="Maximized"
>
<Window.Background>
<SolidColorBrush Color="#01ffffff" Opacity="0" />
</Window.Background>
<Grid>
<Canvas x:Name="canvas1">
<Path Fill="#CC000000" Cursor="Cross" x:Name="backgroundPath">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,1440,810"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="300,200,800,300" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
</Grid>
Now the problem is I can't make the semi transparent area backgroundPath click through. I have set its IsHitTestVisible property to false, but still no change. I have used SetWindowLong to make the whole window transparent, and that lets me click through the window, but that then all the events of my window and controls in it don't work.
Can any one suggest how can I achieve that?
I was actually curious about this and it doesn't look like there really is a "proper" or "official" way to achieve transparency on only the window but not the controls.
In lieu of this, I came up with a functionally effective solution:
MainWindow XAML (I just added a button)
<Window x:Class="test.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"
xmlns:local="clr-namespace:test"
mc:Ignorable="d"
Title="MainWindow"
WindowStyle="None"
AllowsTransparency="True"
ShowInTaskbar="False"
ResizeMode="NoResize"
UseLayoutRounding="True"
Opacity="1"
Cursor="ScrollAll"
Topmost="True"
WindowState="Maximized">
<Window.Background>
<SolidColorBrush Color="#01ffffff" Opacity="0" />
</Window.Background>
<Grid>
<Canvas x:Name="canvas1">
<Path Fill="#CC000000" Cursor="Cross" x:Name="backgroundPath">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,1440,810"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="300,200,800,300" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
<Button x:Name="My_Button" Width="100" Height="50" Background="White" IsHitTestVisible="True" HorizontalAlignment="Center" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
</Window>
MainWindow C#
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Threading;
namespace test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
const int WS_EX_TRANSPARENT = 0x00000020;
const int GWL_EXSTYLE = (-20);
public const uint WS_EX_LAYERED = 0x00080000;
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
private bool _isClickThrough = true;
public MainWindow()
{
InitializeComponent();
// List of controls to make clickable. I'm just adding my button.
List<System.Windows.Controls.Control> controls = new List<System.Windows.Controls.Control>();
controls.Add(My_Button);
Thread globalMouseListener = new Thread(() =>
{
while (true)
{
Point p1 = GetMousePosition();
bool mouseInControl = false;
for (int i = 0; i < controls.Count; i++)
{
Point p2 = new Point();
Rect r = new Rect();
System.Windows.Controls.Control iControl = controls[i];
Dispatcher.BeginInvoke(new Action(() =>
{
// Get control position relative to window
p2 = iControl.TransformToAncestor(this).Transform(new Point(0, 0));
// Add window position to get global control position
r.X = p2.X + this.Left;
r.Y = p2.Y + this.Top;
// Set control width/height
r.Width = iControl.Width;
r.Height = iControl.Height;
if (r.Contains(p1))
{
mouseInControl = true;
}
if (mouseInControl && _isClickThrough)
{
_isClickThrough = false;
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExNotTransparent(hwnd);
}
else if (!mouseInControl && !_isClickThrough)
{
_isClickThrough = true;
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExTransparent(hwnd);
}
}));
}
Thread.Sleep(15);
}
});
globalMouseListener.Start();
}
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
public static void SetWindowExTransparent(IntPtr hwnd)
{
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
}
public static void SetWindowExNotTransparent(IntPtr hwnd)
{
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle & ~WS_EX_TRANSPARENT);
}
private void Button_Click(object sender, EventArgs e)
{
System.Windows.Forms.MessageBox.Show("hey it worked");
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExTransparent(hwnd);
}
}
}
Basically If the mouse is over a control, I call SetWindowExNotTransparent to turn it into a normal, non click-through window. If the mouse is not over a control, it switches it back to a click-through state with SetWindowExTransparent.
I have a thread running that continuously checks the global mouse position against global control positions (where you fill a list of controls you want to be able to click). The global control positions are determined by getting the control position relative to MainWindow and then adding the Top and Left attributes of MainWindow.
Sure, this is a somewhat "hacky" solution. But I'll be damned if you find a better one! And it seems to be working fine for me. (Albeit it might get weird to handle oddly shaped controls. This code only handles rectangular controls.)
Also I just threw this together really quick to see if it would work, so it's not very clean. A proof of concept, if you will.
There is no way to have a part of a window both visually semi-transparent and transparent for user interaction (e.g. mouse clicks).
You either have to:
make the whole window transparent for the user interaction (using SetWindowLong, CreateParams etc)
or make the desired window parts fully transparent
A workaround for this is to draw the semi-transparent area manually, without having a window. This is going to be a tough job, and AFAIK there is no reliable method for doing this. The Windows DWM doesn't offer any public API for that, drawing directly on the Desktop's HDC won't do, overlays are not always supported by the graphics hardware, Direct2D won't let you do that either.
You can create two top-most windows and synchronize their size. The first window would only have the controls for resizing and would handle the mouse input, no content inside. The second window would display the semi-transparent grey background with a transparent region inside - just as your current window in your sample - but completely transparent for any mouse interaction.

Borderless window Drop shadow Opacity elements issue

I am trying to create a borderless window in WPF. I am using the class mentioned in the link.
This works fine as long as all elements are all enabled (Opacity = 1). if i set opacity to 0.5, it is almost invisible.
The opacity works fine if i remove the code for the drop shadow. I am not sure what is the cause of this.
The class is the same one i am using as explained in link above. The rest of my code is below.
Any help is appreciated :
Girija
XAML:
<Window x:Class="Sample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Sample">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Margin="2" Text="Something" Grid.Row="0"/>
<Button Grid.Row="1" Content="Something" IsEnabled="False" Opacity="0.7"/>
</Grid>
Code behind :
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
/// <summary>
/// Raises the <see cref="E:System.Windows.FrameworkElement.Initialized"/> event.
/// This method is invoked whenever
/// <see cref="P:System.Windows.FrameworkElement.IsInitialized"/> is set to true internally.
/// </summary>
/// <param name="e">The <see cref="T:System.Windows.RoutedEventArgs"/>
/// that contains the event data.</param>
protected override void OnInitialized(EventArgs e)
{
//AllowsTransparency = false;
ResizeMode = ResizeMode.NoResize;
WindowStartupLocation = WindowStartupLocation.CenterScreen;
WindowStyle = WindowStyle.None;
DwmDropShadow.DropShadowToWindow(this);
base.OnInitialized(e);
}
}
helper Class taken from above link :
class DwmDropShadow
{
[DllImport("dwmapi.dll", PreserveSig = true)]
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
[DllImport("dwmapi.dll")]
private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset);
/// <summary>
/// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven).
/// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect,
/// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window).
/// </summary>
/// <param name="window">Window to which the shadow will be applied</param>
public static void DropShadowToWindow(Window window)
{
if (!DropShadow(window))
{
window.SourceInitialized += new EventHandler(window_SourceInitialized);
}
}
private static void window_SourceInitialized(object sender, EventArgs e)
{
Window window = (Window)sender;
DropShadow(window);
window.SourceInitialized -= new EventHandler(window_SourceInitialized);
}
/// <summary>
/// The actual method that makes API calls to drop the shadow to the window
/// </summary>
/// <param name="window">Window to which the shadow will be applied</param>
/// <returns>True if the method succeeded, false if not</returns>
private static bool DropShadow(Window window)
{
try
{
WindowInteropHelper helper = new WindowInteropHelper(window);
int val = 2;
int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4);
if (ret1 == 0)
{
Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 };
int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m);
return ret2 == 0;
}
else
{
return false;
}
}
catch (Exception ex)
{
// Probably dwmapi.dll not found (incompatible OS)
return false;
}
}
}
I dont know why are you using shadow effect by using dwmapi.dll. Why arent you using the hardware accelerated drop shadow effects from WPF Extended toolkit or from presentationframework.Aero (referred from .NET assemblies in visual studio)?
<ResourceDictionary Source="/presentationframework.Aero;V3.0.0.0;31bf3856ad364e35;component/themes/aero.normalcolor.xaml"/>
<DropShadowEffect ShadowDepth="2"></DropShadowEffect>
Here you go :)
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
AllowsTransparency="True"
Title="MainWindow"
Background="Transparent"
WindowStyle="None"
Height="350"
Width="500">
<Border Background="#ffFEFEFE" CornerRadius="5" BorderThickness="2" Margin="10" BorderBrush="#ffaaaaaa">
<Border.Effect>
<DropShadowEffect Direction="270"
ShadowDepth="5"
Opacity="0.8"
BlurRadius="7" />
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="70" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Margin="2"
Text="Something"
Grid.Row="0" />
<Button Grid.Row="1"
Content="Something"
IsEnabled="False"
Opacity="0.5" />
</Grid>
</Border>
</Window>
Use nonzero value for one of the margins, eg. 1, 0, 0, 0; or 1, 1, 1, 1; and use your own Margins:
[StructLayout(LayoutKind.Sequential)]
public struct Margins
{
public int Left;
public int Right;
public int Top;
public int Bottom;
}

WP7 - Update UserControl through binding

I have a WP7 silverlight project. I have a 'UserControl' to display rectangles based on a DependencyProject.
Now, when I place the control on my page with the following:
<uc:RectangleControl NumRectangles={Binding Path=RectangleCount,Mode=OneWay} />
I get my rectangles displayed based on initial value of 'RectangleCount'. However, when RectangleCount is changed, the User control never updates. I expect this is caused by me drawing my rectangles in the OnLoaded event, but I can't for the life of me find another event to bind to to cause the control to draw new rectangles when the NumRectangles DP is updated.
Any help?
The UserControl XAML:
<UserControl x:Class="WP7Test.Controls.RectangleControl"
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"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="80" d:DesignWidth="480">
<Canvas x:Name="canvas"
Height="{Binding Height}" Width="{Binding Width}">
</Canvas>
</UserControl>
And in the code behind, I add rectangles based on a dependency property:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace WP7Test.Controls
{
public partial class RectangleControl : UserControl
{
private double RectangleSpacing = 1;
#region Dependency Properties
public int NumRectangles
{
get { return (int)GetValue(NumRectanglesProperty); }
set { SetValue(NumRectanglesProperty, value); }
}
// Using a DependencyProperty as the backing store for NumRectangles. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NumRectanglesProperty =
DependencyProperty.Register("NumRectangles", typeof(int), typeof(RectangleControl), new PropertyMetadata(5));
#endregion
public RectangleControl()
{
InitializeComponent();
Loaded += new RoutedEventHandler(OnLoaded);
}
private void OnLoaded(object sender, RoutedEventArgs args)
{
double rectangleWidth = 20;
double rectangleHeight = 20;
double x = 0;
double y = 0;
for (int i = 0; i < NumRectangles; i++)
{
Brush b = new SolidColorBrush(Colors.Red);
Rectangle r = GenerateRectangle(x, y, rectangleWidth, rectangleHeight, b);
canvas.Children.Add(r);
x += rectangleWidth + 1;
}
}
public Rectangle GenerateRectangle(double x, double y, double width, double height, Brush brush)
{
Rectangle r = new Rectangle();
r.Height = height;
r.Width = width;
r.Fill = brush;
Canvas.SetLeft(r, x);
Canvas.SetTop(r, y);
return r;
}
}
}
When you register your dependency property, you need to provide a handler for the 'changed' event within your PropertyMetadata. See the MSDN documentation here:
http://msdn.microsoft.com/en-us/library/ms557330%28VS.95%29.aspx
The method that you supply as a dependency property change handler will be static so you need to use the arguments passed to this method to obtain a reference to your control. From here you can clear and re-build your UI.

Resources