C# WPF grab screenshot with SnippingTool effect - c#

I'm trying to integrate a screenshot grabbing feature in my WPF app and I'd like it to look like snipping tool.
So far I've managed accomplish something similar by creating a fullscreen window (with a canvas) with opacity set to 0.5 and dark background. When I click somewhere and start dragging, a white rectangle is drawn, generating an effect similar to this.
What I'd like to have is the inner part of that rectangle opening a opacity hole in the background canvas, so that I could see through the selected area - just like snipping tool.
Problem is, being fairly new to .NET, I have no idea how or where to start. Did some research and tests on the OpacityMask field of the screenshot window but got nowhere.
Here's a little vid to show the current effect.
Edit: Also, as bonus question, is there an easy way to grab a screenshot that spans across multiple monitors (virtual screen)? Graphics.CopyFromScreen() only seems to work for 1 screen.
Already fixed this and seems to work for all possible weird virtual desktop layouts:
// Capture screenie (rectangle is the area previously selected
double left = Canvas.GetLeft(this.rectangle);
double top = Canvas.GetTop(this.rectangle);
// Calculate left/top offset regarding to primary screen (where the app runs)
var virtualDisplay = System.Windows.Forms.SystemInformation.VirtualScreen;
var primaryScreen = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
if (virtualDisplay.Left < primaryScreen.Left)
{
left -= Math.Abs(virtualDisplay.Left - primaryScreen.Left);
}
if (virtualDisplay.Top < primaryScreen.Top)
{
top -= Math.Abs(virtualDisplay.Top - primaryScreen.Top);
}

You can have a CombinedGeometry with GeometryCombineMode="Exclude" creating a "punched" effect. Sample:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" AllowsTransparency="True"
WindowStyle="None" Background="Transparent">
<Canvas >
<Path Stroke="Black" Fill="White" Opacity=".5">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,800,600" >
</RectangleGeometry>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="50,50,100,100" >
</RectangleGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
</Window>

Related

Mouse Lag in UNO Wpf/Windows

Im trying to build a high performance app on UNO platform (WPF/Windows/Wasm) that allows the user to draw primitive shapes (lines, rectanges, etc) Since it will have snapping to points, and showing snap point it should be smooth.
The mouse lags when moving over the canvas, the lag is small, but users of this app will be working very quickly and these lags make it feel unresponsive. Is there any way to speed this up?
A simple white box demo
MainPage.Xaml
<Grid>
<Canvas Width="2088" Height="2100.17014099374" x:Name="MainCanvas" >
<Canvas.Background>
<SolidColorBrush Color="#eaeaea"></SolidColorBrush>
</Canvas.Background>
<Ellipse Name="SnapPoint" Canvas.Top="400" Canvas.Left="400" Width="25" Height="25" Fill="Blue"></Ellipse>
</Canvas>
</Grid>
MainPage.Xaml.cs
private void MainCanvas_PointerMoved(object sender, PointerRoutedEventArgs e)
{
var position = e.GetCurrentPoint(MainCanvas).Position;
Canvas.SetLeft(SnapPoint, position.X);
Canvas.SetTop(SnapPoint, position.Y);
}
I have also tried binding the Left and Top properties of the ellipse to view model, but the delay is the same.
I was using remote desktop from Mac to Windows, this caused a delay in the mouse pointer on the screen

How to burn WPF Image + Shape to new image

I've got a WPF User control with a canvas. It looks roughly like this (more complete version at the bottom):
<UserControl>
<Canvas>
<Image x:Name="CurrentImage" ... etc .../>
<Path x:Name="SurfacePath" ... etc .../>
</Canvas>
</UserControl>
Looks great on the screen but I need to write code that will create from all of this a new image "mask" of the same size as the original image. This mask will represent a combination of the two, with the original image pixels as black but the overlaid shape pixels as white. (A low-level SDK will later use the pixel data for some heavy math.)
Unfortunately, I am at a loss as to how to even approach creating this "mask" image. Aside from laying out elements in panels and building paths with geometries, the way that WPF drawing really works has always confused me. I get lost in all classes with similar names and when to use them, ImageDrawing vs DrawingImage, etc. I've read and googled and searched here quite a bit and I still don't know where to start.
Can anyone give me even the rough outline of the steps I would take to even approach this?
Here's an somewhat more complete version of what I'm using.
<UserControl x:Class="MyProject.Core.Shapes.ShapeCanvas"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
UseLayoutRounding="true"
>
<Canvas x:Name="Scene">
<!--Image is drawn first, underneath everything else. Image 0,0 is Canvas 0,0-->
<Image x:Name="CurrentImage"
Canvas.Left="0" Canvas.Top="0"
Source="{Binding Image, Mode=OneWay}"/>
<!-- Path showing the current polyline created with the "Surface" tool. -->
<Path x:Name="SurfacePath"
ClipToBounds="False"
Canvas.Left="0"
Canvas.Top="0"
Stroke="Gray"
StrokeThickness="50"
>
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="{Binding CurrentSegmentStartPoint}">
<PathFigure.Segments>
<PolyBezierSegment Points="{Binding CurrentSegmentPoints}"/>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
</UserControl>
I have used this code for rendering a WPF control to a BitmapSource/Image for code-behind:
public static BitmapSource Create(UIElement control, Size size = default(Size))
{
if (control == null)
return null;
if (size == Size.Empty || (size.Height == 0 && size.Width == 0))
size = new Size(double.PositiveInfinity, double.PositiveInfinity);
control.Measure(size);
RenderTargetBitmap bmp = new RenderTargetBitmap((int)control.DesiredSize.Width, (int)control.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);
Rect rect = new Rect(0, 0, control.DesiredSize.Width, control.DesiredSize.Height);
control.Arrange(rect);
control.UpdateLayout();
bmp.Render(control);
bmp.Freeze();
return bmp;
}
It is derivative of answers here: https://stackoverflow.com/a/27077188/8302901
If your control is an instance of the control that is already on the screen, skip the measure, arrange and layout steps and just use the already calculated size.

Move image with mouse wpf c#

Hello, I have been trying to work this problem out for some time now,
I hope that you are able to lead me in the right direction!
The goal is to have a "flashlight" effect in my wpf game, to do this I have pursued 2 main options.
Have a black png image that is 200% width & height of window with a hole in the center, then move the image with the mouse
Have a black image the size of the window, and have an opacity mask element that is a circle shape that looks through the black layer to the content below. This circle moves with the mouse.
I cannot get either of these options to work, in wpf when you have an opacity mask it appears you cannot move the element that is "punching through" the layer. And for the large image method, I cannot convert a mouse Point object to an image's position. Below are some potential methods of getting mouse position, I have not been able to move the image to "point" location.
Point to screen
private void MouseCordinateMethod(object sender, MouseEventArgs e)
{
var relativePosition = e.GetPosition(this);
var point= PointToScreen(relativePosition);
_x.HorizontalOffset = point.X;
_x.VerticalOffset = point.Y;
}
GetPosition
var point = e.GetPosition(this.YourControl);
The final game will be using kinect for controls, so if there is a way to do this with kinect libraries or natively that is also an option. Thank you.
You could use a Path element with a CombinedGeometry consisting of a very large RectangleGeometry and an excluded (and therefore transparent) EllipseGeometry, which is moved by changing its Center property on mouse input:
<Grid MouseMove="Grid_MouseMove">
<TextBlock Text="Hello, World" FontSize="40"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Path Fill="Black">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,10000,10000"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry x:Name="circle" RadiusX="100" RadiusY="100"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Grid>
The MouseMove handler:
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
circle.Center = e.GetPosition((IInputElement)sender);
}

Neon/Glow effect in C#/WPF

I'd like to create a neon(-like) effect in wpf/c#, to be used on a series of polylines.
The closest i come to this was using blur (not very close, but eh), but the it dims the colors too dark and I have no idea how to make even that glow. Is there an effect close to this, or I should try to write a shader for it somehow?
I'd like to do this for a school project and i'd rather not turn in a bunch of outside libraries for a small amount of self-written code. Also about google: most of the stuff i found were pretty much using blur/dropshadow to create these fading colors, not something that actually has this neon-y effect.
As others have already suggested you should use DropShadowEffect to achieve a neon-like effect:
<Canvas Height="120" Width="280" Background="Black">
<Polyline
Points="10,110 60,10 110,110 105,110 60,18 15,110 10,110"
Stroke="#BB0000"
Fill="#FF0000"
StrokeThickness="2" >
<Polyline.Effect>
<DropShadowEffect Color="#FF9999" ShadowDepth="0" Direction="0" BlurRadius="25" />
</Polyline.Effect>
</Polyline>
<Polyline
Points="10,105 110,105 110,10 115,10 115,110 10,110 10,105"
Stroke="#00BB00"
Fill="#00FF00"
StrokeThickness="2"
Canvas.Left="150">
<Polyline.Effect>
<DropShadowEffect Color="#99FF99" ShadowDepth="0" Direction="0" BlurRadius="25" />
</Polyline.Effect>
</Polyline>
</Canvas>
Unfortunately there is no built-in effect which is specifically designed to create neon effect, but by tweaking the colors you can create quite good (or at least acceptable) results (especially for a school project...):

Multiple regions for mouseover event in a single image

I have an image in WPF that I would like to have display different information based upon where the mouse is currently hovering. I know I've seen this in websites, I just can't seem to figure out the code to do it in WPF.
The image I'm using is a US map, and I need state specific info to appear as the user crosses the borders. Right now the implementation I'm using is a series of Paths drawn in transparent on top of the map, and then using the Mouse.MouseEnter event to trigger the change. The problem is that the updating seems to suffer from horrible lag, or else the MouseEnter event isn't always catching properly.
Does anybody know of a better way to do this?
Sample C#
private void wyoming_MouseEnter(object sender, MouseEventArgs e)
{
//stateName.Text = "Wyoming";
}
Sample XAML
<Canvas MouseDown="Canvas_MouseDown" Name="canvas">
<Viewbox Stretch="Uniform">
<Image Source="USA.png" />
</Viewbox>
<Path Name="wyoming" Stroke="Transparent" StrokeThickness="1" Mouse.MouseEnter="wyoming_MouseEnter" Mouse.MouseMove="wyoming_MouseMove">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsClosed="True" StartPoint="184,121" > <!--NW-->
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="266,129" />
<LineSegment Point="264,193" />
<LineSegment Point="203,190" />
<LineSegment Point="177,186" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>
Well, after playing around with this more, I discovered my problem was that I had not filled the paths, only kept them as lines. This basically made for a very small event trigger area, and as such WPF sometimes missed the event. By filling them with Transparent, everything works quickly.
Personally what I did for this same instance (except I was labeling info pertaining to time zones and broadcast areas) was I made transparent path shapes for the areas I want able to be activated on mouseover and laid them over a U.S. Map, then I attached an Event trigger to each for the MouseEnter/MouseLeave events so when the user would mouse over the area of whatever Path, it would fire off that condition and do the action specified....in my instance it was showing radio station broadcast times and programs based on region and time zone. So if an are was Moused over, an info box visibility was set to Visible. On MouseLeave that same info box was set to Collapsed....etc
It was very effective and worked real well and you can make your interactive areas very defined when using the precise shapes you made with your Paths. If I can dig up the source I'll try and share but the project was over a year ago so hopefully the description gives you enough of an idea to get the creativity going. :)

Categories