Why is my Image.Source still black after setting it's stream? - c#

So I am trying to add a "screenshot" onto the source of the Image by saving the Bitmap to the stream and the setting the Source to the BitmapImage.
But for some reason the Image control is just black it doesnt show anything.
Why is that?
//this.Hide();
//Create a black bitmap with the correct width and height and use it as a "Canvas" that we will
//be performing a bit-block transfer on. (Bitmap is mutable)
Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
//To perform a bit-block transfer we need the object to be a "Graphics" object.
Graphics g = Graphics.FromImage(printscreen);
//Perform the bit-block transfer and alter the image.
//Source being the screens pixels, and the destination is the black bitmap.
g.CopyFromScreen(0, 0, 0, 0, printscreen.Size);
//Create a temporary memory stream for the image.
using (MemoryStream mStream = new MemoryStream())
{
//Save the graphics object (image) in the memory stream. with a specified format.
printscreen.Save(mStream, ImageFormat.Bmp);
var ImageSource = new BitmapImage();
ImageSource.BeginInit();
ImageSource.StreamSource = mStream;
ImageSource.EndInit();
ImageBox.Source = ImageSource;
}
As you can see I am setting the Source property to the ImageSource yet it's black.
WPF
<Window x:Class="eh.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:eh"
mc:Ignorable="d"
Title="MainWindow" Height="450" Loaded="MainWindow_OnLoaded" WindowStyle="None" ResizeMode="NoResize" Background="Black" WindowState="Maximized" WindowStartupLocation="Manual" Left="0" Top="0" Width="800">
<Grid>
<Image x:Name="ImageBox" HorizontalAlignment="Left" VerticalAlignment="Top" Height="1920" Width="1080"/>
</Grid>
</Window>

I forgot to add the CacheOption so I added
ImageSource.CacheOption = BitmapCacheOption.OnLoad;

Related

Invalid WPF canvas mouse up event

I tried to create a screenshot function, set the "Canvas" background as a picture, press and raise the mouse to draw a rectangle, and capture the content inside the rectangle, but my mouse up event is always invalid. I searched all kinds of information and tried, all of them seem to be invalid
public Bitmap GetScreenSnapshot()
{
try
{
System.Drawing.Rectangle rc = SystemInformation.VirtualScreen;
var bitmap = new Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics memoryGrahics = Graphics.FromImage(bitmap))
{
memoryGrahics.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, CopyPixelOperation.SourceCopy);
}
return bitmap;
}
catch (Exception)
{
}
return null;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var screenSnapshot = GetScreenSnapshot();
var bmp = ImageProcessing.ToBitmapSource(screenSnapshot);
bmp.Freeze();
Clipper clipper = new Clipper();
clipper.bitmap = screenSnapshot;//将图片传过去
clipper.Background = new ImageBrush(bmp);
clipper.Show();
}
Here, I use a button to create a Window (Clipper) in the main window, and provide the obtained desktop image to Canvas
<Window x:Class="MyOCR_WPF.Clipper"
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:MyOCR_WPF"
mc:Ignorable="d"
Title="Clipper" Height="450" Width="800" WindowStyle="None" WindowState="Maximized" WindowStartupLocation="CenterScreen">
<Canvas x:Name="canvas" MouseMove="canvas_MouseMove" MouseDown="canvas_MouseDown" MouseUp="canvas_MouseUp" Focusable="True" Background="Transparent">
</Canvas>
Its mouse up event is always invalid
Mouseup is invalid because you have drawn a Rectangle on cancas.When mouseup is triggered on Rectangle, the mouseup of Cancas cannot be triggered.
I tested and found no PreviewMouseUp on Cancas either.
I used a different solution to your problem.
view
<Grid>
<Canvas x:Name="canvas" Focusable="False" />
<Border MouseMove="canvas_MouseMove" MouseDown="canvas_MouseDown" MouseUp="canvas_MouseUp" Background="Transparent"/>
</Grid>

Convert XAML Window to Bitmap

I have a simple XAML like this:
<UserControl x:Class="blabla.MyView"
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"
Name="myUserControl">
<Grid Name="myGrid" Background="White">
<TextBlock Name="myTextBlock"/>
</Grid>
</UserControl>
Now I'd like to create a new view in the background by using this xaml. After this, I'd like to change the controls (or maybe add some controls by myself), save it to a Bitmap and assign it to the Clipboard (just to see the output). I would have tried it like this:
MyView view = new MyView();
view.myTextBlock.Text = "TEST";
Rect bounds = VisualTreeHelper.GetDescendantBounds(view);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(view);
ctx.DrawRectangle(vb, null, new Rect(new System.Windows.Point(), bounds.Size));
}
RenderTargetBitmap bmp = new RenderTargetBitmap((int)view.ActualWidth, (int)view.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bmp.Render(dv);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(bmp));
System.Windows.Clipboard.SetImage(pngImage.Frames[0]);
Unfortunately, the Size of the view is not being changed. Trying to set a hard coded width/height by produces a black image/bitmap.
How can I create a simple Bitmap out of a XAML?
Making the comment to official answer. However, I see that this solution is not the most elegant one. General thing is, that the window, resp. view, will result in black area when rendered while it is not visible/redered itself by showing it anyhow.
The workarround would be to show this view (as you wrote, but in Window).
Small idea doing this, move the window out of visible screen to avoid flickering.
example:
var v = new MainWindow();
v.Top = -10000; // far away
v.Left = -10000; // far away
v.Show();
// .. do rendering
v.Close();

When changing the program size, the image box should automatically expand

I have a program size and if I increase this at the corners is to increase the ImageBox automatically with.Unfortunately, this does not work yet, since an error occurs.Has anyone a solution to this, if I change the form size, which also increases the ImageBox automatically.I would like the size of the ImageBox then also larger, if I change the size of the form.
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
imgPreview.Source = zoom(imgPreview, new System.Drawing.Size(Convert.ToInt32(this.Width), Convert.ToInt32(this.Height))); // Mistake is here
}
System.Drawing.Image zoom(System.Drawing.Image img, System.Drawing.Size size)
{
Bitmap bmp = new Bitmap(img, img.Width + (img.Width * size.Width / 100), img.Height + (img.Height * size.Height / 100));
Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
return bmp;
}
<UserControl x:Class="Vorschau.UCOxyplotPreview"
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"
xmlns:local="clr-namespace:Vorschau"
mc:Ignorable="d" Height="379.573" Width="539.634" SizeChanged="UserControl_SizeChanged">
<Image x:Name="imgPreview" HorizontalAlignment="Left" Height="350" Margin="109,20,0,0" VerticalAlignment="Top" Width="350"/>
</UserControl>
Try to remove the properties HorizontalAlignment, Height, Margin, VerticalAlignment and Width. The Image should resize automatically when done.
//edit
Just to show you a minimalistic example. When using this code your Image size will change your Window size is changing. That's why I think the problem is in your Window xaml where you use your UserControl with the Image.
<Window x:Class="WpfApplication2.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:WpfApplication2"
mc:Ignorable="d"
xmlns:viewModel="clr-namespace:WpfApplication2">
<Image Source="IMG_20160831_105257778.jpg" />
</Window>

Adding an image

I am attempting to implement a flood fill tool to a WPF paint program. I am attempting to bypass any of the vector methods by making sure every drawing action is written to the bitmap first. The bitmap is then set to the canvas.
I am having an issue when passing the image to the canvas. It does not set the image using a relative position. The images will explain much better than I can.
Before Fill:
After Fill:
The image bitmap is written relative to the window and overlaps the toolbar. I am wondering how I can prevent this from happening. Attached is the relevant code from my Fill class.
public override void OnMouseDown(CCDrawingCanvas canvas, System.Windows.Input.MouseButtonEventArgs e) {
double dpi = 96d;
// Get the size of the canvas
System.Windows.Size size = new System.Windows.Size((int)canvas.ActualWidth, (int)canvas.ActualHeight);
// Measure and arrange the surface
canvas.Measure(size);
canvas.Arrange(new Rect(size));
RenderTargetBitmap source = new RenderTargetBitmap(
(int)canvas.ActualWidth,
(int)canvas.ActualHeight,
dpi,
dpi,
PixelFormats.Pbgra32);
source.Render(canvas);
WriteableBitmap modifiedImage = new WriteableBitmap(source);
int h = modifiedImage.PixelHeight;
int w = modifiedImage.PixelWidth;
int[] pixelData = new int[h * w];
int widthInByte = modifiedImage.PixelWidth * (modifiedImage.Format.BitsPerPixel / 8);
modifiedImage.CopyPixels(pixelData, widthInByte, 0);
int oldColor = BitConverter.ToInt32(new byte[] { System.Drawing.Color.White.B, System.Drawing.Color.White.G, System.Drawing.Color.White.R, System.Drawing.Color.White.A }, 0);
int newColor = BitConverter.ToInt32(new byte[] { System.Drawing.Color.Black.B, System.Drawing.Color.Black.G, System.Drawing.Color.Black.R, System.Drawing.Color.Black.A }, 0);
// Perform the recursive fill
FloodFill(pixelData, (int)p.X, (int)p.Y, w, h, oldColor, newColor);
modifiedImage.WritePixels(new Int32Rect(0, 0, w, h), pixelData, widthInByte, 0);
newFill = new GraphicsFill(modifiedImage);
// Adds newFill to canvas.GraphicsList
AddObject(canvas, newFill);
}
XAML Code:
<Window x:Class="ccGui.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:effects="clr-namespace:System.Windows.Media.Effects;assembly=presentationcore"
xmlns:lib="clr-namespace:ccDrawingLib;assembly=ccDrawingLib"
xmlns:local="clr-namespace:ccGui"
Title="MainWindow" Height="600" Width="800">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="New" Command="ApplicationCommands.New" />
<MenuItem Header="Open" Command="ApplicationCommands.Open" />
<MenuItem Header="Save" Command="ApplicationCommands.Save" />
<MenuItem Header="Save As" Command="ApplicationCommands.SaveAs" />
<MenuItem Header="Close" Command="ApplicationCommands.Close" />
</MenuItem>
<MenuItem Header="Tool" Name="menuTools">
<MenuItem Header="Brush" Name="ccToolBrush" Tag="Brush" />
<MenuItem Header="Fill" Name="ccToolFill" Tag="Fill" />
</MenuItem>
</Menu>
<lib:CCDrawingCanvas x:Name="canvas" Background="White" />
</DockPanel>
</Window>
In WPF, practically all UI controls extend the Visual class. You can use that Visual reference to pass to the RenderTargetBitmap.Render Method to easily save an image of that UI control. It seems to me that you shouldn't need to keep switching between the Shapes and BitMapImages to fill your Shapes. I guess that you're using a Path when drawing shapes. If this is true, then you can just use the Path.Fill property to fill it in without having to do any manual calculations.
Furthermore, if you set the Panel.Zindex Attached Property on each new drawn shape and use the next higher Zindex value each time, then the latest shapes will always sit on top of the lower ones, just as in a drawing standard program. You could even enable the users to switch the shape layers of their drawn picture using this property.

Size a window to an image at a URL

The Source of the Image is bound to a URL which points to an image.
If the image at the URL is smaller then the MaxHeight and MaxWidth, the following code works great. The image size is exactly the same as the url and the window is sized properly.
If the image at the URL is larger then the MaxHeight and MaxWidth, only a portion of the image is displayed. The image does not get shrunk to fit into the window.
If I remove Stretch="None", the large picture then shrinks to fit into the MaxHeight and MaxWidth, looks great, but the small image gets expanded to consume all available space and looks like crap.
Here are two images I have been testing with:
http://imgur.com/iaBp2Fv,fiRrTJS#0
<Window x:Class="MyNamespace.Windows.PictureWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Profile Picture" ResizeMode="NoResize" UseLayoutRounding="True" SizeToContent="WidthAndHeight" MaxHeight="750" MaxWidth="750">
<Image Stretch="None" HorizontalAlignment="Center" VerticalAlignment="Center" Source="{Binding}" />
</Window>
What you could do is to to remove the Stretch="None", as you say, to make the large image shrink down. But, to avoid the small image to be scaled up, you just add this property:
StretchDirection="DownOnly"
This prevents small images from being scaled upwards, and allows large images to be scaled down. The Window resizes appropriately as well.
This is the code that I have tested in LinqPad. Just change showLarge to true and false to switch between the images.
bool showLarge = false;
var w = new Window();
w.ResizeMode = ResizeMode.NoResize;
w.UseLayoutRounding = true;
w.SizeToContent = SizeToContent.WidthAndHeight;
w.MaxHeight = 750;
w.MaxWidth = 750;
Image img = new Image();
img.HorizontalAlignment = HorizontalAlignment.Center;
img.VerticalAlignment = VerticalAlignment.Center;
img.StretchDirection = StretchDirection.DownOnly;
if(showLarge)
img.Source = new BitmapImage(new System.Uri(#"http://i.imgur.com/iaBp2Fv.jpg"));
else
img.Source = new BitmapImage(new System.Uri(#"http://i.imgur.com/fiRrTJS.jpg"));
w.Content = img;
w.ShowDialog();
In your code behind, create a property as
public Point ImageSize {get;set}
Get the image from the URL in the constructor/Initialize() and set ImageSize accordingly
Bind the height and width of your window to ImageSize.X and ImageSize.Y
Height="{Binding ImageSize.Y}" Width="{Binding ImageSize.Y}"

Categories