Making a WPF window click-through, but not its controls - c#

First of all I want to define what I mean by transparent to clicks in the text bellow: It means the clicks go through my window without any processing, directly to whichever window is bellow it. I need this behaviour because this application is meant to be an overlay over a game.
I searched the questions relating to making a window transparent to clicks. I was wondering if there was a way to make the window itself to be transparent to clicks, but not its controls (such as text boxes and buttons).
Here is the markup for the window:
<Window x:Class="QuestBrowser.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Quest Browser"
Height="350" Width="525"
AllowsTransparency="True" WindowStyle="None" Topmost="True">
<Window.Background>
<SolidColorBrush Color="White" Opacity="0.1"/>
</Window.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Name="inputTxtBox"></TextBox>
</Grid>
</Window>
And bellow is the code that makes the window transparent to clicks (pretty much copy-pasted from other question, because I'm not good at all at low level Windows programming).
namespace Win32Utils
{
public static class WindowHelper
{
const int WS_EX_TRANSPARENT = 0x00000020;
const int GWL_EXSTYLE = (-20);
public static void SetWindowExTransparent(IntPtr hwnd)
{
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
}
}
}
The method WindowHelper.SetWindowExTransparent is called from within my override of the method Window.OnSourceInitialized (as instructed in the post where I got the code for a transparent window from):
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var hwnd = new WindowInteropHelper(this).Handle;
WindowHelper.SetWindowExTransparent(hwnd);
}
This does make the window transparent to clicks, but all controls I put into it are transparent as well (for example, the TextBox). Is there a way to make the Window surface transparent to clicks, but ensure the TextBox still receives mouse and keyboard input normally?
I wanted to avoid mouse and keyboard hooks for both user privacy reasons and because, as I said, I don't understand much about the Windows API. If hooks are the only way, I would really appreciate a "For Dummies"-style explanation on how they work.

Can you try this
<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"
AllowsTransparency="True"
WindowStyle="None" >
<Window.Background>
<SolidColorBrush Color="#FFB0B0B0"
Opacity="0.05" />
</Window.Background>
<Grid>
<Button Content="Button"
HorizontalAlignment="Left"
Margin="39,125,0,0"
VerticalAlignment="Top"
Width="75" />
<Label Content="Label"
HorizontalAlignment="Left"
Margin="114,50,0,0"
VerticalAlignment="Top" />
<TextBox HorizontalAlignment="Left"
Height="23"
Margin="101,201,0,0"
TextWrapping="Wrap"
Text="TextBox"
VerticalAlignment="Top"
Width="120" />
</Grid>
</Window>
What It basically does Is create a transparent window.(Click through and Invisble). But controls are visible and not-click through.
Or you can try How to create a semi transparent window in WPF that allows mouse events to pass through

Related

How to correctly locate a control in a WPF window

Consider the following code-behind for displaying a child window after clicking a button in the Main Window. The desired result is to have the child window placement show up just to the right of the menu button, in-line with the top of the menu button. (So the window shows right next to the control that activated it.)
private void btnMenu2_Click(object sender, RoutedEventArgs e)
{
var menu2 = new Menu2Window();
//var winLocation = this.btnMenu2.TranslatePoint(new Point(0, 0), Application.Current.MainWindow);
//var winLocation = this.btnMenu2.TranslatePoint(new Point(0, 0), this.spLeftMenu);
//var winLocation = this.spLeftMenu.TranslatePoint(new Point(0, 0), this.btnMenu2);
var objLocation = this.spLeftMenu.TranslatePoint(new Point(0, 0), this.spLeftMenu);
var scnLocation = this.btnMenu2.PointToScreen(objLocation);
//menu2.Left = scnLocation.X + btnMenu2.Width;
menu2.Left = scnLocation.X + 50; // <- Why does this work but using btnMenu2.Width causes placement to be all over the place???
menu2.Top = scnLocation.Y;
menu2.ShowDialog();
}
The code as presented does work in the fashion needed, however I don't like using hard-coded values or magic numbers in code.
If you comment out the line with the hard-coded control width value (50) and un-comment the line using the button's width property, subsequent execution results in the menu window displaying in a sequence of locations that defy logic. It appears that it is getting a value from a random number generator rather than the control's width. I do see a pattern when its run several times, but getting 4 or 5 different locations because of a variation in property value responses each time the code is executed is quite frustrating.
What would be the RIGHT or correct approach here? How do I get a reliable value back from a WPF control property or am I asking too much?
XAML of Main Window:
<Window x:Class="LocateTest.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:LocateTest"
mc:Ignorable="d"
Background="DarkGray"
Title="MainWindow" Height="400" Width="750">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="spLeftMenu"
DataContext="MainWindow"
Orientation="Vertical"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Height="325" Width="50">
<Button Content="B1" Height="50" Width="50"/>
<Button x:Name="btnMenu2"
Content="B2"
Height="50"
Click="btnMenu2_Click"/>
<Button Content="B3" Height="50"/>
</StackPanel>
</Grid>
</Window>
XAML of Menu2 window:
<Window x:Class="LocateTest.Menu2Window"
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:LocateTest"
mc:Ignorable="d"
WindowStyle="None"
AllowsTransparency="True"
WindowStartupLocation="Manual"
Height="150" Width="280">
<Window.Background>
<SolidColorBrush Opacity="0.5" Color="Black"></SolidColorBrush>
</Window.Background>
<Grid>
<Button x:Name="btnClose"
Click="btnClose_Click"
Width="25"
Height="25"
Content="X"
Background="Black"
Foreground="Red" Margin="245,10,10,115">
</Button>
</Grid>
</Window>```
Since you don't explicitly set the width of btnMenu2, the value of its Width property will be double.NaN. Note that the Width value is not the actual width, but the requested width. Use the ActualWidth proeprty instead:
menu2.Left = scnLocation.X + btnMenu2.ActualWidth;
What about using Popup instead? You can put all you want on a Popup control instead of a window and set its StaysOpen property to true to make the user close it automatically through close button or whatever.

Making an application with semi-transparent window and "transparent input"

I want to make an application with transparent window. I want to put one picture at the background of this window and make window transparent for inputs (i.e. if I click at window, it will be same as if I click there at screen if there is no this application). I want to make input box at application startup for input alpha. Is it possible to make an application like this one in c#? If yes, what is the best way to do this?
You can create a transparent window by the below XAML code in a WPF application. You can vary the opacity values between 0 and 1 to get the desired transparency. To get full transparency use 0.
<Window x:Class="WpfApplication3.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"
AllowsTransparency="True" WindowStyle="None" IsActive="False">
<Window.Background>
<SolidColorBrush Opacity="0.5" Color="White"/>
</Window.Background>
<Grid>
<TextBox Width="200" Height="50"/>
</Grid>
</Window>

WPF controls not displaying over transparent opaque window

I am trying to make a transparent window and on this window I am trying to display small web browser controls that would be nontransparent. I can't get it to work, the web browser control is loading and you can even click the links in the control, but it is not visible... any help is appreciated
<Window ...
Title="HelpView" Height="300" Width="300" WindowStyle="None" AllowsTransparency="True" >
<Window.Background>
<SolidColorBrush Opacity="0.6" Color="Black"/>
</Window.Background>
<Canvas Name="HelpCanvas">
<StackPanel>
<WebBrowser Width="150" Height="150" Source="http://www.msn.com"></WebBrowser>
</StackPanel>
</Canvas>
</Window>

FocusedElement is not being honoured

I have created a basic application using Prism & MVVM. So far, it only consists of the Shell, and one View/ViewModel.
During application load, I am loading the View into my main region and this displays on screen. This works, but I cannot get the textbox on the view to focus. It looks like the cursor is in the box (although it's not flashing), but it doesn't accept text input until I click on the textbox.
I've recreated this in a new project, where all I've done is install prism/prism.unityextensions, set up the shell and the view, and loaded the view into the shell region. Neither xaml file has anything in the code behind.
Shell
<Window x:Class="MVVMFocusTest.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://www.codeplex.com/prism"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DockPanel LastChildFill="True">
<ContentControl Name="MainRegion" DockPanel.Dock="Top" prism:RegionManager.RegionName="MainRegion" />
</DockPanel>
</Grid>
</Window>
View1
<UserControl x:Class="MVVMFocusTest.View1"
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="300" d:DesignWidth="300">
<StackPanel>
<Grid FocusManager.FocusedElement="{Binding ElementName=Username}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0">Username</Label>
<TextBox Name="Username" Grid.Row="0" Grid.Column="1" ToolTip="Enter Username" TabIndex="0" />
<Label Grid.Row="1" Grid.Column="0">Password</Label>
<PasswordBox Grid.Row="1" Grid.Column="1" Name="LoginPassword" PasswordChar="*" ToolTip="Enter Password" TabIndex="1" />
</Grid>
</StackPanel>
</UserControl>
Can anyone point out what I'm doing wrong? As far as I'm aware, the FocusManager.FocusedElement="{Binding ElementName=Username}" should be sufficient to set the focus.
As per FocusManager documentation -
Logical focus pertains to the FocusManager.FocusedElement within a
specific focus scope.
So, its not necessary that element with logical focus will have keyboard focus as well but vice versa is true i.e. element with keyboard focus will surely have a logical focus as well.
As stated in documentation FocusManager.FocusedElement guarantees logical focus and not keyboard focus. So what you can do is create an attach behaviour similar to FocusManager.FocusedElement which will set keyboard focus on an element.
You can refer to this for setting keyboard focus using attached behaviour - Setting keyboard focus in WPF.
Code from that article -
namespace Invoices.Client.Wpf.Behaviors
{
using System.Windows;
using System.Windows.Input;
public static class KeyboardFocus
{
public static readonly DependencyProperty OnProperty;
public static void SetOn(UIElement element, FrameworkElement value)
{
element.SetValue(OnProperty, value);
}
public static FrameworkElement GetOn(UIElement element)
{
return (FrameworkElement)element.GetValue(OnProperty);
}
static KeyboardFocus()
{
OnProperty = DependencyProperty.RegisterAttached("On", typeof(FrameworkElement), typeof(KeyboardFocus), new PropertyMetadata(OnSetCallback));
}
private static void OnSetCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var frameworkElement = (FrameworkElement)dependencyObject;
var target = GetOn(frameworkElement);
if (target == null)
return;
frameworkElement.Loaded += (s, e) => Keyboard.Focus(target);
}
}
}
Use in XAML -
<UserControl xmlns:behaviors="clr-namespace:Invoices.Client.Wpf.Behaviors">
<Grid behaviors:KeyboardFocus.On="{Binding ElementName=TextBoxToFocus}">
<TextBox x:Name="TextBoxToFocus" />
</Grid>
</UserControl>
FocusManager.FocusedElement="{Binding ElementName=Username}" sets logical focus but not physical focus.
Physical focus is the normal focus, logical focus is kinda a second focus which is still a little bit buggy in wpf 4.0.
I would suggest you to use Keyboard.Focus(this.Username).

Layout two controls on a line in a stretchable WPF window

Here's a fairly common UI pattern: Text box that contains a path to the left, and a Browse button to the right of it. If the window resizes, the button stays to the right, but the text box stretches to reveal more/less of the path. So in the good old days of anchors, the button would be anchored to the right and the text box would be anchored both left and right.
Trying to replicate this in WPF seems to be worryingly difficult.
If I create a new window, it comes with a Grid layout by default. I place my text box to the left and size it appropriately, then place the button to its right. HorizontalAlignment for the text box is Stretch and for the button it is Right.
In my mind, this works as described, but in real life the text box doesn't resize at all, but instead tries to center itself in the window, while the button acts as expected. What gives?
Here is my XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="241" d:DesignWidth="414" SizeToContent="WidthAndHeight">
<Grid>
<TextBox Height="23" HorizontalAlignment="Stretch" Name="textBox1" VerticalAlignment="Top" Margin="12,11,101,0" />
<Button Content="Button" Height="23" HorizontalAlignment="Right" Margin="0,11,12,0" Name="button1" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
You would create another GridControl with two columns, one with a fixed width for Browse button and another one for the TextBox.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" />
<Button Grid.Column="1" />
</Grid>
You can use a DockPanel and set LastChildFill property to true to ensure that the textbox uses all the available space:
<DockPanel LastChildFill="true">
<Button DockPanel.Dock="Right">Browse</Button>
<TextBox>Content</TextBox>
</DockPanel>

Categories

Resources