WPF Image Uniform to None - wpf

I want to make my Image to stretch uniform when its size is smaller than original. If there is enough space, I don't want to stretch the image. I'am using the Image control:
<Image Name="ImageBox" Stretch="Uniform" SizeChanged="ImageBox_SizeChanged_1"/>
My event handler:
private void ImageBox_SizeChanged_1(object sender, SizeChangedEventArgs e)
{
if (OriginalImageSize == null) return;
if (ImageBox.RenderSize.Width > OriginalImageSize.Width && ImageBox.RenderSize.Height > OriginalImageSize.Height)
ImageBox.Stretch = Stretch.None;
else
ImageBox.Stretch = Stretch.Uniform;
}
When I load image it's ok, next I resize the window so there is enough space to show whole image and scaling is not necessary, image starts blinking (from Stretch.None to Stretch.Uniform I guess).
Any advice?

I think you are looking for the StretchDirection property. You might want to try
<Image Name="ImageBox" Stretch="Uniform" StretchDirection="DownOnly"/>
without your event.

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

border.background image fixed

I'm starting in WPF and I'm developing an application that has a background (.png) applied as follow:
'<Border.Background>'
'<ImageBrush ImageSource="final.png" AlignmentY="Top"/>'
'</Border.Background>'
No problems until here. My problem is because I have an Expander in my window and, in my collapsed method I make an alteration in window size, as shown:
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
windowFrame.Height = 400;
}
I would like that image, used as background, stay fixed after expander action, ie, when the window is expanded the background is shown full, and when is Collapsed the background shall be cutted.
In analogy to c#, is similar like BackgroundImageLayout = none. You can redim your WindowsForm and the image will be showing according to windows size.
I need finish this job and I hope find my answer here.
Thanks a lot!
<ImageBrush ImageSource="final.png" AlignmentY="Top" Stretch="None"/>
The default value for Stretch is "Fill"

WPF - Maximizing borderless window by taking in account the user taskbar

I am creating a WPF window with a custom chrome, so I setted ResizeMode="NoResize" and WindowStyle="None" to implement my own chrome. However, there is an issue while maximizing the borderless window: it takes the whole screen.
I found the following trick to fix part of the issue:
http://chiafong6799.wordpress.com/2009/02/05/maximizing-a-borderlessno-caption-window/
This successfully restrain the window size to prevent from covering a taskbar. However, if the user have his taskbar positionned at the left or at the top, this won't work, as the window is at position 0,0.
Is there any way to retrieve more accurately the available area, or to query the user taskbar's position so I can position the maximized window accordingly?
I had a quick play around and it seems that setting the Windows Left and Top properties is ignored when setting WindowState.Maximized with a borderless form.
One workaround would be to ignore the WindowState functions and create your own Maximize/Restore functions
Rough example.
public partial class MainWindow : Window
{
private Rect _restoreLocation;
public MainWindow()
{
InitializeComponent();
}
private void MaximizeWindow()
{
_restoreLocation = new Rect { Width = Width, Height = Height, X = Left, Y = Top };
System.Windows.Forms.Screen currentScreen;
currentScreen = System.Windows.Forms.Screen.FromPoint(System.Windows.Forms.Cursor.Position);
Height = currentScreen.WorkingArea.Height;
Width = currentScreen.WorkingArea.Width;
Left = currentScreen.WorkingArea.X;
Top = currentScreen.WorkingArea.Y;
}
private void Restore()
{
Height = _restoreLocation.Height;
Width = _restoreLocation.Width;
Left = _restoreLocation.X;
Top = _restoreLocation.Y;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MaximizeWindow();
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
Restore();
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
DragMove();
}
base.OnMouseMove(e);
}
}
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="74.608" Width="171.708" ResizeMode="NoResize" WindowStyle="None">
<Grid>
<Button Content="Max" HorizontalAlignment="Left" Margin="0,29,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
<Button Content="Restore" HorizontalAlignment="Left" Margin="80,29,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_2"/>
</Grid>
</Window>
Obviously you will want to clean this code up, but it seems to work wherever the Taskbar is located, However you may need to add some logic to get the correct Left, Top if the users font DPI is larger than 100%
Another way to do this is by handling the WM_GETMINMAXINFO Win32 message. The code here shows how to do that.
Note that there are a few things that I would do differently, such as returning IntPtr.Zero instead of (System.IntPtr)0 in WindowProc and making MONITOR_DEFAULTTONEAREST a constant. But that's just coding style changes, and doesn't affect the net result.
Also make sure to pay attention to the update where the WindowProc is hooked during the SourceInitialized event instead of OnApplyTemplate. That's the better place to do it. If you're implementing a class derived from Window, then another option is to override OnSourceInitialized to hook the WindowProc instead of attaching to the event. That's what I normally do.

can't render the buttons on the entire display in SLXNA

I have a SLXNA game and am trying to display a menu on the SL part. The problem is that the buttons seem to display but not entirely. In a certain part of the screen the buttons just stop to be drawn(i can see tje button till a point where it looks like it gets 'cut off'). What could be the problem?
note: the app is in landscape and is a default SL/XNA template
here is the code(i will only display the code that interests us):
XAML :
</Grid.RowDefinitions>
<!-- Toggles the visibility of the ColorPanel -->
<Button VerticalAlignment="Top" BorderBrush="DarkRed" Foreground="DarkRed" Margin="1,0,-1,0">pause</Button>
<Button VerticalAlignment="Bottom" BorderBrush="DarkRed" Foreground="DarkRed" Margin="1,0,-1,0">change</Button>
<!-- Arrange buttons in a horizontal line by using StackPanel -->
</Grid>
i declare :
UIElementRenderer elementRenderer;
public GamePage()
{
// some typical code.....
LayoutUpdated += new EventHandler(GamePage_LayoutUpdated);
}
void GamePage_LayoutUpdated(object sender, EventArgs e)
{
// Create the UIElementRenderer to draw the XAML page to a texture.
// Check for 0 because when we navigate away the LayoutUpdate event
// is raised but ActualWidth and ActualHeight will be 0 in that case.
if (ActualWidth > 0 && ActualHeight > 0 && elementRenderer == null)
{
elementRenderer = new UIElementRenderer(this, (int)ActualWidth, (int)ActualHeight);
}
}
private void OnDraw(object sender, GameTimerEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.LightGoldenrodYellow);
// draw some 3d objects
elementRenderer.Render();
spriteBatch.Begin();
spriteBatch.Draw(elementRenderer.Texture, Vector2.Zero, Color.White);
spriteBatch.End();
// TODO: Add your drawing code here
}
I've seen this before. I think the problem here is that because your page is Landscape it actually gets created first by Silverlight as Portrait and then "rotated" around - leaving your UIElementRenderer with the wrong size and everything looking wrong.
Try recreating your UIElementRenderer in response to the OrientationChanged event. (i.e. call the code you have in the GamePage_LayoutUpdated method again)

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:

Resources