Draw on Desktop from WPF application - wpf

I would draw a rectangle on desktop from my WPF application. I've searched but i didn't find a good solution and a good example.
I would draw a rectangle at the edges of the screen and changes its color when something of specific happen.
How i can i do this in a correct way?
I found this code :
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
class Program {
[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
static extern void ReleaseDC(IntPtr dc);
static void Main(string[] args) {
IntPtr desktop = GetDC(IntPtr.Zero);
using (Graphics g = Graphics.FromHdc(desktop)) {
g.FillRectangle(Brushes.Red, 0, 0, 100, 100);
}
ReleaseDC(desktop);
}
}
But i don't have a console application and i don't understand how i can re-paint / refresh my rectangle or change its color from this code.
thank all.

You can create a full screen WPF Application that has a transparent Window. You can find out how to do that from the Creating Border-less Windows in WPF page on Paul Sherrif's Blog. In short though, to get a borderless window, you need to set the following attributes on your Window:
•WindowStyle="None"
•ShowInTaskbar="False"
•AllowsTransparency="True"
•Background="Transparent"
Then you simply do the drawing in the WPF Application, but it will look like the drawing is on the Desktop.

This code cannot be applied to WPF in any manner. WPF is a retained-mode API, unlike Windows Forms, so you can't draw it like this (actually you can use HWND interop or DrawingVisual). But it shouldn't bother you as you don't need to draw it by yourself, just declare required rectangles and any other stuff using XAML.

Related

Wpf and Hwnd unique name

we have a wpf application that should be 'piloted' by a legacy win32 winform application. (We don't own the code ;) )
The legacy application should pilot our application (minimize, bring to front, shut, etc) via windowsclass name we should provide as a configuration parameter written into an ini file.
The problem is we cannot make it work with wpf since if we insert the classname Spy++ gives us, nothing happens. The point is Spi++ returns something like this
HwndWrapper[MyWpfProgram.exe;;16978ce2-3b8d-4c46-81ee-e1c6d6de4e6d]
where the guid is randomly generated at every run.
Is there any way to solve this issue?
Thank you.
There is no way to do what I asked. But we found a walkaround. "Simply" embedding the xaml windows within a windows form.
These are the steps we followed:
1 - Add a Windows Form to the project.
2 - Remove the app.xaml and make the new form the entry point of the application.
3 - Since we need the hwnd of the main.xaml we added this prop to its code behind
public IntPtr Hwnd
{
get { return new WindowInteropHelper(this).Handle; }
}
4 - then from the constructor of the form we create an instance of the wpf window class
private Main app;
public ContainerForm()
{
InitializeComponent();
app = new Main();
ElementHost.EnableModelessKeyboardInterop(app);
}
we needed
ElementHost.EnableModelessKeyboardInterop(app);
since we want all the keyboard input to pass from the the windows form to the xaml window
5 - now we want to bond the xpf window to the winform. In order to do that we need to use Windows Api and we do it at the OnShow event of the form (the reason why will be explicated later).
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetFocus(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll", EntryPoint = "SetWindowLongA", SetLastError = true)]
private static extern long SetWindowLong(IntPtr hwnd, int nIndex, long dwNewLong);
private const int GWL_STYLE = (-16);
private const int WS_VISIBLE = 0x10000000;
private void ContainerForm_Shown(object sender, EventArgs e)
{
app.Show();
SetParent(app.Hwnd, this.Handle);
SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);
MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);
SetFocus(app.Hwnd);
}
with
SetParent(app.Hwnd, this.Handle);
wo do the magic, then with
SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);
we remove al the chrome from the wpf window (there is a border even if the window is defined borderless, don't ask me why)
then we make the wpf window fill all the client area of the winform
MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);
and then we focus the wpf window
SetFocus(app.Hwnd);
that's the reason why we do everything in the show event. Since if we do it at form's constructor, then the wpf window will loose its focus since the in winform the main window got the focus from the operating system.
We didn't understand why we needed to add the other api calls at this point, but if we left them at constructor's the trick didn't work.
Anyway,
problem solved ;)
Use HwndSource.
You can use native Windows API calls to create a window with the expected classname, then use HwndSource to add WPF content to it:
var source = HwndSource.FromHwnd(nativeWindowHandle);
source.RootVisual = mainGrid;
If you need to use a WPF window, I think you could still solve this with a "proxy" window, but it wouldn't be pretty:
Have your WPF application spawn a native message-only window.
Use HwndSource.AddHook to handle messages like WM_CLOSE, WM_SIZE on the native window and pass them along to the "real" WPF window.
For the window handles, titles, and class names, Spy++ uses fairly simple Windows APIs.
FindWindowEx http://msdn.microsoft.com/en-us/library/windows/desktop/ms633500%28v=vs.85%29.aspx
EnumWindows http://msdn.microsoft.com/en-us/library/windows/desktop/ms633497%28v=vs.85%29.aspx
GetClassName http://msdn.microsoft.com/en-us/library/windows/desktop/ms633582%28v=vs.85%29.aspx
You can create a "loader" program that will...
Start the wpf app
Use the above APIs to get the proper class names and windows handles
Edit the legacy INI
Start the legacy app

Bitmap class in WPF

I'm working with Emgu Cv in Winforms to do face recognition using Kinect. Now, i want to move to WPF. However, the EmguCv library support only Bitmap class.
Can i use the Bitmap class (used in Winforms) in WPF ? if not, is there an other method to use Emgu cv with kinect in WPF?
Thanks.
System.Drawing.Bitmap can not be used directly as image source for WPF, so you have to convert it to System.Windows.Media.Imaging.BitmapSource.
The best way to do it is by using Imaging.CreateBitmapSourceFromHBitmap.
You can use an extension method:
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
public static BitmapSource ToBitmapSource(this System.Drawing.Bitmap source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
IntPtr ip = source.GetHbitmap();
try
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ip,
IntPtr.Zero, Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(ip);
}
}
Please note that you must invoke DeleteObject, because Bitmap.GetHbitmap() leaks a GDI handle (see this answer).
Once you have a BitmapSource, you can display it using an Image control and by setting the Source property.
You can read more about WPF imaging in this article: Imaging Overview

Get all opened desktop applications using WPF

I am trying to create a Visual Tree Explorer application (like Snoop, Mole) which should automatically identify all desktop applications and detect whether it is a WPF app. or not. I am tring to do this in WPF. Is there any way to get all WPF opened applications. Any special API, or native functions in system dlls, etc.
Regards,
Jawahar
I think you can iterate through the list of all processes and for those processes who have a window, check if the window class name starts with HwndWrapper (I've noticed the WPF windows have a class name like this: HwndWrapper[DefaultDomain;;2e60e21a-8752-4daf-820b-aed289668930])
The code should be something like this:
Process[] procs = Process.GetProcesses();
foreach(Process p in procs)
{
if (p.MainWindowHandle != null)
{
Console.WriteLine(p.MainWindowTitle);
StringBuilder sb = new StringBuilder(257);
RealGetWindowClass(p.MainWindowHandle, sb, 256);
Console.WriteLine(sb.ToString());
if(sb.ToString().StartsWith("HwndWrapper"))
{
Console.WriteLine("WPF window");
}
}
}
[DllImport("user32.dll")]
static extern uint RealGetWindowClass(IntPtr hwnd, [Out] StringBuilder pszType, uint cchType);
with maybe some adjustments depending on your case. When dealing with such a window, one should assume it's a WPF window not take it for a certainty, so error checking must be extensive.

WPF HwndHost keyboard focus

The chart area in the screenshot is a HwndHost control which hosts a native Win32 window (with it's own registered WNDCLASS) implemented in C++/CLI and drawn with Direct2D. The HwndHost is hosted in a WPF Border control.
The problem I have is that I can't set the keyboard focus to the hosted Win32 window. I want the focus to move to the hosted Win32 window when the used clicks on the chart area. I tried calling SetFocus on WM_LBUTTONDOWN, but that screws up the focus in the rest of the application.
Currently, even if I click on the Win32 window, the focus remains on the tree-view on the left, and if I press the up/down cursor keys, the tree-view will get them, not the chart window.
How do I make the hosted Win32 window receive keyboard input from when the user clicks on the chart area, until it clicks on another control (like the tree-view, or the toolbar)?
alt text http://dl.dropbox.com/u/190212/public/wpf_hwndhost.png
EDIT: Here's the C++/CLI code for the window host:
template <typename T>
inline T intPtrToPtr(IntPtr value)
{
return reinterpret_cast<T>(static_cast<void*>(value));
}
public ref class ChartWindowHost : public HwndHost, IKeyboardInputSink
{
private:
ChartWindow* chartWindow; // this is a C++ class doing the actual work
protected:
virtual HandleRef BuildWindowCore(HandleRef parent) override
{
chartWindow = new ChartWindow;
const HINSTANCE hInstance = intPtrToPtr<HINSTANCE>(Marshal::GetHINSTANCE(Assembly::GetExecutingAssembly()->GetModules()[0]));
const HWND parentWindow = intPtrToPtr<HWND>(parent.Handle);
chartWindow->Create(hInstance, parentWindow);
return HandleRef(this, IntPtr(chartWindow->GetHandle()));
}
virtual void DestroyWindowCore(HandleRef /*window*/) override
{
chartWindow->Destroy();
delete chartWindow;
chartWindow = NULL;
}
};
Well, msdn says something about having to override WndProc() on the HwndHost subclass...

How can one host Flash content in a WPF application and use transparency?

How can I go about hosting flash content inside a WPF form and still use transparency/alpha on my WPF window? Hosting a WinForms flash controls does not allow this.
Unless the control you use to display the Flash content is built in WPF, you will run in to these "airspace" issues. Every display technology from Win32 to WinForms used HWNDs "under the hood", but WPF uses DirectX. The Window Manager in Windows however, still only understands HWNDs, so WPF apps have one top-level HWND-based window, and everything under that is done in DirectX (actually things like context menus and tooltips also have top-level HWNDs as well). Adam Nathan has a very good description of WPF interop in this article.
Although I haven't done it, you can probably use the WebBrowser control found in WPF 3.5 sp1 to wrap your Flash content within WPF. I'm not sure how the transparency will be affected though.
Can you use Expression to convert the flash content to XAML? I believe that there are tools in there or off to the side that do this.
Just have been struggling with same problem of how to upload & Make WPF transparent with ability of displaying Flash, because if you enable on your MainWindow "Allow transparency" Flash will not show once the application will run.
1) I used WebBrowser Control to play Flash(.swf) files. They are on my PC, however it can play from internet or wherever you have hosted them. Don't forget to name your WebBrowser Control to get to it in C#.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyHelper.ExtendFrame(this, new Thickness(-1));
this.MyBrowser.Navigate(#"C:\Happy\Download\flash\PlayWithMEGame.swf");
}
2) Now for transparency. I have set in WPF 'false' to "Allow Transparency" and set "Window Style" to 'None'. After that I have used information from HERE and HERE and created a following code that produced desired effect of allowing transparency on MainWindow and running Flash at same time, here is my code:
public class MyHelper
{
public static bool ExtendFrame(Window window, Thickness margin)
{
IntPtr hwnd = new WindowInteropHelper(window).Handle;
window.Background = Brushes.Transparent;
HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = Colors.Transparent;
MARGINS margins = new MARGINS(margin);
DwmExtendFrameIntoClientArea(hwnd, ref margins);
return true;
}
[DllImport("dwmapi.dll", PreserveSig = false)]
static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
}
struct MARGINS
{
public MARGINS(Thickness t)
{
Left = (int)t.Left;
Right = (int)t.Right;
Top = (int)t.Top;
Bottom = (int)t.Bottom;
}
public int Left;
public int Right;
public int Top;
public int Bottom;
}
And called it from Window_Loaded() + you need 'below' line for 'DllImport' to work.
using System.Runtime.InteropServices;
using System.Windows.Interop;

Resources