In WPF, view a portion of an image - wpf

We have an image where we create view box coordinates that are topleft/bottom right points within the image that are setup to allow for viewing portions of an image at different times in our application. In WPF, how do we load an image, and with topleft/bottom right points within that image, only show the portion of the image within that view box?

You can do this with a CroppedBitmap:
<Image>
<Image.Source>
<CroppedBitmap Source="<path to source image>" SourceRect="20,20,50,50"/>
</Image.Source>
</Image>
This will display the 50x50 region of the image starting at position (20,20)

Using a RenderTransform with a Clip works even better, because the CroppedBitmap is kinda immutable:
<Image x:Name="MyImage">
<Image.RenderTransform>
<TranslateTransform X="-100" Y="-100" />
</Image.RenderTransform>
<Image.Clip>
<RectangleGeometry Rect="0 0 250 250" />
</Image.Clip>
</Image>
This will display the image at offset (100, 100) with a size of (150, 150), so don't forget the rect must include the startoffsets.
Here's a method to calculate it in code:
public static void ClipImage(System.Windows.Controls.Image image, Rect visibleRect)
{
image.RenderTransform = new TranslateTransform(-visibleRect.X, -visibleRect.Y);
image.Clip = new RectangleGeometry
{
Rect = new Rect(
0,
0,
visibleRect.X + visibleRect.Width,
visibleRect.Y + visibleRect.Height)
};
}

It seems to me that you can make the image control a part of the viewbox as shown below:
<Viewbox Name="vBox" Stretch="None" HorizontalAlignment="Left"
VerticalAlignment="Top" Height="50" Width="50">
<Image Name="ClippedImage"
Source="{Binding NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}"
Stretch="None" />
</Viewbox>
This will give you a view box 50x50. obviously you can change the height and width to suit your needs. I use a scrollviewer to pan around the smaller viewbox.

Related

How to adjust a canvas that child with position (-1,-1) is displayed completely

NEW INFORMATION!!!
Meanwhile i have found a solution but there is a new problem.
The solution is to set the margin of the canvas in code-behind to a new object of type Thickness with top and left 1 or 2.
But the canvas is lying on a tabcontrol.
When i switch between tabs or make a mousedown on the canvas the margin is lost.
I'm working with VS2015 on a WPF-application and have a very curious problem.
I got in one of my WPF windows a canvas as parent for some child elements.
One of these elements is a rectangle which shall show the user the size of a DIN-A4 page.
It is added in code-behind to the children collection of the canvas.
Normally i would place that rectangle at position (0,0).
But because of some problems i have to trick and set the position to (-1,-1) like that:
public static System.Windows.Shapes.Rectangle GetRectangle(double top, double left, double width, double height)
{
var rectangle = new System.Windows.Shapes.Rectangle();
System.Windows.Controls.Canvas.SetLeft(rectangle, -1);
System.Windows.Controls.Canvas.SetTop(rectangle, -1);
rectangle.Width = Math.Round(GetSizeInPoint(width)) + 2;
rectangle.Height = Math.Round(GetSizeInPoint(height));
rectangle.StrokeThickness = 1;
rectangle.Stroke = System.Windows.Media.Brushes.Red;
return rectangle;
}
But the result of it is that just a small part of the top and left border of the rectangle can be seen.
Do i have a chance to "move" the canvas so that the rectangle is displayed completely?
Hereby another important problematic point is that the Grid named "grdProtocolDesigner" can be serialized to XML and saved in the database.
So a complete restructuring would be a big problem.
Here the relevant part of my XAML including the canvas:
<ContentPresenter x:Name="protocolContainer"
Grid.Row="1"
Grid.Column="0">
<ContentPresenter.Content>
<Grid x:Name="grdProtocolDesigner"
Grid.Row="1"
Grid.Column="0">
<ScrollViewer>
<!--Important! The background of this panel has to be "Transparent" so that drag'n'drop-events can work.
Otherwise the events are not fired.-->
<Canvas x:Name="protocolDesignerPanel"
AllowDrop="True"
Visibility="{Binding DesignerPanelsVisibility}"
Width="2200" Height="4000"
MouseEnter="designerpanel_MouseEnter"
MouseLeave="designerpanel_MouseLeave"
MouseDown="designerpanel_MouseDown"
MouseMove="designerpanel_MouseMove"
MouseUp="designerPanel_MouseUp">
<Canvas.RenderTransform>
<ScaleTransform x:Name="scaleProtocolLayout"/>
</Canvas.RenderTransform>
<Canvas.Background>
<ImageBrush ImageSource="../../pictures/CanvasBackground.png"
TileMode="Tile"
Stretch="None"
Viewport="0, 0, 10, 10"
ViewportUnits="Absolute" />
</Canvas.Background>
</Canvas>
</ScrollViewer>
</Grid>
</ContentPresenter.Content>
</ContentPresenter>

In WPF what is the proper method for drawing a line and PNGs on a bitmap that is attached to a canvas?

I don't do that much with WPF anymore and I always seem at a loss for the most basic things. I've tried the Googles but they're not helping.
I've got a canvas (maybe I shouldn't use a canvas?) and I want to attach an image. The only way that I could find to do this was like so:
<Canvas Grid.Column="2" HorizontalAlignment="Right" Height="822" VerticalAlignment="Top" Width="1198" Name="MainCanvas">
<Canvas.Background>
<ImageBrush ImageSource="/MapDesignModule;component/MapFrame.bmp" Stretch="None" AlignmentY="Top" AlignmentX="Right"/>
</Canvas.Background>
</Canvas>
Now, I need to draw lines on the image attached to the canvas (later I will also have to place transparent PNGs, or BMPs with white set to Alpha 0, on the image as well).
In the past I would get a writeablebitmap from the image.source but you apparently can't do that from an ImageBrush?
What is the 'proper way' to put an image on the screen and draw and blit images onto it?
Just put multiple Image and Line elements on top of each other in a common Panel, e.g. a Canvas:
<Canvas>
<Image Source="/MapDesignModule;component/MapFrame.bmp" />
<Image Source="/MapDesignModule;component/Overlay.png" />
<Line X1="100" Y1="100" X2="200" Y2="200" Stroke="Black" />
</Canvas>
You could also assign a name to the Canvas
<Canvas x:Name="canvas">
to access it in code behind:
canvas.Children.Add(new Image
{
Source = new BitmapImage(new Uri(
"pack://application:,,,/MapDesignModule;component/MapFrame.bmp"))
});
canvas.Children.Add(new Line
{
X1 = 100,
Y1 = 100,
X2 = 200,
Y2 = 200
});
In case you later want to add multiple shape overlays, you may consider using an ItemsControl with an appropriate view model, e.g. as shown here: https://stackoverflow.com/a/40190793/1136211

Xaml, wpf image position and crop issue

I have images of 600px width and 600px height. we have three sizes of circles. all have the center in the middle. Some have reflection as shadow beneath it. I would like to crop the image for display purposes.
So the largest circle as shown above has a diameter of about 500 pixels, but the medium and small ones have less. I know in the code which size I have of object type Product. Because of the size differences I have to place them differently and used three placeholder images for it, like this:
<Image x:Name="imgCoinHolderSmall"
HorizontalAlignment="Center"
Margin="0,495,0,0"
VerticalAlignment="Top"
Stretch="Fill"
Width="200"
Height="200"/>
<Image x:Name="imgCoinHolderMedium"
HorizontalAlignment="Center"
Margin="0,510,0,0"
VerticalAlignment="Top"
Stretch="Fill"
Width="200"
Height="200"/>
<Image x:Name="imgCoinHolderLarge"
HorizontalAlignment="Center"
Margin="0,520,0,0"
VerticalAlignment="Top"
Stretch="Fill"
Width="200"
Height="200"/>
So can I change the properties of the image such that it does not display the red part of this screenshot:
By the way, I do not display the images on their original size (as you can see at the xaml code) I set the width to 200. It is just a display thing, I do not have to store the new image. I would like to do it on the fly, preferably by setting image properties in the xaml. (for all three sizes of circles)
Is using the CroppedBitmap the best approach? http://msdn.microsoft.com/en-us/library/ms752345.aspx it is for windows rt by the way.
One option would be to use a clipping mask:
<Image Source="MyImage.jpg">
<Image.Clip>
<RectangleGeometry Rect="10,10,80,80"></RectangleGeometry>
</Image.Clip>
</Image>
The rect structure takes X,Y, Width and Height values that you have to set depending on your image.

Get scaling factor of an image when the image goes out of grid

I have a grid that contains an image.
<Grid Name="grid1">
<Image Name="image1" Stretch="None" Source="C:\Users\User\Desktop\image.jpg"/>
</Grid>
If the size of image was greater than the size of the grid, I want to scale it manually by render transform to fit the grid. I don't want to use Stretch="Fill" because I need the scale factor.
Is there any way to detect the situation that an UIElement goes out of view?
I need your help.
You could simply set the Stretch property to Uniform (or perhaps Fill) and calculate the scaling factor from the ActualWidth of the Image and the Width of the ImageSource, whenever you need it. The sample below does the calculation in a SizeChanged handler, but it could be anywhere else.
<Image Name="image1" Stretch="Uniform" Source="C:\Users\User\Desktop\image.jpg"
SizeChanged="ImageSizeChanged"/>
The calculation looks like this:
private void ImageSizeChanged(object sender, SizeChangedEventArgs e)
{
var scale = image1.ActualWidth / image1.Source.Width;
}
As Uniform is the default value of the Stretch property, you wouldn't have to set it at all.
I'm not sure why you would want to rescale your image manually when WPF can do it for you...
Instead of putting the image straight into the grid, use a Viewbox control:
<Grid Name="grid1">
<Viewbox>
<Image Name="image1" Stretch="None" Source="C:\Users\User\Desktop\image.jpg"/>
</Viewbox>
</Grid>
The Viewbox will automatically scale the picture to fit inside the grid...

Silverlight - polygon scaling and clipping

I have a Canvas, to which I've added several thousand polygons.
I would like to be able to zoom in (which I'm doing via a ScaleTransform.
However I've been trying to use a Canvas.Clip as well to only draw a portion of the Canvas, but as soon as the ScaleTransform values are changed, the clipping stops working...
<Canvas Grid.Row="1" Margin="10" x:Name="cnvMain" Background="Transparent" >
<Canvas.Clip>
<RectangleGeometry x:Name="CanvasClip" Rect="0, 0, 300, 300"/>
</Canvas.Clip>
<Canvas.RenderTransform>
<ScaleTransform x:Name="CanvasScaleTransform" ScaleX="1" ScaleY="1"></ScaleTransform>
</Canvas.RenderTransform>
</Canvas>
And in my codebehind,
private void slScale_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
CanvasScaleTransform.ScaleX = slScale.Value;
CanvasScaleTransform.ScaleY = slScale.Value;
}
Am I doing anything obviously wrong?
The ScaleTransform (as all other transformations) is applied AFTER every other rendering. That means, first the cliprect is applied, then the scale transform. A solution would be to do the clipping one level higher, by putting another canvas around this one.
Put a border around your canvas and attach the clip region to the border rather than the canvas.

Resources