2D Graphic Matrix Blur - c#

I'm developing an UserControl named Matrix2D to display 2 dimension matrix points in a x,y custom graphic.
Strange thing is, if I set Auto to Width/Height the columns/rows to the Grid that contains this Matrix2D control, like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<elem:Matrix2D x:Name="matrix2"
Grid.Row="0"
Grid.Column="0"
MaximumX="255"
MaximumY="127"
ZoomScale="1" />
</Grid>
I get the expected result (clean pixels):
I have also the expected result if I set HorizontalAlignment="Left" and VerticalAlignment="Top", like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<elem:Matrix2D x:Name="matrix2"
Grid.Row="0"
Grid.Column="0"
MaximumX="255"
MaximumY="127"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ZoomScale="1" />
</Grid>
Although, If I don't set Auto to Grid neither set the Alignment, like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<elem:Matrix2D x:Name="matrix2"
Grid.Row="0"
Grid.Column="0"
MaximumX="255"
MaximumY="127"
ZoomScale="1" />
</Grid>
I get this blur result:
I already test several ways of developing this user control, with always the same result.
This is the current version:
<UserControl x:Class="Matrix2D"
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"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Background="White">
<Image x:Name="populationImg" Stretch="None" />
</Grid>
</UserControl>
In code behind:
I populate a WriteableBitmap and then I set populationImg.Source with it.
Already test the trick of setting RenderOptions.BitmapScalingMode="NearestNeighbor", but the result is approximate, and since this a Scientific Graphic I need that it be accurate. You can check in the corners of this result that is not accurate:
Even more visible in smaller graphic:
Setting RenderOptions.EdgeMode="Aliased" doesn't change anything.
Update
This explains why I have this blur effect:
To properly center an image, the container should have an even width,
height if the image's pixel width, height are even. If the image has
an odd pixel width, height, the containing element should also have an
odd width, height.
from Pixel Snapping in WPF Applications (In #Nicolas Repiquet answer)
Although, it didn't actually helped me to solve my problem. I would like that my user control be independent of its container. Any ideas how to do this?

My guess is that your control position within the parent container is not aligned to the pixel grid (ie, the x and y coordinates of your control on the screen is not a round number of pixels).
Try this :
<elem:Matrix2D SnapsToDevicePixels="true" ... />
UIElement.SnapsToDevicePixels Property
EDIT
"To properly center an image, the container should have an even width, height if the image's pixel width, height are even. If the image has an odd pixel width, height, the containing element should also have an odd width, height. " from Pixel Snapping in WPF Applications

Related

WPF: how to set window height equal to Grid height

Using WPF, I have a Window, that is filled with one Grid. Currently this Grid has one Row and three Columns, each cell filled with the same UserControl.
Future versions might add more Rows to the Grid.
I want to make my window have the size of the Grid.
<Window
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns: ...
...
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="600"
Title="MainWindow" Height="600" Width="600">
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<View:ImageView Grid.Row="0" Grid.Column="0" DataContext="..."/>
<View:ImageView Grid.Row="0" Grid.Column="1" DataContext="..."/>
<View:ImageView Grid.Row="0" Grid.Column="2" DataContext="..."/>
</Grid>
The Window has a Width of 600. The Grid will stretch to this window. It will have a Width about 600. The Grid will have three columns with the same Width, something near 200. The ImageView in each cell will stretch to fill the Cell. Each Cell will have a Width about 200.
This determines the Height of the Cells, and thus the Height of the Grid.
Addition: after comments I don't want to resize the window. After it decided about what it should displays and what sizes the controls should have to display it nicely, there is no need to resize it.
For debugging, to see what happens, I resized the mainwindow, and recorded what happens to the various Height / Width / ActualHeight / ActualWidth values.
I noticed that the Grid is resized, such that its Width exactly fills the Width of the Window. Resizing will also Resize the cells and the ImageViews that are in the Cells.
This fitting is not the case with the Height. I can make the Height window Higher and Lower than the Height of the Grid.
I want to set the Height of the window such that it is exactly around the Grid.
I expected something like:
<Window ... Width = "600" Height = "auto" />
Nope, the height is way large than it should be, somewhere near 1000. The Grid is still about 300 x 600.
<Window ... Width = "600" Height = "*" />
This leads to an exception.
Maybe Binding? Something like this?
<Window ... Width = "600" Height = "{Binding Height, ElementName=MainGrid, Mode=OneWay}"/>
<Window ... Width = "600" Height = "{Binding ActualHeight, ElementName=MainGrid, Mode=OneWay}"/>
Nope, still way to large.
So, how to make sure that the window is exactly around the Grid?
Remove the Height from the window and set SizeToContent to Height. However, this will only set the width and height that you expect initially, not on resizing.
<Window
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns: ...
...
mc:Ignorable="d"
d:DesignHeight="600"
d:DesignWidth="600"
Title="MainWindow"
Width="600"
SizeToContent="Height">
If you want to resize the window like you describe, it would mean to keep the aspect ratio and this not easy to solve without unexpected effects or you would have to restrict resizing to the horizontal direction only, which is not trivial either. You can have a look at these related questions.
Resize a WPF window, but maintain proportions?
WPF Window - Only allow horizontal resize
Of course you could also listen for resize events of the window and do custom processing there, but this approach and also the ones above might harm your user experience and could introduce unwanted effects, so maybe it is more advisable to revisit the layout concept.
As #thatguy says, what you're asking for is difficult to do well. In general you don't want code that attempts to resize a window after a user resizes it because you'll get conflicts between what the user's trying to do and the code wants to do.
That still leaves you with a few options if you don't want your images clipped and want them to fill the window as much as possible, which I think is what you're asking for.
1/ You can maintain the aspect ratios of your images, resize them to fill the screen in one or the other direction, and accept you can get whitespace:
2/ You can stretch your images to fill the available space, and hence always fill the entire window as you resize, but losing the aspect ratio:
Code for these is below. Maintaining aspect ratios:
<Window x:Class="ImageResizeTest.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:ImageResizeTest"
mc:Ignorable="d"
Title="MainWindow" Width="600" Height="110">
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Source="https://upload.wikimedia.org/wikipedia/commons/3/30/Googlelogo.png" />
<Image Grid.Row="0" Grid.Column="1" Source="https://upload.wikimedia.org/wikipedia/commons/3/30/Googlelogo.png" />
<Image Grid.Row="0" Grid.Column="2" Source="https://upload.wikimedia.org/wikipedia/commons/3/30/Googlelogo.png" />
</Grid>
</Window>
Stretching images to fill:
<Window x:Class="ImageResizeTest.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:ImageResizeTest"
mc:Ignorable="d"
Title="MainWindow" Width="600" Height="110">
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Stretch="Fill" Source="https://upload.wikimedia.org/wikipedia/commons/3/30/Googlelogo.png" />
<Image Grid.Row="0" Grid.Column="1" Stretch="Fill" Source="https://upload.wikimedia.org/wikipedia/commons/3/30/Googlelogo.png" />
<Image Grid.Row="0" Grid.Column="2" Stretch="Fill" Source="https://upload.wikimedia.org/wikipedia/commons/3/30/Googlelogo.png" />
</Grid>
</Window>

StackPanel height exceed parent Grid height

I have situation like this:
<UserControl>
<Grid x:Name="fullGrid" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="innerGrid" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="leftSide" VerticalAlignment="Stretch">
<ScrollViewer VerticalAlignment="Stretch">
<ItemsControl/ VerticalAlignment="Stretch">
</ScrollViewer>
</StackPanel>
</Grid>
</Grid>
</UserControl>
Problem is that leftSide stackPanel height is higher than it's parent: innerGrid height.
I was debugging it in Snoop and it seems that StackPanel just ignore it's VerticalAlignment property.
I would like to avoid setting Heigh={Binding ElementName=xxx, Path=ActualHeight} because I have some additional Margins inside, and it break the view.
How can I handle that?
ScrollViewer fills its parent. StackPanel wants to size to its children, and so tells them they have as much space as they want (and then "shrinks to fit").
So, StackPanel tells ScrollViewer it can have all the space in the world, which it happily takes. There is no way to stop this besides doing a binding as you describe or setting an absolute height.
So the simple solution is: remove the StackPanel. Then the ScrollViewer will take up the space the Grid assigns it.

Ellipse position according to grid layout

I'm facing some elements' positioning problem in my wp 8.1 app (XAML/C#). I've read that to create a flexible layout, I should use the grid control.
Well, here is my XAML within the page element:
<Grid Style="{StaticResource LayoutGridCreateProfilStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Height="60" Fill="#F3A8E4F9" ></Rectangle>
<Rectangle Grid.Row="1" Height="40" Fill="Gray"></Rectangle>
<ListBox Grid.Row="2" Height="420" Background="Transparent"/>
<Canvas Grid.Row="3">
<Ellipse Height="100" Margin="0,0,0,0" Width="100" Fill="Black" Canvas.Left="152" Canvas.Top="10"></Ellipse>
</Canvas>
</Grid>
In the XAML designer, I'm actually seeing what I want and it looks like below:
And here is how it looks like on my Nokia Lumia 925:
As you can see, the ellipse is cut and I really cannot figure out how to place it as it should be and actually appears in the designer view.
Maybe somebody could help me with that ?
Thanks in advance !
The problem is that you are running out of height to show all the elements of your grid. The first three rows take up an accumulated height of 540 pixels. If the run time height available if 640 then you only have 100 left over for the last row of your grid.
But your Ellipse is offset 10 pixels from the top of the Canvas and so it needs a total of 110 (10 offset + 100 pixel ellipse height) in order to show fully. If your last row has less than 110 pixels of space then it will be truncated, as indeed it seems to be.

understanding WPF Layout

I got a ListView (wrapped in a ScrollViewer), which resizes itself if the elements inseretd exceed the visible area until the parents Max Height is reached.
The ListView is embedded the following way.
<Window ... SizeToContent="Height">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ScrollViewer x:Name="MyScrollViewer" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListView Name="MyListView"
ItemsSource="{Binding Path=RetrievalStringResults, Mode=OneWay}"
IsSynchronizedWithCurrentItem="True" />
</ScrollViewer>
<DockPanel Grid.Row="0" Grid.Column="1">
...
</DockPanel>
<Expander Grid.Row="1" Grid.ColumnSpan="2">
<DockPanel Height="150" HorizontalAlignment="Stretch">
<TextBox DockPanel.Dock="Top" />
</DockPanel>
</Expander>
<DockPanel Grid.Row="2" Grid.ColumnSpan="2">
... some buttons
</DockPanel>
</Grid>
</Window>
I used SizeToContent because I got a text box on the bottom wrapped in an Expander, which shall expanded on demand, and actually therefore my main window needs to resize. This actually works fine.
The problem is though the ListView's Height isn't the max height on start-up wherefore I got that "auto-resize" effect.
How can I set the ListView's hight to the max of the parents height to avoid this effect?
Another, more general issue. I think avoiding static layout parameters (static values for hight/width) is nice, but I got the feeling I'm loosing some control over my UI controls.
I recognized, resizeing my main window manually in height, it "jumps" by the 150 height of the DockPanel wrapped by the Expander on the bottom, anyway to avoid this?
What is the best pratice in dynamic UI Layout? I explored DockPanel beeing more dynamically in sizing to the surounding contorls than a StackPanel. But I guess thats not everything.
I think your issue is with your Grid definitions
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
...
</Grid>
This means that the top row is going to be twice the size of rows 2 and 3, so the top row will only take up 50% of your space while your bottom two rows each take up 25% space.
If you want the top row to take up all available space, make sure it is the only * size row, and set the other rows to Auto so they will take up whatever space they need.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
...
</Grid>

Handling Different Font Sizes Due To Display Properties in a WPF App?

My group is building an editor-type app in WPF. One thing we noticed is that on my WinXP machine, running with the "windows classic style" theme, the text on buttons is fits fine. However on my friend's machine, who's running with the "windows xp style" theme, the font size is bigger so text on buttons get clipped at the bottom.
Is there a way to handle this nicely, like automatically resizing controls to fit the text?
I hesitate to manually resize the button to fit his layout as anyone else can have totally different settings through the Display Properties and Accessibility Options.
Thanks!
A WPF button will automatically resize to fit the content that it has been given, however it will only do this when it is inside a container that does not enforce size and its size has not been set manually. To prove this mess around with the font size in the following code snippet:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button
Grid.Column="1"
Grid.Row="1"
FontSize="24"
Content="QWERTY"/>
</Grid>
I guess that your buttons haven't resized because you have constrained them. To fix this you need to decide how you want them to resize (which can be very complicated when elements would overlap if they just grew blindly) and if none of the supplied panel types perform the growth behaviour that you are looking for then you may need to write your own that does.
Have you hardcoded element sizes using Width and Height properties? In WPF the recommended way to do this is to use the several layout containers.
The following is an example of a grid which lays two buttons at the bottom and a textbox at the top.
<Grid>
<Grid.RowDefinitions>
<!-- TextBox row with unspecified height. -->
<RowDefinition Height="*"/>
<!-- Button row with automated height so it resizes to
fit the content -->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Textbox on first row. -->
<TextBox Margin="3" Name="textBox1" Grid.Row="0" AcceptsReturn="True" />
<!-- StackPanel which lays the two buttons at the bottom horizontally.
RightToLeft is specified so that the first button appears on right.
-->
<StackPanel Grid.Row="1" HorizontalAlignment="Right"
Orientation="Horizontal" FlowDirection="RightToLeft">
<!-- The buttons. Only padding and margin are hardcoded so these
can resize to the contents -->
<Button Padding="3" Margin="3">OK</Button>
<Button Padding="3" Margin="3">Cancel</Button>
</StackPanel>
</Grid>

Categories