1

Okay, this is a real head-scratcher:

If I select a menuitem that causes the image, that makes up the entire window (a writeableBitmap) to have some pixels drawn on it, it does so and displays correctly.

However, if I add a while loop (let's say for 5 loops) to the same method the drawing on the bitmap DOES NOT DISPLAY until the loop is completed and then the 5th redrawn bitmap is correctly displayed.

So, is there some sort of 'automatic refresh' that is happening to the window when a menuitem is selected but is being skipped in the while loop?

More details. This works fine (brings in a 'clean' image, draws some stuff on it, displays it):

// This brings in a 'clean' image
writeableBitmap = new WriteableBitmap(CleanVegMap);
image.Source = writeableBitmap;
// This makes a bunch of draws on the bitmap
DrawDinos2d();

This, however, 'goes away' for 10 seconds and then only displays the last (i.e. 5th) image:

int z = 0;
while (z < 5){
z++;
   // This brings in a 'clean' image
   writeableBitmap = new WriteableBitmap(CleanVegMap);
   image.Source = writeableBitmap;
   // This makes a bunch of draws on the bitmap
   DrawDinos2d();
}

New idea: is it possible that somehow the 5 'drawn' writeableBitmaps are being cached in memory, somehow by the system?

Tried using the Dispatcher (like below):

                Dispatcher.Invoke((Action)delegate
            {               
                writeableBitmap = new WriteableBitmap(CleanVegMap);
                image.Source = writeableBitmap;
                DrawDinos2d();
            });

Same thing (goes away for 10 seconds and then displays only the last image.

Another clue: I just put a MessageBox in the loop at the bottom of each loop and, as I somehow suspected, it 'blitted' the redrawn screen correctly. Somehow:

 System.Windows.MessageBox.Show("Glarp!");

this call 'woke up' the system. Again, any ideas?

zetar
  • 1,225
  • 2
  • 20
  • 45
  • 1
    Do you have any code that you can show us? – Arian Motamedi Jul 18 '13 at 16:11
  • There's a ton of code... unfortunately, I don't think it would help... I'll post a fragment – zetar Jul 18 '13 at 16:13
  • Just added some (hopefully relevant) code. – zetar Jul 18 '13 at 16:20
  • OMG.. Why in the world are you doing pixel by pixel stuff in WPF?? – Federico Berasategui Jul 18 '13 at 16:37
  • Because I'm only drawing about 40 pixels each redraw. Do you have better idea? – zetar Jul 18 '13 at 16:41
  • 2
    Have you tried placing the body of your loop inside the Dispatcher? Based upon your question, that's what seems like the answer for you. – Gayot Fow Jul 18 '13 at 17:17
  • What's the Dispatcher? Never heard of this. I'm still thinking that there's some sort of invalidation/redraw that could force this but I haven't found it. – zetar Jul 18 '13 at 17:28
  • Yes, just tried placing the body of the loop (and then the entire method) inside the Dispatcher. Still same thing. – zetar Jul 18 '13 at 18:58
  • 1
    No, you put it in the window's dispatcher, not the WriteableBitmap's dispatcher. Dispatch to the closest element as possible. And asynchronously... – Gayot Fow Jul 18 '13 at 19:38
  • I really don't understand what you mean. I'm googling, "WPF Window Dispatch asynchronously example" and not really coming up with anything. – zetar Jul 18 '13 at 20:13
  • How would I call a method, 'from the Window's dispatcher asynchronously'? I cannot find any reference or example to do this. – zetar Jul 18 '13 at 20:53
  • I added a code fragment. It should help acquaint you with the Dispatcher and overall that would be useful for you because you're working with bitmaps. – Gayot Fow Jul 18 '13 at 23:57
  • No problem about being 'late'. I know you're in the UK so I figured you had retired for the evening. – zetar Jul 19 '13 at 01:09

1 Answers1

1

What happened when you inserted a MessageBox into the processing and got the results you were expecting was that the UI thread had a chance to get 'caught up' while the MessageBox was open. So it created the 'illusion' that using an MessageBox suddenly made it work, but behind the scenes it was just threads sorting themselves out and clearing their instruction queues.

To create the same effect programmatically, you can update your bitmap with a method like this (ETA: requires .NET 4.5 Framework)...

    public void UpdateBitmap()
    {
        WriteableBitmap writeableBitmap = new WriteableBitmap
                                (100, 100, 96, 96, PixelFormats.Bgr32, null);
        writeableBitmap.Dispatcher.InvokeAsync(() =>
            {
                Console.WriteLine("work goes here");
            });
    }

This runs the operation asynchronously on the thread that the bitmap's dispatcher is associated with and will give the UI a chance to catch up. Depending upon the payload of your 'DrawDinos2d' method, you MIGHT have to migrate the processing to a background thread and feed it to the UI thread on a piece-by-piece basis. But start with this approach first.

ETA: In a .NET 4.0 framework, the counterpart to the above looks like this...

    public void UpdateBitmap()
    {
        object[] objs = new object[] {null};
        WriteableBitmap writeableBitmap = new WriteableBitmap(
             100, 100, 96, 96, PixelFormats.Bgr32, null);
        writeableBitmap.Dispatcher.BeginInvoke((SendOrPostCallback)delegate
            {
                Console.WriteLine(@"work goes here");
            }, objs);
    }

The docs read "Executes the specified delegate asynchronously with the specified arguments on the thread that the System.Windows.Threading.Dispatcher was created on."

Gayot Fow
  • 8,710
  • 1
  • 35
  • 48
  • It's throwing this error: "Error 1 'System.Windows.Threading.Dispatcher' does not contain a definition for 'InvokeAsync' and no extension method 'InvokeAsync' accepting a first argument of type 'System.Windows.Threading.Dispatcher' could be found (are you missing a using directive or an assembly reference?)" I added a reference to System.ServiceModel – zetar Jul 19 '13 at 01:18
  • @zetar Aha! Go to your project properties and switch the 'Target Framework' setting to .NET Framework 4.5 and let me know. The async was added in 4.5 and does not appear in prior versions. – Gayot Fow Jul 19 '13 at 01:26
  • I'm using Visual Studio 2010. It only has .NET Framework 4. If I click on "Install other frameworks..." it takes me to here: http://msdn.microsoft.com/en-US/vstudio/hh487282.aspx and the highest framework I can download is .NET 4.0.3. Where is 4.5? – zetar Jul 19 '13 at 10:45
  • I JUST READ (here: http://stackoverflow.com/questions/12390175/targeting-net-framework-4-5-via-visual-studio-2010) "You must use Visual Studio 2012 in order to utilize .NET 4.5." I am running VS 2010. – zetar Jul 19 '13 at 10:48
  • 1
    What a shame. I have amended my answer to include a 4.0 counterpart to the answer. Check it out and see if it's workable for you and let me know. – Gayot Fow Jul 19 '13 at 10:55
  • Throwing a runtime error when I try to draw pixels in another method (calling thread... different thread)... I'll try moving the Draw out of the UpdateBitmap. Nope.. that isn't working... have problems with drawing on the image being in a different thread. Any ideas? – zetar Jul 19 '13 at 12:39
  • It doesn't seem to be drawing on the correct bitmap... or, it's still in the background. – zetar Jul 19 '13 at 13:10
  • remember to recode from the example code back to your original ... writeableBitmap = new WriteableBitmap(CleanVegMap); – Gayot Fow Jul 19 '13 at 19:55