WPF Viewbox and Image size - c#

I want to create the window that would show list of pictures one below the other. I've created control that contains ViewBox and Image in it:
<UserControl x:Class="..."
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>
<Viewbox Name="viewbox">
<Image Height="10" Name="image" Width="10" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Viewbox>
</Grid>
</UserControl>
public BitmapImage Image
{
get { return image.Source as BitmapImage; }
set { changeImage(value); }
}
public SingleIllustrationViewer()
{
InitializeComponent();
}
private void changeImage(BitmapImage img)
{
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero);
float dpiX = graphics.DpiX / 96;
this.image.BeginInit();
this.image.Source = img;
this.image.EndInit();
this.image.Width = img.PixelWidth / img.DpiX * dpiX;
}
and I'm placing Images on window like this:
double margin = 0;
for (int i = 0; i < illustrations.Count; i++)
{
String path = illustrations[i].printVersions.Last<String>();
BitmapImage bmp = new BitmapImage(new Uri(path));
Controls.SingleIllustrationViewer iv = new Controls.SingleIllustrationViewer();
iv.VerticalAlignment = System.Windows.VerticalAlignment.Top;
iv.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
iv.Margin = new Thickness(50, margin, 0, 0);
iv.Image = bmp;
grid.Children.Add(iv);
margin += iv.Image.Height + 20;
}
So, for example, I've placed 3 pictures (all 3 of same width) like this, and received such an interesting behavior: first one is good, second smaller, third smaller than a second. Here is the screen shot:
Maybe someone can tell me why is that so, and how can fix this, to see all those picture in the same width?
Thanks!
Regards, Tomas

Root cause:
You did not specify the Height or Width of the UserControl, so when the first SingleIllustrationViewer is added to the Grid, it will be stretched to occupied all available space until it reaches the edge of the Grid. The same happens to the second one, but it is constrained to a smaller region due to the incremented margin.
The size specified as
d:DesignHeight="300" d:DesignWidth="300"
is only used by designer, set the size like
Height="300" Width="300"
And then, put you viewers in a StackPanel instead of a Grid, then you don't have to calculate the Margin of a viewer base on the last viewer's position. StackPanel is a container that stacks its children in one direction, vertically or horizontally.
for (int i = 0; i < illustrations.Count; i++)
{
String path = illustrations[i].printVersions.Last<String>();
BitmapImage bmp = new BitmapImage(new Uri(path));
Controls.SingleIllustrationViewer iv = new Controls.SingleIllustrationViewer();
iv.VerticalAlignment = System.Windows.VerticalAlignment.Top;
iv.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
iv.Margin = new Thickness(50, 0, 0, 20); //50 px to the left, 20 px to the next child
iv.Image = bmp;
stackPanel1.Children.Add(iv);
}

Related

Need to draw rectangles on top of image in FlipView, but FlipView loads 3 images at a time

I have a collection of images i want to show with FlipView. On each image I want to draw multiple rectangles, but to do this I need the current dimensions for the image after it has been rendered. I have the coordinates for the rectangles in the same list as my images. I get the dimensions from the images via ImageOpened event, but the problem is the FlipView event loads three images at the same time causing the different rectangles all to be drawn on the first image. Any suggestions?
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
itemList = e.Parameter as List<TableData>;
foreach (var blobImage in itemList)
{
var request = (HttpWebRequest)WebRequest.Create($"http://localhost:58941/api/image?id={blobImage.ImageBlobName}");
request.Method = "GET";
request.ContentType = "application/json";
WebResponse response = await request.GetResponseAsync();
if (response != null)
{
string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
var myDict = JsonConvert.DeserializeObject<BlobImage>(responseString);
var jj = new MyImage(blobImage.ImageDescription, myDict.Uri, blobImage.GpsLatitude, blobImage.GpsLongitude, blobImage.GpsAltitude, blobImage.DateTime, blobImage.ObjectsDetected);
MyImages.Add(jj);
}
}
MyFlipView.ItemsSource = MyImages;
}
private void Image_ImageOpened(object sender, RoutedEventArgs e)
{
Image currentImageDimensions = sender as Image;
currentWidth = currentImageDimensions.ActualWidth;
currentHeight = currentImageDimensions.ActualHeight;
foreach (var imageRectangle in itemList)
{
for (int i = 0; i < imageRectangle.ObjectsDetected.Count; i++)
{
rectangle = new Rectangle();
var xMinConvert = Convert.ToDouble(imageRectangle.ObjectsDetected[i].xMin);
var yMinConvert = Convert.ToDouble(imageRectangle.ObjectsDetected[i].yMin);
var xMaxConvert = Convert.ToDouble(imageRectangle.ObjectsDetected[i].xMax);
var yMaxConvert = Convert.ToDouble(imageRectangle.ObjectsDetected[i].yMax);
var xMin = xMinConvert * currentWidth;
var yMin = yMinConvert * currentHeight;
var xMax = xMaxConvert * currentWidth;
var yMax = yMaxConvert * currentHeight;
rectangle.Height = yMax - yMin;
rectangle.Width = xMax - xMin;
var left = ((bgWidth - currentWidth) / 2) + xMin;
var top = ((bgHeight - currentHeight) / 2) + yMin;
rectangle.Margin = new Thickness(left, top, 0, 0);
rectangle.Stroke = new SolidColorBrush(Windows.UI.Colors.Red);
rectangle.StrokeThickness = 1;
layoutRoot.Children.Add(rectangle);
}
}
}
Xaml:
<ScrollViewer DoubleTapped="scrollViewer_DoubleTapped" MinZoomFactor="1" ZoomMode="Enabled" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid x:Name="cGrid" Width="{Binding ElementName=gridbg, Path=ActualWidth}" Height="{Binding ElementName=gridbg, Path=ActualHeight}">
<FlipView SelectionChanged="MyFlipView_SelectionChanged" Name="MyFlipView" Width="{Binding ElementName=gridbg, Path=ActualWidth}" Height="{Binding ElementName=gridbg, Path=ActualHeight}">
<FlipView.ItemTemplate>
<DataTemplate x:DataType="local:MyImage">
<Image Source="{Binding Image}" Stretch="Uniform" Height="{Binding ElementName=gridbg, Path=ActualHeight}" ImageOpened="Image_ImageOpened" />
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Border>
<Canvas x:Name="layoutRoot">
</Canvas>
</Grid>
</ScrollViewer>
but FlipView loads 3 images at a time
FlipView control supports virtualization at default, load three items at one time is as expected. If disabled the virtualization, all the items will be loaded into the FlipView at one time.
You have several ways to resolve your issue. Since you only need the Rectangle drawing when one FlipViewItem selected, you could put the drawing relative code snippet inside SelectionChanged event handle of FlipView. By doing this you may encounter the issue for getting the Image height and width. Actually you should be able to know the image metadata by getting StorageFile object from uri of the image. If you just want to get the Image control for getting the Height and Width, you may use VisualTreeHelper to get the Image control from FlipView.
Or you could consider to force load only one item to the FlipView each time. For this you could use ISupportIncrementalLoading for incremental loading.
I'm not sure what you are drawing these rectangles for, consider to draw these rectangles to the image before you binding the images to the FlipView if possible.

Writablebitmap Indexed8 writepixels

I try to change writablebitmap using writepixels() method, but it doesn't change any pixels.
it has following consructor
public void createWbm(int viewportW, int viewportH)
{
writeableBitmap = new WriteableBitmap(
viewportW,
viewportH,
96,
96,
PixelFormats.Indexed8,
new BitmapPalette(Form1.form1.getColors()));
i.Source = writeableBitmap;
}
and I use this method calling leftbuttondown event, but there is not any change. Is it necessary to use two loops(outer for row of pixels and inner for columns ) to paint every pixle or it is possible use just writepixels() method? thanks
void BrushPixel(MouseEventArgs e)
{
byte[] ColorData = { 0, 0, 0, 0 }; // B G R
Int32Rect rect = new Int32Rect(
(int)(e.GetPosition(i).X),
(int)(e.GetPosition(i).Y),
1,
1);
writeableBitmap.WritePixels( rect, ColorData, 4, 0);
}
You are incorrectly using it,
at this format (8-bit) your array ColorData either represents an 1x4, 4x1 or 2x2 pixels image.
therefore rect dimensions should match either of these sizes
Keep in mind that these are indices to colors in a palette, not BGR values as you've commented.
Here's a simple example :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var bitmap = new WriteableBitmap(100, 100, 96, 96, PixelFormats.Indexed8, BitmapPalettes.Halftone256);
int width = 50;
int height = 50;
var pixels = new byte[width*height];
var random = new Random();
random.NextBytes(pixels);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width, 0);
Image1.Source = bitmap;
}
}
XAML :
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350"
SnapsToDevicePixels="True"
UseLayoutRounding="True">
<Grid>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderBrush="Black"
BorderThickness="1">
<Image x:Name="Image1" Stretch="None" />
</Border>
</Grid>
</Window>
Note : at this format stride parameter always equals width because a pixel length in bytes is 1.
I strongly suggest you to use WriteableBitmapEx instead, it makes WriteableBitmap manipulation way easier. Beware that it supports only PixelFormats.Pbgra32 but unless you really have a specific reason to use 8-bit I can only recommend it, it can draw many primitives such as lines, rectangles, circles etc ...

StackPanel with Images in ScrollViewer - don't shows images

XAML:
<Grid x:Name="LayoutRoot" VerticalAlignment="Top">
<ScrollViewer x:Name="ScrollViewer1" Margin="0,0,0,0">
<StackPanel x:Name="myStackPanel"/>
</ScrollViewer>
</Grid>
C#:
Image[] image2 = new Image[30];
for (int n = 1; n <= 29; n++)
{
image2[n] = new Image();
BitmapImage bitmapa = downloadBitmap(n);
image2[n].Source = bitmapa;
myStackPanel.Children.Add(image2[n]);
}
I'm downloading BitmapImage from IsolatedSotrage -> downloadBitmap(n).
When I start the app I have a black image in my phone becouse photos are not visible(why?!), but when I lock the phone and unlock I have all the pictures, everything is ok.
When I remove ScrollViewer i don't have any problem.
Why this is happening? Please help me.
Dipak - I used the Grid - I hold each picture in a separate Grid:
Image[] image2 = new Image[30];
for (int n = 1; n <= 29; n++)
{
Grid myGrid = new Grid();
Image2[n] = new Image();
aktualizacja2 bitmapa = new aktualizacja2(n, path);
Image2[n].Source = bitmapa.getBitmap();
myGrid.Children.Add(Image2[n]);
myStackPanel.Children.Add(myGrid);
}
it works, but loading images takes much longer...

how can i show different backgrounds in multiple buttons in WPF

I need to show multiple buttons, but each one must have a different background than other buttons, I have been working on it, but I only got to display multiple buttons but with the same background.
Here is the XAML:
<Window x:Class="apple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="370" Width="525">
<Grid>
<Image Source="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg" Stretch="Fill"/>
<DockPanel Name="dock">
<UniformGrid Name="gridx" DockPanel.Dock="Top" Rows="3" Columns="3" Height="334">
</UniformGrid>
</DockPanel>
</Grid>
</Window>
Also, here is the C# code:
namespace apple
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
masterGUI();
}
public void masterGUI()
{
ImageBrush ib = new ImageBrush();
IconImage[] ico = null;
Bitmap[] img = null;
string[] list = null;
string[] link = Directory.GetFiles(#"C:\ProgramData\Microsoft\Windows\Start Menu\Programs", "*.lnk", SearchOption.AllDirectories);
list = new string[link.Length];
ico = new Icon[link.Length];
img = new Bitmap[link.Length];
for (int n = 0; n < link.Length; n++)
{
System.Windows.Controls.Button newBtn = new Button();
list[n] = System.IO.Path.GetFileNameWithoutExtension(link[n]);
FileToImageIconConverter some = new FileToImageIconConverter(link[n]);
ImageSource imgSource = some.Icon;
ib.ImageSource = imgSource;
newBtn.Background = ib;
newBtn.Content = list[n];
gridx.Children.Add(newBtn);
}
}
}
}
Any idea? thank you.
The ImageBrush needs to be created in the for-loop individually for each item. Otherwise you will end up with the same background for every item.
Also you are approaching this the "wrong" way, in WPF you should use data binding and data templating for this sort of thing instead of imperative looping.

Creating WriteableBitmap form chart issue

I have a problem when trying to create writeable bitmap form Silverlight toolkit Graph.
When using textBlock, everything is fine, but after trying to use Chart, generated bitmap is empty :( .
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
Chart chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
This code creates chart with sinusoid in it. Then Im trying to create bitmap from it.
LayoutRoot.Children.Add(chart_); // I tried to add chart_ to visual tree, It doesn't help
//creates bitmap
ScaleTransform t = new ScaleTransform() { ScaleX = 1.0, ScaleY = 1.0 };
//bitmap = new WriteableBitmap(chart_, t); Tried it also with this way
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, t);
texture = new Texture2D(GraphicsDeviceManager.Current.GraphicsDevice, bitmap.PixelWidth, bitmap.PixelHeight, false, SurfaceFormat.Color);
bitmap.CopyTo(texture);
All this code creates Empty Bitmap.But when I use TextBlock or some primitives like Ellipse, everything works. Im sure, that code generating chart is fine, cause chart is generated fine in Silverlight control.
EDIT:
I tried to create bitmap this way, but it dont help.
chart_.InvalidateMeasure();
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, null);
bitmap.Invalidate();
EDIT 2:
I don't want graph to be in visual tree. I just need generate image of it an than use it in XNA part of my application.
Just a couple of small changes, but I suspect the big change is manually adding a reference to System.Windows.Controls after using NuGet to add the Charting package.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Media.Imaging;
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
Chart chart_;
public MainPage()
{
InitializeComponent();
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
LayoutRoot.Children.Add(chart_); // I tried to add chart_ to visual tree, It doesn't help
}
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
//creates bitmap
WriteableBitmap bitmap;
ScaleTransform t = new ScaleTransform() { ScaleX = 1.0, ScaleY = 1.0 };
//bitmap = new WriteableBitmap(chart_, t); Tried it also with this way
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, t);
//texture = new Texture2D(GraphicsDeviceManager.Current.GraphicsDevice, bitmap.PixelWidth, bitmap.PixelHeight, false, SurfaceFormat.Color);
//bitmap.CopyTo(texture);
image1.Source = bitmap;
}
}
}
And
<UserControl x:Class="SilverlightApplication1.MainPage"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
<Image Height="150" HorizontalAlignment="Left" Margin="97,109,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="200" />
</Grid>
</UserControl>
That should get you going...
Don't forget to delete all of the attempted workarounds - I don't think any of them are necessary. The only code I did not use from your example had to do with the textures - I didn't know what to do with that and it wasn't the problem you were having anyways...
David
I believe that by the time you are creating WriteableBitmap object & calling Render method, Chart has not be rendered yet. You can check this by moving these lines of code to some other event like Button_Click etc. Have a look at below codes as it is creating the WriteableBitmap and then passes it to image control as source....
XAML Code.....
<UserControl x:Class="TryBitmap.MainPage"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Height="25" Width="100" Content="Capture" Click="Button_Click"/>
<Grid x:Name="grdGraphs" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image x:Name="img" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
Code Behind....
public partial class MainPage : UserControl
{
private Chart chart_;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
chart_.SetValue(Grid.ColumnProperty, 0);
grdGraphs.Children.Add(chart_);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var bitmap = new WriteableBitmap((int)(chart_.RenderSize.Width), (int)(chart_.RenderSize.Height));
bitmap.Render(chart_, new MatrixTransform());
bitmap.Invalidate();
img.Source = bitmap;
}
}
I'm afraid this is only going to be half an answer. I can solve your immediate problem but I'm afraid you'll just end up with another one I haven't managed to solve.
Creating the bitmap after a call to Dispatcher.BeginInvoke should ensure that your bitmap isn't completely blank, i.e.:
// do stuff with chart_ ...
Dispatcher.BeginInvoke(() =>
{
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, null);
bitmap.Invalidate();
// At this point, the bitmap shouldn't be blank.
});
However, the results of this likely to be less than satisfactory. I ran your code and I found that the LineSeries was missing from the chart image, although the rest of the chart was there. This remained true even after I set all Durations of the various animations in the ControlTemplate for the LineSeries to 0 and additionally set the following properties on the chart:
chart_.AnimationSequence = AnimationSequence.Simultaneous;
chart_.TransitionDuration = new TimeSpan(0);
I tried wrapping the WriteableBitmap operations in further calls to Dispatcher.BeginInvoke(), and after doing this the data points started to appear more clearly. However, I can't believe that an approach like this is the right solution to the problem.

Categories

Resources