WPF, C#: Draw a line onto existing bitmap in image control - wpf

I have a bitmap image in an image control
I need to draw a red line on the bitmap each time I click with the mouse onto it, at the place where I clicked the mouse.
I first thought of creating a Line object, but found out, that I cannot add the Line. I would need a canvas. But if I put my image in a canvas, my bitmap does not stretch over the whole canvas (I found out, that the coordinates of the bitmap determine the place on the canvas, so my bitmap is wrongly displayed.)
Then I tried using graphics
Graphics graphics = Graphics.FromImage(bitmapImg);
graphics.DrawLine(new System.Drawing.Pen(System.Drawing.Color.Red), 0, 0, bitmapImg.Width, bitmapImg.Height); //not the line yet, just for testing
graphics.DrawImage(bitmapImg, 0, 0, bitmapImg.Width,bitmapImg.Height);
graphics.Dispose();
However, I don`t get anything painted onto my bitmap........
Now I think, I probably have to get the bitmap into an array and then change the pixel color to get the line in the bitmap. I believe, that this would be very slow.
I am now trying something with visualDrawing, however, I have not got it to work yet:-(
What is a good way to get a line onto an existing bitmap in WPF C#???? and how to remove it?
I would be glad for any help! Thank you! I posted it already on the MS forum page, but no answer so far.

When you do Graphics.FromImage, this Graphics class (and also the System.Drawing.Pen) do not belong to WPF, they are part from WinForms and they are internally using Windows' GDI+ calls to draw and cannot draw on top of WPF.
If you didn't got an error when compiling the first line of your code, then probably your bitmapImg is a System.Drawing.Image (from WinForms) not an Image control from WPF (System.Window.Controls.Image).
As adrianm mentioned, the easiest way will probably be to use a Grid:
<Grid>
<Image Source="your image" />
<Line Name="line" Visibility="Hidden" Stroke="Red" StrokeThickness="1" />
</Grid>
Then, in your click event handler you can make the line visible and give it the coordinates you want:
line.Visibility = Visible;
line.X1 = mouse_x;
line.Y1 = mouse_y;
line.X2 = ...;
line.Y2 = ...;

You can place a canvas with the background as transparent on top of your BitmapImage and then draw the line as required.
Code from xaml file:
<Grid>
<Image Source="C:\Users\sm143444\Desktop\download.jpg" />
<Canvas Background="Transparent" x:Name="draw" />
</Grid>
Code from Xaml.cs:
public MainWindow()
{
InitializeComponent();
Point startPoint = new Point(50, 50);
Line newLine = new Line();
newLine.Stroke = Brushes.Black;
newLine.Fill = Brushes.Black;
newLine.StrokeLineJoin = PenLineJoin.Bevel;
newLine.X1 = startPoint.X;
newLine.Y1 = startPoint.Y;
newLine.X2 = startPoint.X + 100;
newLine.Y2 = startPoint.Y + 100;
newLine.StrokeThickness = 2;
this.draw.Children.Add(newLine);
}
output
Or you can even add a ZIndex to your image and Line so that they are laid on different layers on canvas.

Related

How to display a Bitmap Windows zoom level (DPI?) independent in WPF

I am new to WPF, and facing to the following issue.
I am displaying a bitmap, xaml:
<ControlTemplate x:Key="MyEditorImageTemplate">
<Image VerticalAlignment="Top" Margin="3,1,0,0"
RenderOptions.BitmapScalingMode="NearestNeighbor"
Stretch="None">
<Image.Source>
<MultiBinding Converter="{StaticResource MyEditorConverter}">
<Binding Path="....." />
The Bitmap is coming from BitmapSource, provided the following way:
Bitmap^ bitmap = System::Drawing::Image::FromHbitmap((IntPtr)aBmp);
IntPtr hbmp = bitmap->GetHbitmap();
BitmapSource bs =
Imaging::CreateBitmapSourceFromHBitmap(hbmp,
System::IntPtr::Zero,
Int32Rect(0,0,nWidth,nHeight),
BitmapSizeOptions::FromEmptyOptions());
Now this works fine, until I have the windows zoom level at 100%.
If I change it in Windows to 125%:
The bitmap is also zoomed on the GUI.
I understand that this is -sort of- the expected, but is there any way that I still show it 1-1 pixel size, ignoring windows zoom settings?
Ok, I found the solution.
You just need an own Image class, derived from Image, and simple override the OnRender, where you take care of the proper resizing (if necessary).
protected override void OnRender(DrawingContext dc)
{
//base.OnRender(dc); --> we do out own painting
Rect targetRect = new Rect(RenderSize);
PresentationSource ps = PresentationSource.FromVisual(this);
if (ps != null && Source != null)
{
Matrix fromDevice = ps.CompositionTarget.TransformFromDevice;
Vector currentVisibleSize = new Vector(Source.Width, Source.Height);
Vector originalRestoredSize = fromDevice.Transform(currentVisibleSize);
targetRect = new Rect(new Size(originalRestoredSize.X, originalRestoredSize.Y));
}
dc.DrawImage(Source, targetRect);
}
With this, regardless of the Windows zoom setting, you will have always the 100% original size.
I got the idea from this site: http://blogs.msdn.com/b/dwayneneed/archive/2007/10/05/blurry-bitmaps.aspx

RenderTargetBitmap + Resource'd VisualBrush = incomplete image

I've found a new twist on the "Visual to RenderTargetBitmap" question!
I'm rendering previews of WPF stuff for a designer. That means I need to take a WPF visual and render it to a bitmap without that visual ever being displayed. Got a nice little method to do it like to see it here it goes
private static BitmapSource CreateBitmapSource(FrameworkElement visual)
{
Border b = new Border { Width = visual.Width, Height = visual.Height };
b.BorderBrush = Brushes.Black;
b.BorderThickness = new Thickness(1);
b.Background = Brushes.White;
b.Child = visual;
b.Measure(new Size(b.Width, b.Height));
b.Arrange(new Rect(b.DesiredSize));
RenderTargetBitmap rtb = new RenderTargetBitmap(
(int)b.ActualWidth,
(int)b.ActualHeight,
96,
96,
PixelFormats.Pbgra32);
// intermediate step here to ensure any VisualBrushes are rendered properly
DrawingVisual dv = new DrawingVisual();
using (var dc = dv.RenderOpen())
{
var vb = new VisualBrush(b);
dc.DrawRectangle(vb, null, new Rect(new Point(), b.DesiredSize));
}
rtb.Render(dv);
return rtb;
}
Works fine, except for one leeetle thing... if my FrameworkElement has a VisualBrush, that brush doesn't end up in the final rendered bitmap. Something like this:
<UserControl.Resources>
<VisualBrush
x:Key="LOLgo">
<VisualBrush.Visual>
<!-- blah blah -->
<Grid
Background="{StaticResource LOLgo}">
<!-- yadda yadda -->
Everything else renders to the bitmap, but that VisualBrush just won't show. The obvious google solutions have been attempted and have failed. Even the ones that specifically mention VisualBrushes missing from RTB'd bitmaps.
I have a sneaky suspicion this might be caused by the fact that its a Resource, and that lazy resource isn't being inlined. So a possible fix would be to, somehow(???), force resolution of all static resource references before rendering. But I have absolutely no idea how to do that.
Anybody have a fix for this?
You have two problems:
You didn't set a PresentationSource on your visual so Loaded events won't fire.
You didn't flush the Dispatcher queue. Without flushing the Dispatcher queue, any functionality that uses Dispatcher callbacks won't work.
The immediate cause of your problem is failure to flush the Dispatcher queue, since VisualBrush uses it, but you will probably run into the PresentationSource problem before long so I would fix both of these.
Here is how I do it:
// Create the container
var container = new Border
{
Child = contentVisual,
Background = Brushes.White,
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(1),
};
// Measure and arrange the container
container.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
container.Arrange(new Rect(container.DesiredSize));
// Temporarily add a PresentationSource if none exists
using(var temporaryPresentationSource = new HwndSource(new HwndSourceParameters()) { RootVisual = (VisualTreeHelper.GetParent(container)==null ? container : null) })
{
// Flush the dispatcher queue
Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
// Render to bitmap
var rtb = new RenderTargetBitmap((int)b.ActualWidth, (int)b.ActualHeight, 96, 96, PixelFormats.Pbgra32);
rtb.Render(container);
return rtb;
}
FYI, StaticResource lookup is never delayed under any circumstances: It is processed the moment the XAML is loaded and immediately replaced with the value retrieved from the ResourceDictionary. The only way StaticResource could possibly be related is if it picked up the wrong resource because two resources had the same key. I just thought I should explain this -- it has nothing to do with your actual problem.
Well to inline it, you could just do something like this:
<Grid>
<Grid.Background>
<VisualBrush>
<VisualBrush.Visual>
<!-- blah blah -->
</VisualBrush.Visual>
</VisualBrush>
</Grid.Background>
</Grid>
If that doesn't work, my guess would be that it must be something specific with the Visual instance you are using (and that will require further code to better diagnose).

Printing of WPF Window on one page

I am able to print the current Window using the following code:
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog().GetValueOrDefault(false))
{
printDialog.PrintVisual(this, this.Title);
}
However if the Window does not fit the page it get truncated.
How do I make the Window fit the Page ?
I guess I need to make a graphics element first and check if this graphics fits the page, but I have found nothing so far.
There is one solution out there that lots of people are reposting as their own. It can be found here:
http://www.a2zdotnet.com/View.aspx?id=66
The problem w/ that is that it does resize your UI. So this next link takes the previous solution and resizes back to the original size when it's done. This does work, although I can't help but to think there's likely a more elegant solution out there somewhere:
http://www.slickthought.net/post/2009/05/26/Visual-Tree-Printing-in-WPF-Applications.aspx
Slickthought.net domain is defunct. Wayback Machine to the rescue.
https://web.archive.org/web/20130603071346/http://www.slickthought.net/post/2009/05/26/Visual-Tree-Printing-in-WPF-Applications.aspx
<Button Content="Print" Command="{Binding Path=PrintCommand}" CommandParameter="{Binding ElementName=ReportPanel}"></Button>
There are two important things to note here. First, I am using a WPF command to start the printing process. You don't have to do it this way, but it lets me tie the presenter to the UI pretty cleanly. The second thing is the CommandParameter. It is passing in a reference to the the ReportPanel. ReportPanel is just a WPF Grid control that wraps the title TextBlock and a Listbox that contains the actual charts. The simplified XAML is:
<Grid x:Name="ReportPanel" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock />
<ListBox/>
</Grid>
With that UI established, lets jump to the code. When the user clicks the Print button, the following WPF command is executed:
this.PrintCommand = new SimpleCommand<Grid>
{
CanExecuteDelegate = execute => true,
ExecuteDelegate = grid =>
{
PrintCharts(grid);
}
};
This is pretty simple stuff. SimpleCommand implements the ICommand interface and lets me pass in some lambda expressions defining the code I want to run when this command is fired. Clearly, the magic happens in the PrintCharts(grid) call. The code shown below is basically the same code you would find in Pankaj’s article with a couple of modification highlighted in red.
private void PrintCharts(Grid grid)
{
PrintDialog print = new PrintDialog();
if (print.ShowDialog() == true)
{
PrintCapabilities capabilities = print.PrintQueue.GetPrintCapabilities(print.PrintTicket);
double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / grid.ActualWidth,
capabilities.PageImageableArea.ExtentHeight / grid.ActualHeight);
Transform oldTransform = grid.LayoutTransform;
grid.LayoutTransform = new ScaleTransform(scale, scale);
Size oldSize = new Size(grid.ActualWidth, grid.ActualHeight);
Size sz = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
grid.Measure(sz);
((UIElement)grid).Arrange(new Rect(new Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight),
sz));
print.PrintVisual(grid, "Print Results");
grid.LayoutTransform = oldTransform;
grid.Measure(oldSize);
((UIElement)grid).Arrange(new Rect(new Point(0, 0),
oldSize));
}
}
All right, what are these modifications? The most obvious is that I am replacing the use of the original this object (which represented the entire application window in the original code) with the Grid control that was passed in as part of the Command. So all of the measurements and transforms are executed using the Grid. The other change is that I have save the original Transform and Size of the Grid as well. The reason is that when you transform the Grid to fit to the printing page, it causes the actual application UI to change as well. This doesn't look so good on your screen, so after sending the Grid to the printer, I transform it back to its original screen layout.

Image within a WPF application displays smaller than when viewed in external viewer

When I display a JPEG in my WPF application (using the following code), it is shown significantly smaller than if I open the JPEG in the Windows Picture Viewer at actual size.
I've drilled into the properties of my ImageSource at runtime and my image has:
a DPI of 219
a Height of 238.02739726027397
a Width of 312.54794520547944
a PixelHeight of 543
and a PixelWidth of 713
When I use a screen ruler to measure the WPF display of the image, I get approx. 313x240 pixels (which if I could positiont the ruler perfectly would probably be equal to the Width and Height that the ImageSource is reporting.).
My gut tells me this has something to do with WPF's use of device independent units (instead of pixels) but I can't make sense of it, and I still need to know how to display the image at the 'actual' size of 543x713 in my application.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<StackPanel>
<Image Source="Image15.jpg" Stretch="None" />
</StackPanel>
</Window>
Thanks Mark! I did some Googling based on your info and found this article that provided a solution to get the result I wanted. This is starting to make sense now...
Edit: Linkrot. Pasting the critical text from the article here for reference....
<Image Source=”{Binding …}”
Stretch=”Uniform”
Width=”{Binding Source.PixelWidth,RelativeSource={RelativeSource Self}}”
Height=”{Binding Source.PixelHeight,RelativeSource={RelativeSource Self}}” />
Here we’ve set Stretch to Uniform and bound the Width and Height to the PixelWidth and >PixelHeight of the Source, effectively ignoring DPI. The image however will not be pixel >perfect, even when using SnapToDevicePixels (which simply snaps the borders, not pixels >within the image). However, WPF in 3.5 SP1 will support a NearestNeighbor >BitmapScalingMode, which should correct this.
Use a DPI of 96. WPF is scaling your image based on the size in inches, while the image viewer is displaying pixels. On most Windows systems, the screen resolution is assumed to be 96 DPI, so using that in your image will result in a one-to-one translation.
Alternatively, you could extend Image and implement MeasureOverride and ArrangeOverride to change the effect of the image's DPI:
class DpiAgnosticImage : Image
{
protected override Size MeasureOverride(Size constraint)
{
var bitmapImage = Source as BitmapImage;
var desiredSize = bitmapImage == null
? base.MeasureOverride(constraint)
: new Size(bitmapImage.PixelWidth, bitmapImage.PixelHeight);
var dpiScale = MiscUtil.GetDpiScale(this);
desiredSize = new Size(desiredSize.Width / dpiScale.Width, desiredSize.Height / dpiScale.Height);
desiredSize = ImageUtilities.ConstrainWithoutDistorting(desiredSize, constraint);
if (UseLayoutRounding)
{
desiredSize.Width = Math.Round(desiredSize.Width);
desiredSize.Height= Math.Round(desiredSize.Height);
}
return desiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
return new Size(Math.Round(DesiredSize.Width), Math.Round(DesiredSize.Height));
}
}
Use it in xaml as if it were an Image:
<Grid>
<local:DpiAgnosticImage
Stretch="None"
Source="{Binding ViewImage}">
<Image.RenderTransform>
<ScaleTransform
x:Name="SomeName"/>
</Image.RenderTransform>
</local:DpiAgnosticImage>
</Grid>
Flaws to above code (that I know of):
Ignores Stretch
Assumes Source is a BitmapImage
=== Edit - Will's comment suggests he would like to know what is in GetDpiScale()
public static Size GetDpiScale(Visual visual)
{
var source = PresentationSource.FromVisual(visual);
var dpiScale = new Size(
source.CompositionTarget.TransformToDevice.M11,
source.CompositionTarget.TransformToDevice.M22);
return dpiScale;
}
This is the result of the .jpg file itself specifying the DPI - WPF is simply obeying. Here is a forum post detailing the problem with some solutions:

Is it possible to start a video playing in a tooltip in Silverlight?

In Silverlight a tooltip can have as many elements in it as you want.
However, it doesn't receive focus so you can't have user interactivity in it.
Could you, though, start a video playing as soon as the tooltip opens and have the video stop as soon as the tooltip closes?
This is my first answer on Stack Overflow so I ask for your good humor.
I think you could run your video in the tooltip by using a video brush.
Here's some code I used to paint a fire video on the bar in the chart that represented heating with corn. ( long story) right here, you can see it is set to the fill of an ellipse.
#region video brush setup
protected void setupVideo()
{
VideoBrush _vb;
MediaElement mevideo;
_vb = new VideoBrush();
mevideo = new MediaElement();
mevideo.SetValue(Grid.NameProperty, "video");
Uri videoUri = new Uri("http://www.faxt.com/videos/ezburnboilerfire.wmv", UriKind.Absolute);
mevideo.Source = videoUri;
mevideo.Visibility = Visibility.Collapsed;
mevideo.MediaEnded += new RoutedEventHandler(me_MediaEnded);
MediaRoot.Children.Add(mevideo);
_vb.SetSource(mevideo);
Ellipse el = new Ellipse();
el.Width = 100;
el.Height = 100;
el.Fill = _vb;
MediaRoot.Children.Add(el);
}
You could do it with a VideoBrush as suggested by BPerreault, but you could also just set Tooltip.Content to a MediaElement.
That is because the Content property of Tooltip inherits from ContentControl and the Content property of a ContentControl can be any type of object, such as a string, a UIElement, or a DateTime. When Content is set to a UIElement (like MediaElement), the UIElement is displayed in the ContentControl. When Content is set to another type of object, a string representation of the object is displayed in the ContentControl. (from documentation)
It should be something like this:
<TextBlock x:Name="myText" Text="MouseOver and you'll get a ToolTip!">
<ToolTipService.ToolTip>
<MediaElement x:Name="myVideo" Source="Butterfly.wmv" Width="300" Height="300" />
</ToolTipService.ToolTip>
</TextBlock >

Resources