Adding image objects to wpf with code - c#

I'm a rookie at C# and WPF and I'm trying to create a simple car-simulator. Mainly the idea of the simulator is that I have C#-class that creates car-objects that have for example speed variable that can be changed and timer for moving from left to right. I want to do movement with timer and not for example doubleanimation. In WPF I have AddCarButton for adding cars in certain points in Canvas.
The problem is I dont know how to add cars to Canvas. This is very frustrating because it doesn't sound like a big thing to do but I feel like I have tried everything and not succeeded.
This is latest attempt with car-class. I have tried using Canvas.Set-methods but failed.
class car
{
private int speed;
public car(int s)
{
speed = s;
Bitmap bmp = new Bitmap(
System.Reflection.Assembly.GetEntryAssembly().
GetManifestResourceStream("MyProject.Resources.car.png"));
Graphics g = Graphics.FromImage(bmp);
//Canvas.SetBottom(g, 0);
//Canvas.SetLeft(g, 0);
//Canvas.SetBottom(bmp, 0);
//Canvas.SetLeft(bmp, 0);
}
public void addCar(car c)
{
Canvas.SetBottom(c, 0);
Canvas.SetLeft(c, 0);
}

If you're coding on WPF you shouldn't use Windows Forms stuff. To work with images you use BitmapSource and its derived classes, and to access your resources programmatically you usually use pack URIs. It's not the only way, though.
Here is a little example that draws some images on a canvas control.
The XAML code for the canvas could be like this (it's just an example):
<Canvas Height="400" HorizontalAlignment="Left" Margin="0" Name="canvas1" VerticalAlignment="Top" Width="400" />
and your main window code...
public partial class MainWindow : Window
{
BitmapImage carBitmap = new BitmapImage(new Uri("pack://application:,,,/Images/BlueCar.png", UriKind.Absolute));
Image[] carImg = new Image[5];
Random rnd = new Random();
public MainWindow()
{
InitializeComponent();
double maxX = canvas1.Width - carBitmap.Width;
double maxY = canvas1.Height - carBitmap.Height;
for (int i = 0; i < carImg.Length; i++)
{
carImg[i] = new Image();
carImg[i].Source = carBitmap;
carImg[i].Width = carBitmap.Width;
carImg[i].Height = carBitmap.Height;
Canvas.SetLeft(carImg[i], rnd.NextDouble() * maxX);
Canvas.SetTop(carImg[i], rnd.NextDouble() * maxY);
canvas1.Children.Add(carImg[i]);
}
}
}
Obviously you need change the name of your image resource. By the way, to add an image go to Project > Add existing item... and select your image file, now your image will appear in the Solution explorer (by default, Visual Studio stores image resources in a folder called "Images"), if you select it you'll see in the Properties window that its Build action is Resource, don't change this! (some people think it should be Embedded resource but that's incorrect).
If you don't get this new Uri("pack://application:,,,/Images/BlueCar.png", UriKind.Absolute), you should read this link on pack URIs.

You need to put your bitmap in an Image (and not Graphics), and then you need to add the image to the canvas:
Canvas.Children.Add(image);

Related

Xamarin Forms Drag Image between Views

I have an application in Xamarin Forms, and I need that the user can choose one image from below and drag anywhere he wants to in the top view, the idea is: The below view with the images are the home rooms, and the top view is the Houseplant, the user can create his houseplant by dragging and rotating the images, and then finally save the top view as a jpg or png image.
I've searched here and 2 3 pages of google about drag and etc, but I haven't found anything that could help me with that, I tried pan gesture, tap gesture, but no success =[
Sorry if it is duplicated or something, this is my first post, and I really couldn't find anything.
How can I get this working in Xamarin.Forms or at least with custom renderers and etc?
Thank you guys.
Sample image of what I need
For your image in XAML:
<Image Source="plant.png" x:Name="image"/>
You can actually use pan gesture recognizers to drag and drop images in C#:
Define variables:
double x; // totalX for the pan gesture
double y; // totalY for the pan gesture
Initialize pan gesture and add it to the image:
PanGestureRecognizer panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += PanUpdated;
image.GestureRecognizers.Add(panGesture);
The event handler for the gesture:
void PanUpdated(object sender, PanUpdatedEventArgs args)
{
if (args.StatusType.Equals(GestureStatus.Running)) {
x = args.TotalX;
y = args.TotalY;
image.TranslateTo(x, y, 1);
}
else if (args.StatusType.Equals(GestureStatus.Completed)) {
// set the layout bounds of the image to the new position
// method varies depending on what type of layout you are using for the image
// eg. assume the image is in an absolute layout
// where the layout height is the screen height
// and the layout width is the screen width
Task.Run(() => {
Device.BeginInvokeOnMainThread(async () => // run UI task on main thread
{
await Task.Delay(50); // avoid flickering
var screenWidth = Application.Current.MainPage.Width;
var screenHeight = Application.Current.MainPage.Height;
var b = image.Bounds;
var newBounds = new Rectangle(b.X + x, b.Y + y, b.Width, b.Height);
var newAbsoluteBound =
new Rectangle(newBounds.X / (screenWidth - newBounds.Width),
newBounds.Y / (screenHeight - newBounds.Height),
newBounds.Width / screenWidth,
newBounds.Height / screenHeight);
// set new absolute bounds so a new TranslateTo can be applied
AbsoluteLayout.SetLayoutBounds(image, newAbsoluteBound);
await image.TranslateTo(0, 0, 0);
});
});
}
}
Make sure your page or Image is not in a scrollView.
If ScrollView is enabled for both orientations, Pan-Gesture wont work.
.
.

Making a constantly updating Image (WPF)

So i am messing around with making a simple AI and stuff with basic C#, and in this project i have alot of points which i've been visualising by making a bitmap.
This bitmap i have rendered/loaded to an image object in the WPF window.. but my problem is that this is rendered each milisecond, making the framerate quite bad - so how would i make this better?
Can i load it 'constantly'? or should i take another approach?
What i got now is pretty simple but i can show the important parts anyway, taken out of the full class:
private static Bitmap BitMap = new Bitmap(500, 500);
static Graphics GraphicFromBitMap
{
get
{
return Graphics.FromImage(BitMap);
}
}
public static BitmapSource loadBitmapAsImage()
{
IntPtr intPtr = BitMap.GetHbitmap();
BitmapSource bitmapSource = null;
try
{
bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(intPtr,
IntPtr.Zero, Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(intPtr);
}
DeleteObject(intPtr);
return bitmapSource;
}
This is ofcourse only the bitmap part - the actual loading is done the following way:
DispatcherTimer Timer = new DispatcherTimer();
public MainWindow()
{
this.Timer.Tick += new EventHandler(Timer_Tick);
this.Timer.Interval = new TimeSpan(0, 0, 0, 1);
this.Timer.Start();
}
void Timer_Tick(object sender, EventArgs e)
{
WorldMap.Draw();
map.Source = WorldMap.BitMapSource;
}
This is ofcourse only the important parts - i hope my question is understandable but just to clearify and repeat:
I need a WPF image to update 'every frame' or everytime specific values change.
My question might have been answered before, but i couldn't really find anything that work nor something suiting this instance.
BTW making the timer set off more frequently creates an error with the loading, but the exact error code, i can't remember, and i can't seem to create it again - never the less this probably isn't the most practical way of doing this.
EDIT:
For clarification, this is all i got right now: http://imgur.com/EIiSRFQ
I want nothing fancy - it's just for personal projects playing around with programming and math, and that's alot easier if i can visualize the objects that i am 'moving' in my 2D plane.
Right now i am playing around with physics and gravity, trying to create a simple solar system with working physics. this is all just side projects to get to know the different tools better when i am too tired to work on my main project.
I would look to represent the visual elements of the bitmap as controls in WPF. That way you can update them directly and as frequently as they change, without the overhead of creating a bitmap and rendering it.
Performance would be far greater as you'd only update the changes in value.
To demonstrate the point, create a control...
<UserControl x:Class="Sample_Chart.Views.CodeBehindChart"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Canvas x:Name="LayoutRoot" />
</UserControl>
This is as simple as they get. Next edit the code behind file...
public partial class CodeBehindChart : UserControl
{
public CodeBehindChart()
{
InitializeComponent();
Respond();
}
private async void Respond()
{
await Task.Delay(2000);
Random r = new Random();
while (true)
{
this.LayoutRoot.Children.Clear();
for (int i = 0; i < 100; i++)
{
Rectangle rectangle = new Rectangle();
rectangle.SetValue(Canvas.LeftProperty, r.NextDouble() * this.LayoutRoot.ActualWidth);
rectangle.SetValue(Canvas.TopProperty, r.NextDouble() * this.LayoutRoot.ActualHeight);
rectangle.Width = 2;
rectangle.Height = 2;
rectangle.Fill = Brushes.Black;
this.LayoutRoot.Children.Add(rectangle);
}
await Task.Delay(500);
}
}
}
In this code behind we have an async void method which firstly waits for 2 seconds (optional) before then creating 100 visual elements at random locations within the control. It refreshes this every 1/2 second.
If you did the same thing, but based those locations, sizes and fill - even use different shapes - I think you'll have a high performing, scaling and easily extendable solution to your requirements.
Taking this the next stage and controlling from a ViewModel will require a bit more thought which for your 'first project' - albeit an interesting one, may be a step ambitious ;)

OutOfMemoryException on WP8 (C# & Silverlight) because of WritableBitmap

I'm creating and application that needs to add and remove a lot of UIElement to a Canvas.
Basically a Canvas contains a collection of UIElement and automatically renders/updates it on the screen depending what it contains.
In order to avoid having a tons of UIElements who overlap each others on the screen I prefer to add all of them on a secondary Canvas then create a Image from it (thanks to WritableBitmap). Finally I add this Image on my current Canvas.
By allowing to have only few image on my Canvas I expect to have better performance.
Unfortunately it seems I can't delete completely the WritableBitmap, even if I set it to null.
The following code illustrates it :
//My constructor
public WP8Graphics()
{
//Here my collection DataBinded with the Canvas from the Mainpage
this.UIElements = new ObservableCollection<UIElement>();
//The secondary Canvas
GraphicCanvas = new Canvas();
GraphicCanvas.Height = MainPage.CurrentCanvasHeight;
GraphicCanvas.Width = MainPage.CurrentCanvasWidth;
}
///This method can be hit thousand times, it basically create a rectangle
public void fillRect(int x, int y, int width, int height)
{
// some code
// CREATE THE RECTANGLE rect
GraphicCanvas.Children.Add(rect); // My secondary Canvas
WriteableBitmap wb1 = new WriteableBitmap(GraphicCanvas, null);
wb1.Invalidate();
WriteableBitmap wb2 = new WriteableBitmap((int)MainPage.CurrentCanvasWidth, (int)MainPage.CurrentCanvasHeight);
for (int i = 0; i < wb2.Pixels.Length; i++)
{
wb2.Pixels[i] = wb1.Pixels[i];
}
wb2.Invalidate();
wb1 = null;
Image thumbnail = new Image();
thumbnail.Height = MainPage.CurrentCanvasHeight;
thumbnail.Width = MainPage.CurrentCanvasWidth;
thumbnail.Source = wb2;
this.UIElements.Add(thumbnail);
}
After something like 24 WriteableBitmap created a OutOfMemoryException appears.
I read many articles about this problem and in my case it seems the WriteableBitmap depends on my GraphicCanvas and remains because there still have a reference to it. I can't delete my Graphic Canvas nor set myImage source to null.
I have 2 question :
Is there another way to create an image from Canvas or a collection of UIElements ?
Is it possible to remove the reference who keeps that WriteableBitmap alive ?
I hope to be enough clear and easy to read.
Thank you for reading.
EDITED with atomaras suggestion, but still the same problem
WriteableBitmap wb1 = new WriteableBitmap(GraphicCanvas, null);
This line still throws OutOfMemoryException.
You need to copy the pixels of the original writeablebitmap (that will hold on to the GraphicsCanvas) to a new writeablebitmap.
Take a look at this great post http://www.wintellect.com/blogs/jprosise/silverlight-s-big-image-problem-and-what-you-can-do-about-it
Also why do you keep all of the writeablebitmaps in the UIElements collection? Wouldn't the latest one suffice? Cant you clear the UIElements collection right before adding the latest/new bitmap?

How do I make RenderTargetBitmap and VisualBrush play nice together?

My requirements:
a persistent UserControl that handles logic for a custom image, such as a map or drawing
a set of containers to implement caching on the image during zoom or pan movements
VisualBrush copies of the UserControl that I can add to the containers for use with Effects
I currently implement image caching with a RenderTargetBitmap, but that seems to have trouble with the VisualBrush-covered Rectangle objects I'm using.
My question: What can I add/change in this code to get the VisualBrush objects to render correctly after RenderTargetBitmap uses them? What strange thing is RenderTargetBitmap doing that makes the VisualBrush invisible?
This is a problem that I have been unable to reproduce without a decent amount of code.
In my xaml file I have:
<Window x:Class="ElementRender.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525">
<Grid>
<Grid Name="_contentContainer">
<Rectangle Fill="White"/>
<Grid Name="_content">
<Grid Name="_back"/>
<Grid Name="_body"/>
</Grid>
</Grid>
<StackPanel VerticalAlignment="Bottom" Orientation="Horizontal">
<Button Content="New" Name="New"/>
<Button Content="Move" Name="Move"/>
<Button Content="Update" Name="Update"/>
</StackPanel>
</Grid>
</Window>
and the .xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
public partial class MainWindow : Window
{
private const int imageWidth = 150;
private const int imageHeight = 150;
private readonly UserControl Control;
public MainWindow()
{
InitializeComponent();
// User Control setup
Control = new UserControl() {
Width = imageWidth, Height = imageHeight,
Content = BuildImage()
};
_body.Children.Add(SoftCopy(Control));
// event setup
Move.Click += (sender, e) => _content.RenderTransform = new TranslateTransform(50, 50);
New.Click += (sender, e) => {
HardCopy();
_content.RenderTransform = null;
Control.Content = BuildImage();
};
}
private FrameworkElement BuildImage()
{
return new Rectangle{Fill=Brushes.Blue};
}
private void HardCopy()
{
int width = (int) _contentContainer.ActualWidth;
int height = (int) _contentContainer.ActualHeight;
// render the current image
var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (var context = dv.RenderOpen())
{
var brush = new VisualBrush(_contentContainer) { Opacity = .5 };
context.DrawRectangle(brush, null, new Rect(0, 0, width, height));
}
rtb.Render(dv);
var lastRender = new Image
{
Source = rtb,
Stretch = Stretch.None,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Width = width,
Height = height
};
_back.Children.Clear();
_back.Children.Add(lastRender);
}
private FrameworkElement SoftCopy(FrameworkElement element)
{
return new Rectangle{Fill= new VisualBrush(element), Width=element.Width, Height=element.Height};
}
}
A few helping notes about the code:
the xaml's _contentContainer works with HardCopy() to copy the current images into the image cache, _back.
SoftCopy returns a FrameworkElement that looks exactly like the one past in, but without any transforms, effects, or visual parents. This is very important.
BuildImage simulates building a new image to be pasted over the cache after the initial image has been transformed somehow.
If you build and run the application removing the SoftCopy() from the _body.Children.Add(SoftCopy(Control));, you see the effect that I want to get: the new element is pasted above the old element, and the old element seems to retain its transform.
Alternatively, if you cut out the line var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32); from HardCopy, the caching function is broken, but the SoftCopy is displayed correctly.
However, if you run the application as-is, you notice that the new BlueRectangle (as rendered through a VisualBrush) doesn't display at all, until you hit the "New" button again, pushing the image to the cache, and still not showing you the new created image.
I'm going to be pompous enough to call this a bug in WPF. I eventually found out how to fix the strange behavior I was getting:
var visual = visualBrush.Visual;
visualBrush.Visual = null;
visualBrush.Visual = visual;
This should essentially be a null operation: by the end, the visual brush has the same visual as when it started. However, adding this code segment after rendering the VisualBrush into the RenderTargetBitmap fixed the issue I was having.
I didn't quite understand the post but there are few important things:
If you apply RenderTransform/Margins to element and take picture of it(RenderTargetBItmap), you're gonna have bad time. It will be offseted and you will get only sub-picture.
The idea is to take picture without any rendertransforms, and then later copy RenderTransform over from the old one. If needed.

Transforming coordinates from an image control to the image source in WPF

I'm trying to learn WPF, so here's a simple question, I hope:
I have a window that contains an Image element bound to a separate data object with user-configurable Stretch property
<Image Name="imageCtrl" Source="{Binding MyImage}" Stretch="{Binding ImageStretch}" />
When the user moves the mouse over the image, I would like to determine the coordinates of the mouse with respect to the original image (before stretching/cropping that occurs when it is displayed in the control), and then do something with those coordinates (update the image).
I know I can add an event-handler to the MouseMove event over the Image control, but I'm not sure how best to transform the coordinates:
void imageCtrl_MouseMove(object sender, MouseEventArgs e)
{
Point locationInControl = e.GetPosition(imageCtrl);
Point locationInImage = ???
updateImage(locationInImage);
}
Now I know I could compare the size of Source to the ActualSize of the control, and then switch on imageCtrl.Stretch to compute the scalars and offsets on X and Y, and do the transform myself. But WPF has all the information already, and this seems like functionality that might be built-in to the WPF libraries somewhere. So I'm wondering: is there a short and sweet solution? Or do I need to write this myself?
EDIT I'm appending my current, not-so-short-and-sweet solution. Its not that bad, but I'd be somewhat suprised if WPF didn't provide this functionality automatically:
Point ImgControlCoordsToPixelCoords(Point locInCtrl,
double imgCtrlActualWidth, double imgCtrlActualHeight)
{
if (ImageStretch == Stretch.None)
return locInCtrl;
Size renderSize = new Size(imgCtrlActualWidth, imgCtrlActualHeight);
Size sourceSize = bitmap.Size;
double xZoom = renderSize.Width / sourceSize.Width;
double yZoom = renderSize.Height / sourceSize.Height;
if (ImageStretch == Stretch.Fill)
return new Point(locInCtrl.X / xZoom, locInCtrl.Y / yZoom);
double zoom;
if (ImageStretch == Stretch.Uniform)
zoom = Math.Min(xZoom, yZoom);
else // (imageCtrl.Stretch == Stretch.UniformToFill)
zoom = Math.Max(xZoom, yZoom);
return new Point(locInCtrl.X / zoom, locInCtrl.Y / zoom);
}
It would probably be easier if you used a ViewBox. For example:
<Viewbox Stretch="{Binding ImageStretch}">
<Image Name="imageCtrl" Source="{Binding MyImage}" Stretch="None"/>
</Viewbox>
Then when you go and call GetPosition(..) WPF will automatically account for the scaling.
void imageCtrl_MouseMove(object sender, MouseEventArgs e)
{
Point locationInControl = e.GetPosition(imageCtrl);
}

Categories

Resources