Using VisualBrush, I am taking snapshots of a Window that contains a TabControl.
Snapshot #1:
Switch to "Dos" (not Microsoft), snapshot #2:
If I just take a picture of the TabControl or the DockPanel, everything works fine, this problem is particular to taking a picture of the Window. Help!!
Code behind:
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WpfVisual
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var bitmapSource = this.TakeSnapshot(this);
Snapshot.Source = bitmapSource;
}
public BitmapSource TakeSnapshot(FrameworkElement element)
{
if (element == null)
{
return null;
}
var width = Math.Ceiling(element.ActualWidth);
var height = Math.Ceiling(element.ActualHeight);
element.Measure(new Size(width, height));
element.Arrange(new Rect(new Size(width, height)));
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
var target = new VisualBrush(element);
dc.DrawRectangle(target, null, new Rect(0, 0, width, height));
dc.Close();
}
var renderTargetBitmap = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(visual); // maybe here?
return renderTargetBitmap;
}
}
}
Xaml:
<Window x:Class="WpfVisual.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<TabControl x:Name="Tabs" DockPanel.Dock="Left" Width="200">
<TabItem Header="Uno">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="50" Foreground="Red">#1</TextBlock>
</TabItem>
<TabItem Header="Dos">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="50" Foreground="Green">#2</TextBlock>
</TabItem>
</TabControl>
<Button DockPanel.Dock="Top" Margin="10" FontSize="14" Click="Button_Click">Take a snapshot</Button>
<Image x:Name="Snapshot" Margin="10,0,10,10"></Image>
</DockPanel>
</Window>
I found that I was able to get it to work by commenting out the part where I make a VisualBrush from the Window.
//var visual = new DrawingVisual();
//using (var dc = visual.RenderOpen())
//{
// var target = new VisualBrush(element);
// dc.DrawRectangle(target, null, new Rect(0, 0, width, height));
// dc.Close();
//}
and to call the element directly in renderTargetBitmap.Redner
renderTargetBitmap.Render(element);
though now I'm not sure why I ended up using DrawRectangle and VisualBrush when I could have just rendered the element directly.
Related
I was following this article and I got my canvas to be saved, however, I want to extend the code's functionality and save a particular part of my canvas as an image, rather than my entire canvas.
I tried setting the rect.Offset and rect.Location properties but the image is always saved from the upper left corner of my canvas.
Does anyone know how can I achieve my wanted functionality in a similar way?
Thanks!
A simple method would be to use a CroppedBitmap after rendering the whole canvas. You could reuse the same RenderTargetBitmap, if you need multiple images.
RenderTargetBitmap rtb = new RenderTargetBitmap((int)canvas.RenderSize.Width,
(int)canvas.RenderSize.Height, 96d, 96d, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);
var crop = new CroppedBitmap(rtb, new Int32Rect(50, 50, 250, 250));
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(crop));
using (var fs = System.IO.File.OpenWrite("logo.png"))
{
pngEncoder.Save(fs);
}
If you want to save to a bitmap object instead of a file, you can use:
using (Stream s = new MemoryStream())
{
pngEncoder.Save(s);
Bitmap myBitmap = new Bitmap(s);
}
I know this is an old question, but it took me a while of searching and trying different answers to come up with something that worked reliably well. So to save some time for those in the future, here is a little service to either save a canvas out to a file, or return an ImageSource for display elsewhere in your application.
It should be made more robust for a production application, additional null and error checking, etc..
public static class RenderVisualService
{
private const double defaultDpi = 96.0;
public static ImageSource RenderToPNGImageSource(Visual targetControl)
{
var renderTargetBitmap = GetRenderTargetBitmapFromControl(targetControl);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
var result = new BitmapImage();
using (var memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
result.BeginInit();
result.CacheOption = BitmapCacheOption.OnLoad;
result.StreamSource = memoryStream;
result.EndInit();
}
return result;
}
public static void RenderToPNGFile(Visual targetControl, string filename)
{
var renderTargetBitmap = GetRenderTargetBitmapFromControl(targetControl);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
var result = new BitmapImage();
try
{
using (var fileStream = new FileStream(filename, FileMode.Create))
{
encoder.Save(fileStream);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"There was an error saving the file: {ex.Message}");
}
}
private static BitmapSource GetRenderTargetBitmapFromControl(Visual targetControl, double dpi = defaultDpi)
{
if (targetControl == null) return null;
var bounds = VisualTreeHelper.GetDescendantBounds(targetControl);
var renderTargetBitmap = new RenderTargetBitmap((int)(bounds.Width * dpi / 96.0),
(int)(bounds.Height * dpi / 96.0),
dpi,
dpi,
PixelFormats.Pbgra32);
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
{
var visualBrush = new VisualBrush(targetControl);
drawingContext.DrawRectangle(visualBrush, null, new Rect(new Point(), bounds.Size));
}
renderTargetBitmap.Render(drawingVisual);
return renderTargetBitmap;
}
}
And a sample WPF app demonstrating it's use.
MainWindow.xaml
<Window x:Class="CanvasToBitmapDemo.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:CanvasToBitmapDemo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Click="Button_Click" Content="Capture Image" Width="100"/>
<Button Click="Button_Click_1" Content="Save To Disk" Width="100"/>
</StackPanel>
<Canvas x:Name="PART_Canvas" Grid.Row="1" Grid.Column="0">
<Ellipse Canvas.Top="50"
Canvas.Left="60"
Fill="Gold"
Width="250"
Height="250" />
<Polyline Stroke="#FF853D00"
StrokeThickness="10"
StrokeEndLineCap="Round"
StrokeStartLineCap="Round"
Points="110,100 120,97 130,95 140,94 150,95 160,97 170,100" />
<Ellipse Canvas.Top="115"
Canvas.Left="114"
Fill="#FF853D00"
Width="45"
Height="50" />
<Polyline Stroke="#FF853D00"
StrokeThickness="10"
StrokeEndLineCap="Round"
StrokeStartLineCap="Round"
Points="205,120 215,117 225,115 235,114 245,115 255,117 265,120" />
<Ellipse Canvas.Top="120"
Canvas.Left="208"
Fill="#FF853D00"
Width="45"
Height="50" />
<Polyline Stroke="#FF853D00"
StrokeThickness="10"
StrokeEndLineCap="Round"
StrokeStartLineCap="Round"
Points="150,220 160,216 170,215 180,215 190,216 202,218 215,221" />
</Canvas>
<Image x:Name="PART_Image" Grid.Row="1" Grid.Column="1" Stretch="None"/>
</Grid>
And the code behind making the calls into the service.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
PART_Image.Source = RenderVisualService.RenderToPNGImageSource(PART_Canvas);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
RenderVisualService.RenderToPNGFile(PART_Canvas, "myawesomeimage.png");
}
}
Looking at the link you posted, obviously you can choose the rendered target coordinates here.
RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
(int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
See if this solution works for you.
Size size = new Size(width, height);
canvas.Measure(size);
canvas.Arrange(new Rect(X, Y, width, height));
//Save Image
...
...
// Revert old position
canvas.Measure(new Size());
In my case, I needed to apply a size constraint for the resulting image, since the images needed to be stored and later used as a small icon.
I was able to scale the image down to a reasonable size using CreateScaledRect below in combination with GetScaledRenderTargetBitmapFromControl (thanks to code from #Andy Stagg's post).
Then, to store the image for later use, I used the SaveImageOfControlToStream method below.
private static Rect CreateScaledRect(Visual targetControl)
{
Rect scaledRect;
var bounds = VisualTreeHelper.GetDescendantBounds(targetControl);
// maintain aspect ratio and make sure scaledRect is at least 64 wide or 64 high
if (bounds.Width < bounds.Height)
{
scaledRect = new Rect(new Point(), new Size(64, bounds.Height / bounds.Width * 64));
}
else
{
scaledRect = new Rect(new Point(), new Size(bounds.Width / bounds.Height * 64, 64));
}
return scaledRect;
}
private static BitmapSource GetScaledRenderTargetBitmapFromControl(Visual targetControl, double dpi = defaultDpi)
{
if (targetControl == null) return null;
// Calling CreateScaledRect here to reduce image size
var bounds = CreateScaledRect(targetControl);
var renderTargetBitmap = new RenderTargetBitmap((int)(bounds.Width * dpi / 96.0),
(int)(bounds.Height * dpi / 96.0),
dpi,
dpi,
PixelFormats.Pbgra32);
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
{
var visualBrush = new VisualBrush(targetControl);
drawingContext.DrawRectangle(visualBrush, null, new Rect(new Point(), bounds.Size));
}
renderTargetBitmap.Render(drawingVisual);
return renderTargetBitmap;
}
public static void SaveImageOfControlToStream(Stream outputStream, Visual targetControl)
{
var bitmapSource = GetScaledRenderTargetBitmapFromControl(targetControl);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource ));
encoder.Save(outputStream);
}
I am attempting to create a png capture of a StackPanel, however when I save, I am getting a distorted view where all the content is black rectangles, and the size is not correct. The width and height are correct in the image save, however all the content is forced to the top and squished together
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Views="clr-namespace:POExpress.Views" x:Class="POExpress.MainWindow"
Title="My Window" Height="500" MinWidth="1000" Width="1000">
<Grid>
<TabControl>
<TabItem Header="My Epics">
<Grid Background="#FFE5E5E5">
<Border Margin="0,52,0,0" BorderThickness="1" BorderBrush="Black">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="sp_ports" Orientation="Vertical"/>
</ScrollViewer>
</Border>
<Button x:Name="btn_capture" Content="Save to png" Margin="0,10,114,0" VerticalAlignment="Top" Height="31" Background="White" HorizontalAlignment="Right" Width="99" Click="Btn_capture_Click"/>
</Grid>
</TabItem>
</TabControl>
</Grid>
public RenderTargetBitmap GetImage()
{
Size size = new Size(sp_ports.ActualWidth, sp_ports.ActualHeight);
if (size.IsEmpty)
return null;
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingvisual = new DrawingVisual();
using (DrawingContext context = drawingvisual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(sp_ports), null, new Rect(new Point(), size));
context.Close();
}
result.Render(drawingvisual);
return result;
}
public static void SaveAsPng(RenderTargetBitmap src)
{
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.Filter = "PNG Files | *.png";
dlg.DefaultExt = "png";
if (dlg.ShowDialog() == true)
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
using (var stream = dlg.OpenFile())
{
encoder.Save(stream);
}
}
}
private void Btn_capture_Click(object sender, RoutedEventArgs e)
{
SaveAsPng(GetImage());
}
What it should render as (with some info blacked out)
All UIElements inherit from Visual, so you can feed your StackPanel to the Render method directly.
public RenderTargetBitmap GetImage()
{
Size size = sp_ports.DesiredSize;
if (size.IsEmpty)
return null;
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
result.Render(sp_ports);
return result;
}
UPDATE
As pointed out by #Clemens, there are some subtle intricacies to using the UIElement directly. His other comment, however, is the million dollar one.
Size size = uiElement.DesiredSize
Gives us the size of the visible portion of uiElement.
Size size = new Size(uiElement.ActualWidth, uiElement.ActualHeight)
Returns the full size of uiElement, also extending in the non-visible range.
Given you've run into this problem, you're after the latter. The main gotcha is you'll need to reevaluate the visual before rendering. Currently, you're projecting the full visual to the desired size (the visible part) of the UIElement.
public RenderTargetBitmap GetImage(FrameworkElement element)
{
Size size = new Size(element.ActualWidth, element.ActualHeight);
if (size.IsEmpty)
return null;
element.Measure(size);
element.Arrange(new Rect(size));
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingvisual = new DrawingVisual();
using (DrawingContext context = drawingvisual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(element), null, new Rect(new Point(), size));
}
result.Render(drawingvisual);
return result;
}
I use FrameworkElement to incorporate ActualWidth and ActualHeight.
UPDATE 2
As soon as I change the size of the stack panel, the screenshot gets hosed again. It seems to remember whatever the longest state was and squishes based on that.
After some fiddling around I was able to reproduce your issue. It occurs when the StackPanel has to extend to fill any remaining space. The solution is to give the uiElement infinite space to calculate its desired size, which relieves us from the dependency on actual sizes.
public RenderTargetBitmap GetImage(FrameworkElement element)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(element.DesiredSize));
Size size = element.DesiredSize;
if (size.IsEmpty)
return null;
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingvisual = new DrawingVisual();
using (DrawingContext context = drawingvisual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(element), null, new Rect(new Point(), size));
}
result.Render(drawingvisual);
return result;
}
I've checked Expander behavior (ref test app) but couldn't find anything funny going on there.
For completeness, here's my test app.
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public RenderTargetBitmap GetImage(FrameworkElement element)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(element.DesiredSize));
Size size = element.DesiredSize;
if (size.IsEmpty)
return null;
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingvisual = new DrawingVisual();
using (DrawingContext context = drawingvisual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(element), null, new Rect(new Point(), size));
}
result.Render(drawingvisual);
return result;
}
public static void SaveAsPng(RenderTargetBitmap src)
{
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.Filter = "PNG Files | *.png";
dlg.DefaultExt = "png";
if (dlg.ShowDialog() == true)
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
using (var stream = dlg.OpenFile())
{
encoder.Save(stream);
}
}
}
private void Btn_capture_Click(object sender, RoutedEventArgs e)
{
SaveAsPng(GetImage(sp_ports));
}
}
}
MainWindow.cs
<Window x:Class="WpfApp.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:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Top" Click="Btn_capture_Click">Take Pic</Button>
<StackPanel x:Name="sp_ports">
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="H1" Width="40"/>
<DataGridTextColumn Header="H2" Width="*"/>
</DataGrid.Columns>
</DataGrid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="200" />
<RowDefinition Height="Auto" />
<RowDefinition Height="400" />
</Grid.RowDefinitions>
<StackPanel Background="Red"/>
<Expander Grid.Row="1" ExpandDirection="Down" IsExpanded="False">
<TabControl Height="400">
<TabItem Header="Tab 1">
<TextBox FontSize="50" TextWrapping="Wrap">Text for Tab 1</TextBox>
</TabItem>
<TabItem Header="Tab 2">
<TextBox FontSize="50" TextWrapping="Wrap">Text for Tab 1</TextBox>
</TabItem>
</TabControl>
</Expander>
<StackPanel Grid.Row="2" Background="Blue"/>
</Grid>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="H1" Width="40"/>
<DataGridTextColumn Header="H2" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</DockPanel>
</Window>
I am adding images to the WPF stack panel using the C# code. For positioning the images, I am using margin keyword but when I have run the project, a white space for the last image created and push the first image. why?
In the below, I have loaded two images from the same source whit different margins and as you see the first image is covered by the white space from the second image. note that the source Image is a png image and has not any with border.
the code is below(note that I used image control first and then i used the border control and both have the same problem ):
Border newLegBorder =new Border();
BitmapImage casingLegBitmapImage = new BitmapImage(new Uri("images/casingleg.png", UriKind.Relative));
newLegBorder.Background = new ImageBrush(casingLegBitmapImage);
newLegBorder.Width = casingLegBitmapImage.Width;
newLegBorder.Height = casingLegBitmapImage.Height;
newLegBorder.SetValue(Grid.ColumnProperty, 0);
newLegBorder.SetValue(Grid.RowProperty, 0);
newLegBorder.VerticalAlignment = VerticalAlignment.Top;
newLegBorder.Margin = new Thickness(0, 0, 100, 0);
newLegBorder.Width = casingLegBitmapImage.Width;
newLegBorder.Height = casingLegBitmapImage.Height;
schematic.Children.Add(newLegBorder);
Border newLeg2Border = new Border();
BitmapImage casingLeg2BitmapImage = new BitmapImage(new Uri("images/casingleg.png", UriKind.Relative));
newLeg2Border.Background = new ImageBrush(casingLeg2BitmapImage);
newLeg2Border.Width = casingLeg2BitmapImage.Width;
newLeg2Border.Height = casingLeg2BitmapImage.Height;
newLeg2Border.SetValue(Grid.ColumnProperty, 0);
newLeg2Border.SetValue(Grid.RowProperty, 0);
newLeg2Border.VerticalAlignment = VerticalAlignment.Top;
newLeg2Border.Margin = new Thickness(100, 0, 0, 0);
newLeg2Border.Width = casingLeg2BitmapImage.Width;
newLeg2Border.Height = casingLeg2BitmapImage.Height;
schematic.Children.Add(newLeg2Border);
<Window
xmlns="schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerikDocking="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Docking"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:Chromes="clr-namespace:Telerik.Windows.Controls.Chromes;assembly=Telerik.Windows.Controls"
xmlns:Primitives="clr-namespace:Telerik.Windows.Controls.Primitives;assembly=Telerik.Windows.Controls.Navigation"
x:Class="imagetoolbox.wellSchematic"
Title="wellSchematic"
Height="402" Width="458">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="236"/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="schematic" HorizontalAlignment="Left" Height="371" VerticalAlignment="Top" Width="214">
</StackPanel>
</Grid>
</Window>
The problem is that the stackpanel will, well, stack all children. The first image is not covered by the second image, they are merely stacked on top of each other.
To position the images you should use the grid directly, combined with margins and alignments.
Play around with the XAML sample below to get the layout you want, and transfer it to codebehind.
<Window
....>
<Grid x:Name="schematic">
<!-- First image will stretch vertically -->
<Image Stretch="Fill" Margin="10,20,0,25" HorizontalAlignment="Left" VerticalAlignment="Top" Width="10">
<Image.Source>
<BitmapImage UriSource="images/casingleg"/>
</Image.Source>
</Image>
<Image Margin="0,30,20,0" HorizontalAlignment="Right" VerticalAlignment="Top" Width="10">
<Image.Source>
<BitmapImage UriSource="images/casingleg"/>
</Image.Source>
</Image>
<Image Margin="40,0,0,10" HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="10">
<Image.Source>
<BitmapImage UriSource="images/casingleg"/>
</Image.Source>
</Image>
</Grid>
</Window>
Code-behind for the above sample.
public class WellSchematic : UserControl
{
public WellSchematic()
{
InitializeComponent();
var im1 = CreateImage();
im1.HorizontalAlignment = HorizontalAlignment.Left;
im1.VerticalAlignment = VerticalAlignment.Top;
im1.Margin = new Margin(10,20,0,25);
im1.Stretch = Stretch.Fill;
var im2 = CreateImage();
im2.HorizontalAlignment = HorizontalAlignment.Right;
im2.VerticalAlignment = VerticalAlignment.Top;
im2.Margin = new Margin(0,30,20,0);
var im3 = CreateImage();
im3.HorizontalAlignment = HorizontalAlignment.Left;
im3.VerticalAlignment = VerticalAlignment.Bottom;
im3.Margin = new Margin(40,0,0,10);
// Add more pictures
}
private void CreateImage()
{
var image = new Image();
var bmp = new BitmapImage(new Uri(#"images/casingleg", UriKind.Relative));
image.Source = bmp;
image.Width = bmp.Width;
image.Height = bmp.Height;
schematic.Children.Add(image);
}
}
i am trying to create a map object on windows phone 8 and nothing shows yet i have no errors on my code. help me heres the code:
the ui element doesnt show on the map and yet i created a polygon, and a rectangle element on the grid.
here is my xaml:
<phone:PhoneApplicationPage
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"
x:Class="Fisheries.Landing_site"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="False">
<!--LayoutRoot contains the root grid where all other page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="Landing Sites" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<maps:Map x:Name="MyMap" Center="5.354683, 24.644135" ZoomLevel="10" LandmarksEnabled="True" PedestrianFeaturesEnabled="True"/>
</Grid>
</Grid>
and here is my cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Device.Location;
using Windows.Devices.Geolocation;
using Microsoft.Phone.Maps.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Fisheries
{
public partial class Landing_site : PhoneApplicationPage
{
public Landing_site()
{
InitializeComponent();
UpdateMap();
/* Rectangle MyRectangle = new Rectangle();
MyRectangle.Fill = new SolidColorBrush(Colors.Black);
MyRectangle.Height = 20;
MyRectangle.Width = 20;
MyRectangle.SetValue(Grid.RowProperty, 0);
MyRectangle.SetValue(Grid.ColumnProperty, 0);*/
}
public void UpdateMap()
{
Map drew = new Map();
drew.Margin = new Thickness(-12, 0, 0, 0);
ContentPanel.Children.Add(drew);
drew.SetView(new GeoCoordinate(5.354683, 24.644135), 3.5D);
//creating the grid
Grid MyGrid = new Grid();
MyGrid.RowDefinitions.Add(new RowDefinition());
MyGrid.RowDefinitions.Add(new RowDefinition());
MyGrid.Background = new SolidColorBrush(Colors.Transparent);
//Creating a Rectangle
Rectangle MyRectangle = new Rectangle();
MyRectangle.Fill = new SolidColorBrush(Colors.Black);
MyRectangle.Height = 20;
MyRectangle.Width = 40;
MyRectangle.SetValue(Grid.RowProperty, 0);
MyRectangle.SetValue(Grid.ColumnProperty, 0);
//adding a rectangle to the grid
MyGrid.Children.Add(MyRectangle);
//Creating a Polygon
Polygon MyPolygon = new Polygon();
MyPolygon.Points.Add(new Point(2, 0));
MyPolygon.Points.Add(new Point(22, 0));
MyPolygon.Points.Add(new Point(2, 40));
MyPolygon.Stroke = new SolidColorBrush(Colors.Black);
MyPolygon.Fill = new SolidColorBrush(Colors.Black);
MyPolygon.SetValue(Grid.RowProperty, 1);
MyPolygon.SetValue(Grid.ColumnProperty, 0);
//addding a new polygon to the grid
//Creating a MapOverlay and adding the Grid to it.
MapOverlay MyOverlay = new MapOverlay();
MyOverlay.Content = MyGrid;
MyGrid.Children.Add(MyPolygon);
//setting the geolocation cordiante to work on some polygon to show on the map
MyOverlay.GeoCoordinate = new GeoCoordinate(5.354683, 24.644135);
//this is where is set the position overlay to a specific location on the map
MyOverlay.PositionOrigin = new Point(0, 0.5);
//Creating a MapLayer and adding the MapOverlay to it
MapLayer MyLayer = new MapLayer();
MyLayer.Add(MyOverlay);
MyMap.Layers.Add(MyLayer);
}
}
}
I want to create some kind of simulation. There will be numerous sprites floating around. Because I think that rendering every frame thousand times the same primitives which make up a sprite, will be slow, I want render them once into a bitmap and then show this sprite every frame.
But it doesn't seem to work, the screen stays white.
My WPF source is trivial:
<Window x:Class="WPFGraphicsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="800" Width="1000">
<Canvas>
</Canvas>
</Window>
And this is my code:
public partial class MainWindow : Window
{
Ellipse e;
RenderTargetBitmap bmp2;
public MainWindow()
{
InitializeComponent();
e = new Ellipse();
e.Width = 40;
e.Height = 40;
e.Fill = new SolidColorBrush(Color.FromRgb(0, 0, 200));
((Canvas)this.Content).Children.Add(e);
((Canvas)this.Content).Measure(new Size(1000, 800));
((Canvas)this.Content).Arrange(new Rect(new Size(1000, 800)));
RenderTargetBitmap bmp2 = new RenderTargetBitmap(40, 40, 96, 96, PixelFormats.Pbgra32);
bmp2.Render(e);
((Canvas)this.Content).Children.Remove(e);
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawImage(bmp2, new Rect(100,100, 40, 40));
}
}
Why doesn't this work?
You could put an Image object on the Canvas and then use the RenderTargetBitmap to update the image. For example
<Window x:Class="WPFGraphicsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="800" Width="1000">
<Canvas>
<Image Name="frameImage" />
</Canvas>
</Window>
Then you can update the image like this, for the example, I am just rendering a new ellipse every 100 ms. Of course you should manage the Pens and Brushes better than what I show here, this is just an example to clarify the suggestion.
public partial class MainWindow : Window
{
DispatcherTimer _timer = new DispatcherTimer();
RenderTargetBitmap _renderSurface =
new RenderTargetBitmap(100, 100, 96, 96, PixelFormats.Pbgra32);
Random _rnd = new Random();
public MainWindow()
{
InitializeComponent();
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(100);
_timer.Tick += new EventHandler(_timer_Tick);
_timer.Start();
}
void _timer_Tick(object sender, EventArgs e)
{
DrawingVisual visual = new DrawingVisual();
DrawingContext context = visual.RenderOpen();
int value = _rnd.Next(40);
context.DrawEllipse(
new SolidColorBrush(Colors.Red),
new Pen(new SolidColorBrush(Colors.Black), 1),
new Point(value, value), value, value);
context.Close();
_renderSurface.Render(visual);
frameImage.Source = _renderSurface;
}
}