Making a constantly updating Image (WPF) - c#

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 ;)

Related

How to ensure UWP app is always full screen on launch?

Is there a way (either C# or XAML) I can maximize a UWP app window even after I resized and closed it previously on desktop?
I have tried with ApplicationViewWindowingMode.FullScreen but this makes the app go entire full screen and covers the Windows Taskbar too.
You can use another value PreferredLaunchViewSize from ApplicationViewWindowingMode and then set ApplicationView.PreferredLaunchViewSize but the key is to find out what the size is going to be.
Theoretically, you could use a really big number and window would just extend to the max it could be. However, it's probably safer to just calculate the screen dimensions in effective pixels.
So if you just call the following method before InitializeComponent(); on your main Page, it should maximize the window on startup.
private static void MaximizeWindowOnLoad()
{
// Get how big the window can be in epx.
var bounds = ApplicationView.GetForCurrentView().VisibleBounds;
ApplicationView.PreferredLaunchViewSize = new Size(bounds.Width, bounds.Height);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
}
Note the app somehow remembers these settings even after you uninstalled it. If you ever want to change back to the default behavior (app starts up with the previous window size), simply call ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Auto; once and remove all the code.
Update
Looks like in the latest Windows 10 build, ApplicationView.GetForCurrentView().VisibleBounds no longer returns the full window size in effective pixels anymore. So we now need a new way to calculate it.
Turns out it's quite straightforward since the DisplayInformation class also gives us the screen resolution as well as the scale factor.
The following is the updated code -
public MainPage()
{
MaximizeWindowOnLoad();
InitializeComponent();
void MaximizeWindowOnLoad()
{
var view = DisplayInformation.GetForCurrentView();
// Get the screen resolution (APIs available from 14393 onward).
var resolution = new Size(view.ScreenWidthInRawPixels, view.ScreenHeightInRawPixels);
// Calculate the screen size in effective pixels.
// Note the height of the Windows Taskbar is ignored here since the app will only be given the maxium available size.
var scale = view.ResolutionScale == ResolutionScale.Invalid ? 1 : view.RawPixelsPerViewPixel;
var bounds = new Size(resolution.Width / scale, resolution.Height / scale);
ApplicationView.PreferredLaunchViewSize = new Size(bounds.Width, bounds.Height);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
}
}
If you want to MAXIMISE your app on launch you can use the following:
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Maximized;
But be sure to put it into the Loaded Event for your Page or it will not work!
I've too few points to comment directly. None of the above resized to a maximized view for me (or the below single-line ApplicationViewWindowingMode.Maximized method), but I have used some of the answers to come up with something that worked for me. It is still very clunky however. The screen size given in 'DisplayInformation' is too big to allow the page to be resized directly to it. Trying to do it didn't work and I had to take 60 off height and width to get it to return 'true', therefore I have the following bit of nonsense which worked, maybe it will help someone else find a better answer. It goes in the page/window loaded event. Nothing else needs to be added elsewhere.
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var view = ApplicationView.GetForCurrentView();
var displayInfo = DisplayInformation.GetForCurrentView();
double x = ActualWidth;
double y = ActualHeight;
bool answer = true;
// Get the screen resolution (APIs available from 14393 onward).
var resolution = new Size(displayInfo.ScreenWidthInRawPixels-60, displayInfo.ScreenHeightInRawPixels-60);
answer = view.TryResizeView(resolution); //This should return true if the resize is successful
if (answer)
{
x = displayInfo.ScreenWidthInRawPixels - 60;
y = displayInfo.ScreenHeightInRawPixels - 60;
}
answer = true;
while (answer == true)
{
x++;
answer = view.TryResizeView(new Size { Width = x, Height = y });
}
x = x - 1;
answer = true;
while (answer == true)
{
y++;
answer = view.TryResizeView(new Size { Width = x, Height = y });
}
Adding the following line to the OnLaunched event under App.xaml.cs did it for me.
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.FullScreen;
NOTE: Make sure to add it before the following line
Window.Current.Activate();
If you like to go fullscreen at the runtime use the following line.
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
I have this one liner that works as I expected Justins code to, but for some reason, when using Justins answer, my window would not be maximized... But then I changed something that did make it maximized but I lost all my fluent design such as Acrylic and RevealHighlite...
So I came up with this one liner which keeps all of my fluent design principles happy:
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
Something to note:
I did try Justins answer, and I am using his method of MaximizeWindowOnLoad() which I have called straight after the initializeComponent();
Full overview:
public class()
{
this.InitializeComponent();
MaximizeWindowOnLoad();
}
private static void MaximizeWindowOnLoad()
{
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
}

Flicker in C# WinForms program on screen clear

Alright, so I've done some research into this topic and most of the solutions I've found claim to fix the problem but I am finding that they aren't quite working right. I'm in the early stages of implementing just a simple little particle engine, nothing crazy I'm just doing it out of boredom. I have not done anything like this with WinForms before, I have certainly with C/C++ but this is a new thing for me. The following is the code I am using to draw the particles to the screen, the boiler plate code for the particles is not relevant as it works fine, I am more curious about my actual game loop.
Here is the main code for updates and redraws
public MainWindow()
{
this.DoubleBuffered = true;
InitializeComponent();
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle(object sender, EventArgs e)
{
Graphics g = CreateGraphics();
while (IsApplicationIdle())
{
UpdateParticles();
RenderParticles(g);
g.Dispose();
}
}
//Variables for drawing the particle
Pen pen = new Pen(Color.Black, 5);
Brush brush = new SolidBrush(Color.Blue);
public bool emmiter = false;
private void EmitterBtn_Click(object sender, EventArgs e)
{
//Determine which emitter to use
if (emmiter == true)
{
//Creates a new particle
Particle particle = new Particle(EmitterOne.Left, EmitterOne.Top, .5f, .5f, 20, 20);
emmiter = false;
}
else if(emmiter == false)
{
Particle particle = new Particle(EmitterTwo.Left, EmitterTwo.Top, -.5f, .5f, 20, 20);
emmiter = true;
}
}
public void RenderParticles(Graphics renderer)
{
Invalidate();
Thread.Sleep(0);
//Iterate though the static list of particles
for (int i = 0; i < Particle.activeParticles.Count; i++)
{
//Draw Particles
renderer.DrawRectangle(pen, Particle.activeParticles[i].x,
Particle.activeParticles[i].y,
Particle.activeParticles[i].w,
Particle.activeParticles[i].h);
}
}
public void UpdateParticles()
{
for (int i = 0; i < Particle.activeParticles.Count; i++)
{
//Move particles
Particle.activeParticles[i].MoveParticle();
}
}
The issue I am running into is that anytime the screen is getting cleared and updated, it gets this awful flickering, and not only that but it sometimes won't whenever I emit a particle.
The form is basically just using labels as invisible locations on the screen to say where to render each particle.
Anyway, I've seen this topic before but nothing has fixed anything, the current implementation is the least flickery/laggy but is not solving the issue.
Any help is appreciated, thanks!
EDIT* I realized I was never deallocating the graphics object each loop so I did that and there is no more delay whenever I click the emitter button, however the flicker is still there, I updated the code accordingly.
Getting rid of the visible paint artifacts requires double-buffering. In other words, render the scene into a back-buffer that, when ready, gets quickly blitted to the screen surface in a single step. That's a built-in feature in Winforms, simply set the DoubleBuffered property to true in the form constructor. You must use the Paint event to take advantage of that. Override OnPaint() and call RenderParticles(e.Graphics).
You need to take care of timing, right now your UI thread is burning 100% core and animation speed completely depends on the number of particles and the speed of the machine. Instead of Application.Idle, drop a Timer from the toolbox onto your form. In the Tick event handler, call UpdateParticles() and this.Invalidate() to get the Paint event to fire again. The timer's Interval property value is critical, you get the most reproducible update rate by picking 15 or 31 msec (64 or 32 FPS).
You are not always going to get the desired FPS rate, the timer will simply delay or skip a Tick event if the machine gets busy or is too slow or other code on the UI thread needs to run. To make sure that doesn't affect the animation, you must measure actual elapsed time instead of moving the particles by a fixed amount. Either Environment.TickCount, DateTime.UtcNow or Stopwatch are suitable ways to measure true elapsed time.

Adding image objects to wpf with code

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);

Which is faster when animating the UI: a Control or a Picture?

/I'm working with and testing on a computer that is built with the following:
{1 GB RAM (now 1.5 GB), 1.7 GHz Intel Pentium Processor, ATI Mobility Radeon X600 GFX}
I need scale / transform controls and make it flow smoothly. Currently I'm manipulating the size and location of a control every 24-33ms (30fps), ±3px. When I add a 'fade' effect to an image, it fades in and out smoothly, but it is only 25x25 px in size. The control is 450x75 px to 450x250 px in size. In 2D games such as Bejeweled 3, the sprites animate with no choppy animation.
So as the title would suggest: which is easier/faster on the processor: animating a bitmap (rendering it to the parent control during animation) or animating the control it's self?
EDIT:
Hey, I thought this was a helpful community, not one that down-rates questions that don't seem challenging! (And I've seen more ridiculous questions here with better ratings too!) Please drop me a line first before negatively rating my questions!
I managed to find some free-time in my heck-tick scheduled, to quickly whip up a new project. I'm sure my time could have been better spent else where but hopefully someone else in my shoes may find this of use out there...
The answer is: a Picture over a Control. When rendering a bitmap onto the canvas, there are very little events that will fire, if any. As for the control, it is filled with events - some chained, some looped, and the addition of recursion, so a simple 'LocationChanged' event wouldn't even cover the half of what actually is taking place under the hood.
What I would do for controls that have lots of dynamic animations applied to them during runtime, is to develop a two piece set: a control [rendering] template or active interface (for when the control is at a stand-still or before the play of an animation), and a the animating structure with basic defining properties such as the display image [the rendered control], the rectangle bounds, and any animation algorithms that may be applied latter.
Edit: As Requested, here are the before and after code examples:
// This is the triggering event of the translating animation
private void object_Click(object sender, EventArgs e)
{
// the starting point is at (75,75)
element.Transform(new Point(500, 250));
}
Before:
public class ControlElement : UserControl
{
private Timer tick;
private Point pT0;
public ControlElement() : base()
{
tick = new Timer();
tick.Interval = 30; // about 30fps
tick.Tick += new EventHandler(tick_Tick);
}
void tick_Tick(object sender, EventArgs e)
{
// get the new point from distance and current location/destination
this.Location = Utils.Transform(this.Location, pT0, 3);
if ((pT0.X - this.Location.X)+(pT0.Y - this.Location.Y) <= 0)
{
this.Location = pT0;
tick.Stop();
//this.Visible = true;
}
}
public void Transform(Point destination)
{
pT0 = destination;
//this.Visible = false;
tick.Start();
}
}
After: I create a class that holds a picture of what the control would look like using the DrawToBitmap feature. It still contains the same animation methods as above. I had to add the Location and LocationChanged elements since this class was no longer a control. If and when the actual control needed to be accessed, I would stop rendering and display an instance of the control it's self.
Here is the rendering call:
void element_LocationChanged(object sender, EventArgs e)
{
canvas.Invalidate();
}
void canvas_Paint(object sender, PaintEventArgs e)
{
if (element != null)
{
Bitmap bmp = new Bitmap(element.Display);
Pen p = new Pen(Color.FromArgb(128, 128, 128), 1);
e.Graphics.DrawImage(bmp, element.Location);
e.Graphics.DrawRectangle(p,
element.Location.X, element.Location.Y,
bmp.Width, bmp.Height);
}
}

Simple WPF sample causes uncontrolled memory growth

I have boiled down an issue I'm seeing in one of my applications to an incredibly simple reproduction sample. I need to know if there's something amiss or something I'm missing.
Anyway, below is the code. The behavior is that the code runs and steadily grows in memory until it crashes with an OutOfMemoryException. That takes a while, but the behavior is that objects are being allocated and are not being garbage collected.
I've taken memory dumps and ran !gcroot on some things as well as used ANTS to figure out what the problem is, but I've been at it for a while and need some new eyes.
This reproduction sample is a simple console application that creates a Canvas and adds a Line to it. It does this continually. This is all the code does. It sleeps every now and again to ensure that the CPU is not so taxed that your system is unresponsive (and to ensure there's no weirdness with the GC not being able to run).
Anyone have any thoughts? I've tried this with .NET 3.0 only, .NET 3.5 and also .NET 3.5 SP1 and the same behavior occurred in all three environments.
Also note that I've put this code in a WPF application project as well and triggered the code in a button click and it occurs there too.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows;
namespace SimplestReproSample
{
class Program
{
[STAThread]
static void Main(string[] args)
{
long count = 0;
while (true)
{
if (count++ % 100 == 0)
{
// sleep for a while to ensure we aren't using up the whole CPU
System.Threading.Thread.Sleep(50);
}
BuildCanvas();
}
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void BuildCanvas()
{
Canvas c = new Canvas();
Line line = new Line();
line.X1 = 1;
line.Y1 = 1;
line.X2 = 100;
line.Y2 = 100;
line.Width = 100;
c.Children.Add(line);
c.Measure(new Size(300, 300));
c.Arrange(new Rect(0, 0, 300, 300));
}
}
}
NOTE: the first answer below is a bit off-base since I explicitly stated already that this same behavior occurs during a WPF application's button click event. I did not explicitly state, however, that in that app I only do a limited number of iterations (say 1000). Doing it that way would allow the GC to run as you click around the application. Also note that I explicitly said I've taken a memory dump and found my objects were rooted via !gcroot. I also disagree that the GC would not be able to run. The GC does not run on my console application's main thread, especially since I'm on a dual core machine which means the Concurrent Workstation GC is active. Message pump, however, yes.
To prove the point, here's a WPF application version that runs the test on a DispatcherTimer. It performs 1000 iterations during a 100ms timer interval. More than enough time to process any messages out of the pump and keep the CPU usage low.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
namespace SimpleReproSampleWpfApp
{
public partial class Window1 : Window
{
private System.Windows.Threading.DispatcherTimer _timer;
public Window1()
{
InitializeComponent();
_timer = new System.Windows.Threading.DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(100);
_timer.Tick += new EventHandler(_timer_Tick);
_timer.Start();
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
void RunTest()
{
for (int i = 0; i < 1000; i++)
{
BuildCanvas();
}
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static void BuildCanvas()
{
Canvas c = new Canvas();
Line line = new Line();
line.X1 = 1;
line.Y1 = 1;
line.X2 = 100;
line.Y2 = 100;
line.Width = 100;
c.Children.Add(line);
c.Measure(new Size(300, 300));
c.Arrange(new Rect(0, 0, 300, 300));
}
void _timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
RunTest();
_timer.Start();
}
}
}
NOTE2: I used the code from the first answer and my memory grew very slowly. Note that 1ms is much slower and less iterations than my example. You have to let it run for a couple minutes before you start to notice growth. After 5 minutes it's at 46MB from a starting point of 30MB.
NOTE3: Removing the call to .Arrange completely eliminates the growth. Unfortunately, that call is pretty vital to my use since in many cases I'm creating PNG files from the Canvas (via the RenderTargetBitmap class). Without the call to .Arrange it doesn't layout the canvas at all.
I was able to reproduce your problem using the code you provided. Memory keeps growing because the Canvas objects are never released; a memory profiler indicates that the Dispatcher's ContextLayoutManager is holding on to them all (so that it can invoke OnRenderSizeChanged when necessary).
It seems that a simple workaround is to add
c.UpdateLayout()
to the end of BuildCanvas.
That said, note that Canvas is a UIElement; it's supposed to be used in UI. It's not designed to be used as an arbitrary drawing surface. As other commenters have already noted, the creation of thousands of Canvas objects may indicate a design flaw. I realise that your production code may be more complicated, but if it's just drawing simple shapes on a canvas, GDI+-based code (i.e., the System.Drawing classes) may be more appropriate.
WPF in .NET 3 and 3.5 has an internal memory leak. It only triggers under certain situations. We could never figure out exactly what triggers it, but we had it in our app. Apparently it's fixed in .NET 4.
I think it's the same as the one mentioned in this blog post
At any rate, putting the following code in the App.xaml.cs constructor solved it for us
public partial class App : Application
{
public App()
{
new HwndSource(new HwndSourceParameters());
}
}
If nothing else solves it, try that and see
Normally in .NET GC gets triggered on object allocation upon crossing a certain threshold, it does not depend on message pumps (I can't imagine it's different with WPF).
I suspect that Canvas objects are somehow rooted deep inside or something. If you do c.Children.Clear() right before the BuildCanvas method finishes, the memory growth slows down dramatically.
Anyway, as a commenter noted here, such usage of framework elements is pretty unusual. Why do you need so many Canvases?
Edit 2: Obviously not the answer, but was part of the back-and-forth among answers and comments here, so I'm not deleting it.
The GC never gets a chance to collect those objects because your loop and its blocking calls never end, and therefore the message pump and events never get their turn. If you used a Timer of some sort so that messages and events actually have a chance to process, you probably wouldn't be able to eat up all your memory.
Edit: The following does not eat up my memory as long as the interval is greater than zero. Even if the interval is just 1 Tick, as long as it isn't 0. If it's 0, we're back to the infinite loop.
public partial class Window1 : Window {
Class1 c;
DispatcherTimer t;
int count = 0;
public Window1() {
InitializeComponent();
t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds( 1 );
t.Tick += new EventHandler( t_Tick );
t.Start();
}
void t_Tick( object sender, EventArgs e ) {
count++;
BuildCanvas();
}
private static void BuildCanvas() {
Canvas c = new Canvas();
Line line = new Line();
line.X1 = 1;
line.Y1 = 1;
line.X2 = 100;
line.Y2 = 100;
line.Width = 100;
c.Children.Add( line );
c.Measure( new Size( 300, 300 ) );
c.Arrange( new Rect( 0, 0, 300, 300 ) );
}
}

Categories

Resources