I have a Silverlight application which has on it a Canvas.
On that Canvas, I dynamically "paint" a bunch of stuff, but adding controls to the canvas.
I have a button outside the Canvas area which clears the content.
The objects are removed (successfully). However, the Canvas area does not refresh itself immediately; it currently requires a MouseOver or other event for the Canvas itself.
What is the simplest way to have an external object invalidate a Canvas? I'm sure I'm missing something simple!
It's a bit grubby, but you could try changing the visibility to 'Visible' (even though it already is) of the Canvas, so:
myCanvas.Visibility = Visibility.Visible;
We've found that this forces as redraw, even if the actual value of myCanvas.Visible hasn't changed...
Give it a go, it's only a one liner that may fix things. Although I would expect the Canvas to be redrawing anyway if you're removing things from it.
In case someone comes along after the fact like I did looking for this answer...
yourFrameworkElement.InvalidateArrange();
yourFrameworkElement.UpdateLayout();
worked for me.
Note: calling only this.yourFrameworkElement.UpdateLayout();
did not work for me. I had to call InvalidateArrange()
. I called UpdateLayout()
immediately thereafter for completeness - probably has zero real impact.
Also the myCanvas.Visibility = Visibility.Visible;
trick did not work for me as I was trying to cause the LayoutUpdated event to fire.
Not a simple one-liner, but it works. It works well in my experience. It postpones the given action for a tenth of a second to allow the UI thread to update, but it still executes the action on the UI thread.
using System;
using System.Windows.Threading;
public static class MyTestClass
{
public static void Postpone(Action a_action)
{
TaggedDispatchTimer timer = new TaggedDispatchTimer();
timer.Interval = TimeSpan.FromSeconds(0.1);
timer.Tick += OnDoPostponedAction;
timer.UserState = a_action;
timer.Start();
}
private static void OnDoPostponedAction(object sender, EventArgs e)
{
TaggedDispatchTimer timer = sender as TaggedDispatchTimer;
timer.Stop();
timer.Tick -= OnDoPostponedAction;
var action = timer.UserState as Action;
if (action != null)
action();
}
}
public class TaggedDispatchTimer : DispatcherTimer
{
public Object UserState { get; set; }
}
Here's how I use it:
MyTestClass.Postpone(() =>
{
// Do some bloody long operation.
});
In my case (I had a Canvas inside a Canvas and I needed to resize the inner canvas due to an orientation change), the myCanvas.Visibility = Visibility.Visible;
trick didn't do it, but it was really near:
myCanvas.Visibility = Visibility.Collapsed;
myCanvas.Visibility = Visibility.Visible;
worked just fine.
The InvalidateArrange
followed by an UpdateLayout
also didn't do it.
At the end, all of this was not necessary, since I realized that the Canvas inside a Canvas was not necessary and was able to change the inner Canvas for a StackPanel, and no tricks were needed for it to work as expected.