I have a ContentPresenter and i try to scale the Content = ""(FrameworkElement)`.
Because if the Content.Size is to big it get cut off. I prepared these pictures to explain my problem.
We start from here. As you can see there is a part missing (yellow Panel is cuted).
So what I tried was ScaleTransform.
(XAML)
<ContentPresenter Content="SomeContent"
Grid.Row="1"
Grid.Column="0">
<ContentPresenter.RenderTransform>
<ScaleTransform ScaleX="0.75" ScaleY="0.75"></ScaleTransform>
</ContentPresenter.RenderTransform>
</ContentPresenter>
So after I added the ScaleTransform I got this result.
And obviously my Content got scaled, but its still cutted.
So I stopped to try it in XAML and this is what came out in c# .
public static FrameworkElement ScaleContent(FrameworkElement element, Size space)
{
ScaleTransform scaleTransform = new ScaleTransform();
double height = element.ActualHeight;
double width = element.ActualWidth;
if (height == 0 || width == 0)
{
element.Measure(new Size(space.Width,
space.Height));
element.Arrange(new Rect(0, 0, element.DesiredSize.Width, element.DesiredSize.Height));
height = element.ActualHeight;
width = element.ActualWidth;
}
double scaleWidth = space.Width / width;
double scaleHeight = space.width / height;
scaleTransform.ScaleX = scaleWidth;
scaleTransform.ScaleY = scaleHeight;
element.RenderTransform = scaleTransform;
return element;
}
And this is what came out.
To let you see the complete Example. I saved a Picture in a bigger Format, where the Content fits.
So what do you think is the problem ?
Thank you !
Related
I have a problem with image cropping. When I try to crop image with rectangle the cropping area is set to some strange value.
Here is xaml code:
<Grid Name="GridImage">
<Image Name="LoadedImage" SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="HighQuality">
</Image>
<Canvas x:Name="ImageCanvas" ClipToBounds="True">
<Rectangle x:Name="SelectionRectangle" Stroke="LightBlue" Fill="#220000FF" Visibility="Collapsed" />
</Canvas>
</Grid>
and here how I crop image:
var imagePosition = LoadedImage.TransformToAncestor(GridImage).Transform(new Point(0, 0));
Rect rect1 = new Rect(Math.Max(Canvas.GetLeft(SelectionRectangle) - imagePosition.X, 0), Math.Max(Canvas.GetTop(SelectionRectangle) - imagePosition.Y, 0), SelectionRectangle.Width, SelectionRectangle.Height);
var ratioX = LoadedImage.Source.Width / LoadedImage.ActualWidth;
var ratioY = LoadedImage.Source.Height / LoadedImage.ActualHeight;
Int32Rect rcFrom = new Int32Rect
{
X = (int)(rect1.X * ratioX),
Y = (int)(rect1.Y * ratioY),
Width = (int)(rect1.Width * ratioX),
Height = (int)(rect1.Height * ratioY)
};
try
{
BitmapSource bs = new CroppedBitmap((BitmapSource)LoadedImage.Source, rcFrom);
LoadedImage.Source = bs;
SetImageStretch(LoadedImage);
SetElementVisibility(Visibility.Hidden, Visibility.Visible, SelectionRectangle);
}
private void SetImageStretch(Image image)
{
if (image.Source.Width > image.ActualWidth || image.Source.Height > image.ActualHeight)
image.Stretch = Stretch.Uniform;
else image.Stretch = Stretch.None;
}
Does anybody know how to fix that?
Here how it looks before cropping:
How it looks after cropping:
Most likely the problem is with image resolution, e.g. 300 dpi, versus screen resolution (most likely 96 dpi). Have you checked image's PixelWidth and PixelHeight?
I am setting the ImageSource of an Image like this:
Stream imageStreamSource = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
TiffBitmapDecoder decoder = new TiffBitmapDecoder(imageStreamSource,BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
BitmapSource bmSrc = decoder.Frames[0];
bmSrc.Freeze();
ImageSource = bmSrc;
The Image uses a ScaleTransform (LayoutTransform) in a Scrollviewer.
LayoutTransform is needed to update the ScrollViewers content size.
I want to scale the image to the size of the parent of the scrollviewer (bounds):
double horizontalAspectRatio = gridBounds.Width / image.Width;
double verticalAspectRatio = gridBounds.Height / image.Height;
if (horizontalAspectRatio > verticalAspectRatio) {
scaleTransformImage.ScaleX = scaleTransformImage.ScaleY = verticalAspectRatio;
MessageBox.Show("to height");
} else {
scaleTransformImage.ScaleX = scaleTransformImage.ScaleY = horizontalAspectRatio;
MessageBox.Show("to width");
}
After doing that an InvalidOperationException is thrown and it says that measuring the image's layout requires the DesireSize not to be NaN.
I tried to measure and arrange the image manually like this:
image.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
image.Arrange(new Rect(0d, 0d, gridBounds.Width, gridBounds.Height));
But it doesn't seem to have an effect.
I am just starting out with Transforms and do not have a lot of knowledge yet....
As long as you did not explicitly set the Image control's Width and Height properties, their values will be NaN, and the aspect ratio calculation will fail:
double horizontalAspectRatio = gridBounds.Width / image.Width;
double verticalAspectRatio = gridBounds.Height / image.Height;
You may instead use the Image's ActualWidth and ActualHeight, or if it is not yet laid out, the Width and Height of its Source:
double horizontalAspectRatio = gridBounds.Width / image.Source.Width;
double verticalAspectRatio = gridBounds.Height / image.Source.Height;
I'm working on a project in C# windows forms where I'm using pictureboxes to display multiple tachometers.
Each picturebox consists of a background image and an image. The background image is static, but the image (displaying the actual indicator) is rotated and updated a couple of times per second.
Background image: http://i.stack.imgur.com/FBsKI.jpg
Fron image: http://i.stack.imgur.com/T0nJU.jpg
It works well as long as I have one or two tachometers running simultaneously, but when I add more it starts to lag a lot.
Here is the code I use for updating the picturebox:
private void timer4_Tick(object sender, EventArgs e)
{
Bitmap image = RotateImage(indicatorImg, i++, false, true, Color.Transparent);
pictureBox4.Image = image;
}
The RotateImage function I'm using (Credit to RenniePet at https://stackoverflow.com/a/16027561/3660713):
public static Bitmap RotateImage(Image inputImage, float angleDegrees, bool upsizeOk,
bool clipOk, Color backgroundColor)
{
lock (lockObject)
{
// Test for zero rotation and return a clone of the input image
if (angleDegrees == 0f)
return (Bitmap)inputImage.Clone();
// Set up old and new image dimensions, assuming upsizing not wanted and clipping OK
int oldWidth = inputImage.Width;
int oldHeight = inputImage.Height;
int newWidth = oldWidth;
int newHeight = oldHeight;
float scaleFactor = 1f;
// If upsizing wanted or clipping not OK calculate the size of the resulting bitmap
if (upsizeOk || !clipOk)
{
double angleRadians = angleDegrees * Math.PI / 180d;
double cos = Math.Abs(Math.Cos(angleRadians));
double sin = Math.Abs(Math.Sin(angleRadians));
newWidth = (int)Math.Round(oldWidth * cos + oldHeight * sin);
newHeight = (int)Math.Round(oldWidth * sin + oldHeight * cos);
}
// If upsizing not wanted and clipping not OK need a scaling factor
if (!upsizeOk && !clipOk)
{
scaleFactor = Math.Min((float)oldWidth / newWidth, (float)oldHeight / newHeight);
newWidth = oldWidth;
newHeight = oldHeight;
}
// Create the new bitmap object. If background color is transparent it must be 32-bit,
// otherwise 24-bit is good enough.
Bitmap newBitmap = new Bitmap(newWidth, newHeight, backgroundColor == Color.Transparent ?
PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb);
newBitmap.SetResolution(inputImage.HorizontalResolution, inputImage.VerticalResolution);
// Create the Graphics object that does the work
using (Graphics graphicsObject = Graphics.FromImage(newBitmap))
{
graphicsObject.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphicsObject.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
graphicsObject.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
// Fill in the specified background color if necessary
if (backgroundColor != Color.Transparent)
graphicsObject.Clear(backgroundColor);
// Set up the built-in transformation matrix to do the rotation and maybe scaling
graphicsObject.TranslateTransform(newWidth / 2f, newHeight / 2f);
if (scaleFactor != 1f)
graphicsObject.ScaleTransform(scaleFactor, scaleFactor);
graphicsObject.RotateTransform(angleDegrees);
graphicsObject.TranslateTransform(-oldWidth / 2f, -oldHeight / 2f);
// Draw the result
graphicsObject.DrawImage(inputImage, 0, 0);
}
return newBitmap;
}
}
Any ideas on how to improve performance? Is there a better way to do this?
I would create a static list of images for each angle of degrees required on startup.
You could then reference the correct index in the list and use that pre rotated image.
Thank you for your help!
What I did was that I added a WPF control to my Winforms project. The WPF control contains the two images and simply contains code for rotating the front image:
C#:
public void Rotate(float deg)
{
RotateTransform rt = new RotateTransform(deg);
image2.RenderTransform = rt;
}
XAML:
<UserControl x:Class="Project.WPF_imgControl"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Image HorizontalAlignment="Left" Name="image1" Stretch="Fill" VerticalAlignment="Top" Source="/Project;component/Images/Tachometer.png" />
<Image RenderTransformOrigin =".5,.5" HorizontalAlignment="Left" Name="image2" Stretch="Fill" VerticalAlignment="Top" Source="/Project;component/Images/Pointer_80%25.png" />
</Grid>
Now I just have to find out why the image looks jagged in the WPF component compared to my standard form.
Edit: RenderOptions.BitmapScalingMode="HighQuality" did the trick :)
//Jocke
Hi I have an empty Panel, and want to add items to it at runtime. I also want to make sure that panel's Width doesn't extend the window's Width, If so I want o add items in other panel.
In my XAML :
<Border Grid.Row="1" Margin="5,0,5,0" Padding="2" Background="LightYellow"
BorderBrush="SteelBlue" BorderThickness="1" CornerRadius="3" >
<StackPanel Name="legendsPanel" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<DockPanel Name="legendsPnl1">
</DockPanel>
<DockPanel Name="legendsPnl2">
</DockPanel>
</StackPanel>
</Border>
In code behind :
int legendSize = 30; // W for 1 Legend
private void AddLegend(string title, double value, Brush clr)
{
MessageBox.Show("SP - " + legendsPanel.ActualHeight.ToString() + " DP - " + legendsPnl1.ActualHeight.ToString()); // 0, 0
Rectangle rect = new Rectangle();
rect.Width = 10; rect.Height = 10; rect.Fill = clr;
Label lbl = new Label();
lbl.Content = title + value;
/*
* Here, before adding to legendsPnl1 I want to make sure that it's W = (W + legendSize) <= legendsPanelWidth
* legendsPanel - > StackPanel i.e. Parent of legendsPnl1
* Width of legendsPanel, Grid, Window all are in wither Stretch mode or free Width - Auto or *,
* so can't make out how to get the W of them. At runtime, their W, ActualWidth are shown either 0, NaN, null
*/
legendsPnl1.Children.Add(rect);
legendsPnl1.Children.Add(lbl);
}
How to find legendsPnl1 panel hasenough space to add both elements (of size legendSize var ) , else add the elements in legendsPnl1 panel ????
Thanks
I'm drawing a cell wall on a map and it is the wrong size. I checked the height of the image in debugger and it was the same size as the cellSize which is correct, but visually it appears shorter and the start of it is not drawn in the correct place. Like it is being drawn over top of at both ends. When I change the Height property of the image it does not make the image bigger. Is that not how to set the image size, with Height and Width?
Here is the code.
private void calculateWall(int wall, int cell)
{
int[] mapData = this.getMapData(cell);
int startOfCol = mapData[0];
int endOfCol = mapData[1];
int startOfRow = mapData[2];
int endOfRow = mapData[3];
CellSide rightSide = this.getCells()[cell].myRightWall;
CellSide bottomSide = this.getCells()[cell].myBottomWall;
float thickness = myMap.myCellSize * (float)0.1;
Math.Round(thickness, 0);
int newThickness = Convert.ToInt32(thickness);
float height = myMap.myCellSize * (float)0.2;
Math.Round(height, 0);
int newHeight = Convert.ToInt32(height);
if (rightSide.hasWall == 1)
{
Image verticalWall = new Image();
// Create source.
BitmapImage bi = new BitmapImage();
// BitmapImage.UriSource must be in a BeginInit/EndInit block.
bi.BeginInit();
bi.UriSource = new Uri("verticalWall.jpg", UriKind.RelativeOrAbsolute);
bi.EndInit();
// Set the image source.
verticalWall.Source = bi;
verticalWall.Width = newThickness;
verticalWall.Height = myMap.myCellSize;
verticalWall.SetValue(Canvas.TopProperty, Convert.ToDouble(startOfRow));
verticalWall.SetValue(Canvas.LeftProperty, Convert.ToDouble(endOfCol - (newThickness / 2)));
verticalWall.IsHitTestVisible = false;
this.view.pbxMap.Children.Add(verticalWall);
}
Regarding your last questions in the comments.
Ok, here's a screenshot of how I structured your project: Screenshot
Now, wherever you use any of the pictures,you'll have to add the folder to that path:
Source="exit.png"
Will become:
Source="/Images/exit.png"
This:
<Page.Background>
<ImageBrush ImageSource="marbleBackground.jpg"/>
</Page.Background>
Will become:
<Page.Background>
<ImageBrush ImageSource="/Images/marbleBackground.jpg"/>
</Page.Background>
And so on ... problem solved :)
When you programmatically draw an image in WPF, its stretch property is set to uniform by default. Add this line:
verticalWall.Stretch = Stretch.Fill;
Now it will size correctly even if it does not stretch uniformly.