How do you create the transparency effects that you see in windows 10? Something like this:
I have no clue how to approach this in c#. Logically thinking I would take a snapshot of the desktop every time the form comes into focus. Then blur it and place it at 0, 0(screen to client coordinates). That doesn't seem very effective. Any help? Again. not an experienced C# programmer, so a detailed explanation would be much appreciated
Edit: I saw some the answers referring me to a page for alpha blending. This is not what I am looking for. I wanted to know how to create the blur that you see in the image, the rest I can figure out at my own pace
Though all of the comments and the answers say it is not possible for WinForms, it definitely works also for WinForms (as SetWindowCompositionAttribute can be called on a Win32 window handle):
internal enum AccentState
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_INVALID_STATE = 4
}
internal enum WindowCompositionAttribute
{
WCA_ACCENT_POLICY = 19
}
[StructLayout(LayoutKind.Sequential)]
internal struct AccentPolicy
{
public AccentState AccentState;
public int AccentFlags;
public int GradientColor;
public int AnimationId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct WindowCompositionAttributeData
{
public WindowCompositionAttribute Attribute;
public IntPtr Data;
public int SizeOfData;
}
internal static class User32
{
[DllImport("user32.dll")]
internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
}
And then in your Form constructor:
public Form1()
{
InitializeComponent();
BackColor = Color.Black; // looks really bad with the default back color
var accent = new AccentPolicy { AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND };
var accentStructSize = Marshal.SizeOf(accent);
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var data = new WindowCompositionAttributeData
{
Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
SizeOfData = accentStructSize,
Data = accentPtr
}
User32.SetWindowCompositionAttribute(Handle, ref data);
Marshal.FreeHGlobal(accentPtr);
}
Result:
Windows Forms doesn't support AcrylicBrush so far. Only UWP support this.
But you have a Win32 API SetWindowCompositionAttribute to simulate this behavior.
The SetWindowCompositionAttribute API
By calling the Windows internal API SetWindowCompositionAttribute, you can get a lightly blurred transparent Window but this transparency is much less than the AcyclicBrush.
How to implement it
Calling SetWindowCompositionAttribute API is not very easy, so I've written a wrapper class for easier usage. But it's for WPF only.
I've written two posts talking about this:
https://walterlv.github.io/post/win10/2017/10/02/wpf-transparent-blur-in-windows-10.html (not in English)
3 Ways to create a window with blurring background on Windows 10 - walterlv
Other options
It's recommended to use AcrylicBrush using UWP and you can read Microsoft's documents Acrylic material - UWP app developer | Microsoft Docs for more details about it.
You can get that by using some algorithms
I don't know will it work or not. When I see your post I just think and got this concept.
Get entire windows desktop wallpaper image by using copyscreen method
Draw that image into a new bitmap where bitmap width = screen resolution.width and bitmap height = screen resolution.height eg: Bitmap bm = new Bitmap (1920, 1080)
Learn how to blur a bitmap image in c#. There is many of blogs teaching how to blur a bitmap programmatically.
Make blur of captured desktop wallpaper image
Put a picture of inside the form and draw the blurred bitmap into picturebox
You can make FormBorderStyle.None to get rid of old borders
You should keep picturebox picture style = normal in order to get full blurred image
Related
I'm working in a company where the owner wish take a screenshot of a windows mail app; specifically the section where the e-mail are show; and if the window or the section have an scroll, then must avoid it and take the screenshot of that whole section.
I'm building it on a .net console app, and I have download a lot of examples where shows just how to take a screeshot of an specific or any window.
The closest code (I think) that I found was this:
IntPtr current = IntPtr.Zero;
IntPtr handle = IntPtr.Zero;
List<IntPtr> thumbs = new List<IntPtr>();
if (handle == IntPtr.Zero)
handle = ((System.Windows.Interop.HwndSource)System.Windows.Interop.HwndSource.FromVisual(this)).Handle;
current = DWM.GetWindow(handle, DWM.GetWindowCmd.First);
do
{
int GWL_STYLE = -16;
int TASKSTYLE = 0x10000000 | 0x00800000;
if (TASKSTYLE == (TASKSTYLE & DWM.GetWindowLong(current, GWL_STYLE)))
{
thumbs.Add(current);
}
current = DWM.GetWindow(current, DWM.GetWindowCmd.Next);
if (current == handle)
current = DWM.GetWindow(current, DWM.GetWindowCmd.Next);
}
while (current != IntPtr.Zero);
this.DataContext = thumbs;
What customer expects it's to take an screenshot of a windows mail app but as I said before, the section that shows the e-mail in fact. So, it must looks something like:
Result
I am not sure how to do it from a Console Application, as that is very limited, but you can create a screen capture program from Windows Forms significantly easier.
The course I took from Huw Collingbourne who will teach you how to do it. It is not a small program to type out in this box though.
The program he teaches had a mainform, then would pop up a smaller transparent form that you would maneuver over the area you want to capture. To capture you would press the button which would capture the transparent form with this code:
formRect = new Rectangle(this.Left, this.Top, this.Left + this.Width, this.Top + this.Height);
this.Hide();
mainForm.GrabRect(formRect);
Going back to the main form the program would put the picture on the picturebox for you to view.
public void GrabRect(Rectangle rect)
{
int rectWidth = rect.Width - rect.Left;
int rectHeight = rect.Height - rect.Top;
Bitmap bm = new Bitmap(rectWidth, rectHeight);
Graphics g = Graphics.FromImage(bm);
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, new Size(rectWidth, rectHeight));
this.pb_screengrab.Image = bm;
Clipboard.SetImage(bm);
g.Dispose();
}
Here are the links to his course
https://bitwisecourses.com/p/program-a-screen-capture-tool-in-c-sharp
and his course sold through udemy
https://www.udemy.com/course/program-a-screen-capture-tool-in-c/learn/lecture/15760316#content
bitwisecourses.com is his direct website I believe. Otherwise Udemy will offer nice discounts like $10-$13 for their various classes. Once you bought a bunch and don’t do anything for a few months they will try to charge you full price, but you only need to email them and they will start discounting you again.
I put everything I did here: https://github.com/johnbnstx/HuwBurn_ScreenGrab
I hope this helps.
Using a class derived from HwndHost and some voodoo code:
protected override HandleRef BuildWindowCore(HandleRef hwndHost)
{
CheckGuestValidity();
var guestHwnd = new HandleRef(GuestHwnd, _guestHwnd);
Win32.SetWindowStyle(guestHwnd.Handle, WindowStyles.WS_CHILD | Win32.GetWindowStyle(guestHwnd.Handle));
WindowsFormsHost.EnableWindowsFormsInterop();
System.Windows.Forms.Application.EnableVisualStyles();
Win32.SetParent(guestHwnd.Handle, hwndHost.Handle);
Win32.SetWindowStyle(_guestHwnd, Win32.GetWindowStyle(_guestHwnd) & ~WindowStyles.WS_CAPTION);
return guestHwnd;
}
I have managed to get an otherwise parentless (except for Windows' main window) window from a Delphi COM server to adopt my WPF application window as it's parent window. Problem is it is located under all the other user controls etc. at the very bottom of my WPF window. I would like to size and position it to fit inside a rectangle on a tab in a TabControl. How can I go about this? I realise it will take a few resize and position Win32 API calls and maybe even a few low level Windows messages, but I'm up for it.
First I found a handy little class to convert between coordinate systems. It is #Lu55's answer , not the accepted one, to the question How do I convert a WPF size to physical pixels?
Then, my WindowHostView XAML looks like this:
<Grid x:Name="WindowContainer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Coral" >
<delphi:Win32WindowHost x:Name="Host" />
</Grid>
The Win32WindowHost class is what contains the code included in my question.
Now I have a neat little routine inside WindowHostView to fit the guest window into the the host's boundarys.
private void PositionGuestWindow(IntPtr hWnd)
{
var rect = WindowContainer.GetAbsoltutePlacement();
var winLeft = Win32Calc.PointsToPixels(rect.Left, Win32Calc.Dimension.Width);
var winTop = Win32Calc.PointsToPixels(rect.Top, Win32Calc.Dimension.Height);
var winWidth = Win32Calc.PointsToPixels(rect.Width, Win32Calc.Dimension.Width);
var winHeight = Win32Calc.PointsToPixels(this.ActualHeight - 20, Win32Calc.Dimension.Height);
Win32Api.MoveWindow(Host.GuestHwnd, (int)winLeft, (int)winTop, (int)winWidth + 1, (int)winHeight, true);
}
And Win32Api.MoveWindow is plain old:
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
I'm trying to make a screen capture manager & recorder, both screenshot & video, although the latter is not important for this question.
The user can select a process he/she wants to be captured and set a hotkey.
The screenshots work fine for the regular desktop and some games. However, when trying to capture a screenshot from some games (e.g Splinter Cell Blacklist), if these games are full-screen, and depending on Windows 7 Aero, the contents are black, or the desktop is showing with a small bar at the top left of the screen.
Here's my initial code:
public static Bitmap GetScreen()
{
Bitmap bmpScreenCapture = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
using (Graphics g = Graphics.FromImage(bmpScreenCapture))
{
g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y,
0, 0,
bmpScreenCapture.Size,
CopyPixelOperation.SourceCopy);
}
return bmpScreenCapture;
}
During my search I found a code project article:
http://www.codeproject.com/Articles/274461/Very-fast-screen-capture-using-DirectX-in-Csharp
And slightly modified the code
public Surface CaptureScreen()
{
Surface s = Surface.CreateOffscreenPlain(d, rc.Width, rc.Height, Format.A8R8G8B8, Pool.Scratch);
d.GetFrontBufferData(0, s);
Surface.ToFile(s, #"C:\test\img.jpg", ImageFileFormat.Jpg);
return s;
}
However, also that image comes out black.
Does anyone have any suggestions as to how this is done?
Use this code (which I found here) to disable Aero before taking any screenshots, then re-enable it.
public readonly uint DWM_EC_DISABLECOMPOSITION = 0;
public readonly uint DWM_EC_ENABLECOMPOSITION = 1;
[DllImport("dwmapi.dll", EntryPoint = "DwmEnableComposition")]
protected static extern uint Win32DwmEnableComposition(uint uCompositionAction);
public void ManageAero(bool a)
{
if (a)
Win32DwmEnableComposition(DWM_EC_ENABLECOMPOSITION );
if (!a)
Win32DwmEnableComposition(DWM_EC_DISABLECOMPOSITIO N);
}
I am trying to render text on a bitmap in a Windows Phone 7 application.
Code that looks more or less like the following would work fine when it's running on the main thread:
public ImageSource RenderText(string text, double x, double y)
{
var canvas = new Canvas();
var textBlock = new TextBlock { Text = text };
canvas.Children.Add(textBloxk);
Canvas.SetLeft(textBlock, x);
Canvas.SetTop(textBlock, y);
var bitmap = new WriteableBitmap(400, 400);
bitmap.Render(canvas, null);
bitmap.Invalidate();
return bitmap;
}
Now, since I have to render several images with more complex stuff, I would like to render the bitmap on a background thread to avoid an unresponsive UI.
When I use a BackgroundWorker to do so, the constructor for TextBlock throws an UnauthorizedAccessException claiming that this is an invalid cross-thread access.
My question is: how can I render text on a bitmap without blocking the UI?
Please don't suggest using a web service to do the rendering. I need to render a large number of images and the bandwidth cost is not acceptable for my needs, and the ability to work offline is a major requirement.
The solution doesn't necessarily has to use WriteableBitmap or UIElements, if there is another way to render text.
EDIT
Another thought: does anyone know if it should be possible to run a UI message loop in another thread, and then have that thread do the work? (instead of using a BackgroundWorker)?
EDIT 2
To consider alternatives to WriteableBitmap, the features I need are:
Draw a background image.
Measure the width and height of a 1-line string, given a font familiy and size (and preferably style). No need for word wrapping.
Draw a 1-line string, with given font family, size, style, at a given coordinate.
Text rendering should support a transparent background. I.e. you should see the background image between the characters.
This method copies the letters from an pre-made image instead of using TextBlock, it's based on my answer to this question. The main limitation is requiring a different image for each font and size needed. A size 20 Font needed about 150kb.
Using SpriteFont2 export the font and the xml metrics file in the sizes you require. The code assumes they're named "FontName FontSize".png and "FontName FontSize".xml add them to your project and set the build action to content. The code also requires WriteableBitmapEx.
public static class BitmapFont
{
private class FontInfo
{
public FontInfo(WriteableBitmap image, Dictionary<char, Rect> metrics, int size)
{
this.Image = image;
this.Metrics = metrics;
this.Size = size;
}
public WriteableBitmap Image { get; private set; }
public Dictionary<char, Rect> Metrics { get; private set; }
public int Size { get; private set; }
}
private static Dictionary<string, List<FontInfo>> fonts = new Dictionary<string, List<FontInfo>>();
public static void RegisterFont(string name,params int[] sizes)
{
foreach (var size in sizes)
{
string fontFile = name + " " + size + ".png";
string fontMetricsFile = name + " " + size + ".xml";
BitmapImage image = new BitmapImage();
image.SetSource(App.GetResourceStream(new Uri(fontFile, UriKind.Relative)).Stream);
var metrics = XDocument.Load(fontMetricsFile);
var dict = (from c in metrics.Root.Elements()
let key = (char) ((int) c.Attribute("key"))
let rect = new Rect((int) c.Element("x"), (int) c.Element("y"), (int) c.Element("width"), (int) c.Element("height"))
select new {Char = key, Metrics = rect}).ToDictionary(x => x.Char, x => x.Metrics);
var fontInfo = new FontInfo(new WriteableBitmap(image), dict, size);
if(fonts.ContainsKey(name))
fonts[name].Add(fontInfo);
else
fonts.Add(name, new List<FontInfo> {fontInfo});
}
}
private static FontInfo GetNearestFont(string fontName,int size)
{
return fonts[fontName].OrderBy(x => Math.Abs(x.Size - size)).First();
}
public static Size MeasureString(string text,string fontName,int size)
{
var font = GetNearestFont(fontName, size);
double scale = (double) size / font.Size;
var letters = text.Select(x => font.Metrics[x]).ToArray();
return new Size(letters.Sum(x => x.Width * scale),letters.Max(x => x.Height * scale));
}
public static void DrawString(this WriteableBitmap bmp,string text,int x,int y, string fontName,int size,Color color)
{
var font = GetNearestFont(fontName, size);
var letters = text.Select(f => font.Metrics[f]).ToArray();
double scale = (double)size / font.Size;
double destX = x;
foreach (var letter in letters)
{
var destRect = new Rect(destX,y,letter.Width * scale,letter.Height * scale);
bmp.Blit(destRect, font.Image, letter, color, WriteableBitmapExtensions.BlendMode.Alpha);
destX += destRect.Width;
}
}
}
You need to call RegisterFont once to load the files then you call DrawString. It uses WriteableBitmapEx.Blit so if your font file has white text and a transparent background alpha is handled correctly and you can recolour it. The code does scale the text if you draw at a size you didn't load but the results aren't good, a better interpolation method could be used.
I tried drawing from a different thread and this worked in the emulator, you still need to create the WriteableBitmap on the main thread. My understanding of your scenario is that you want to scroll through tiles similar to how mapping apps work, if this is the case reuse the old WriteableBitmaps instead of recreating them. If not the code could be changed to work with arrays instead.
I'm not sure if this will fully resolve your issues, but there are 2 tools that I use in my comic book reader (I won't shamelessly plug it here, but I'm tempted.. a hint if you are searching for it.. it is "Amazing"). There are times where I need to stitch together a bunch of images. I use Rene Schulte's (and a bunch of other contributors) WriteableBitmapExtensions (http://writeablebitmapex.codeplex.com/). I have been able to offload rendering/stitching of an image to a background thread and then set the resulting WriteableBitmap as the source of some image on the UI thread.
Another up and comer in this space is the .NET Image Tools (http://imagetools.codeplex.com/). They have a bunch of utilities for saving/reading various image formats. They also have a few of the low levels, and I wish there were an easy way to use both (but there isn't).
All of the above work in WP7.
I guess the major difference is with these tools you won't be using XAML you will be writing directly to your image (so you may need to do size detection of your text and stuff like that).
The very nature of UI elements requires interaction with them on the UI thread. Even if you could create them on a background thread, when you came to try to render them into the WriteableBitmap you'd get a similar exception, and even then if it allowed you to do that, the elements wouldn't actually have a visual representation until they were added into the visual tree. You might need to use a generic image manipulation library instead of using UI elements.
Perhaps you could describe your scenario on a wider basis, we might have a better solution for you :)
First off, are you sure about rendering this as a bitmap? How about generating a Canvas with an image and TextBlock?
I need to render a large number of images
I have a feeling that this generating will kill phone performance. Generally, for bitmap mainupulation, the best way is to use XNA. Some parts of the XNA framework do a great job Silverlight projects. (BTW the refreshed Windows Phone Developer Tools will allow Silverlight and XNA coexist in the same project)
I would step back and think about this feature. Developing something like this for a week and then end up with unacceptable performance would make me a sad panda.
EDIT
As far I understand you need some kind of popup with image as a background and the message.
Make a Canvas with TextBlock but hide it.
<Canvas x:Name="userInfoCanvas" Height="200" Width="200" Visibility="Collapsed">
<Image x:Name="backgroundImage"> </Image>
<TextBlock x:Name="messageTextBlock" Canvas.ZIndex="3> </TextBlock> <!--ZIndex set the order of elements -->
</Canvas>
When you got the new message, show the Canvas to the user (a opacity animation would be nice), when you finish rendering on the background thread.
messageTextBlock.Text = message;
backgroundImage.Source = new BitmapImage(renderedImage);
Obviouslly, here is a problem with update. UIelements can be updated only form the UI Thread, hence update must be queue with Dispatcher
Dispatcher.BeginInvoke(DispatcherPriority.Background, messageUpdate); //messageUpdate is an Action or anthing that can be infered to Delegate
PS. didn't compile, this is more pseudocode.
You can draw on WriteableBitmap in thread, but You have to
create WriteableBitmap in main UI thread
do draw work in background thread
assign BitmapSource in main UI thread
i'll agree with Derek's answer: you're trying to use UI controls without a UI.
If you want to render a bitmap you need to stick to classes for drawing text on bitmaps.
i presume Windows Phone 7 has the .NET Compact Framework.
psudeo-code:
public Bitmap RenderText(string text, double x, double y)
{
Bitmap bitmap = new Bitmap(400, 400);
using (Graphics g = new Graphics(bitmap))
{
using (Font font = SystemFonts....)
{
using (Brush brush = new SolidColorBrush(...))
{
g.DrawString(text, font, brush, new Point(x, y));
}
}
}
return bitmap;
}
I need to draw a line (with the mouse) over everything with C#. I can get a Graphics object of the desktop window by using P/Invoke:
DesktopGraphics = Graphics.FromHdc(GetDC(IntPtr.Zero));
However, anything I draw using this graphics object is only showing on the left monitor, and nothing on the right monitor. It doesn't fail or anything, it just doesn't show.
After I create the Graphics object, it shows the visible clip region to be 1680 x 1050 which is the resolution of my left monitor. I can only assume that it's only getting a device context for the left monitor. Is their a way to get the device context for both (or any number) monitors?
EDIT 3/7/2009:
Additional information about the fix I used.
I used the fix provided by colithium to come up with the following code for creating a graphics object for each monitor as well as a way to store the offset so that I can translate global mouse points to valid points on the graphics surface.
private void InitializeGraphics()
{
// Create graphics for each display using compatibility mode
CompatibilitySurfaces = Screen.AllScreens.Select(s => new CompatibilitySurface()
{
SurfaceGraphics = Graphics.FromHdc(CreateDC(null, s.DeviceName, null, IntPtr.Zero)),
Offset = new Size(s.Bounds.Location)
}).ToArray();
}
private class CompatibilitySurface : IDisposable
{
public Graphics SurfaceGraphics = null;
public Size Offset = default(Size);
public PointF[] OffsetPoints(PointF[] Points)
{
return Points.Select(p => PointF.Subtract(p, Offset)).ToArray();
}
public void Dispose()
{
if (SurfaceGraphics != null)
SurfaceGraphics.Dispose();
}
}
[DllImport("gdi32.dll")]
static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
Here is a link to another person that had the same problem. It was solved with a call to:
CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL)
which will return a DC to all monitors.
Following URL to get EnumDisplayMonitor may solve your problem
MSDN
To retrieve information about all of the display monitors, use code like this:
EnumDisplayMonitors(NULL, NULL, MyInfoEnumProc, 0); One more URL given at
MSJ