Silverlight: Force Canvas to Invalidate or Repaint

2020-07-10 11:30发布

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!

4条回答
爷的心禁止访问
2楼-- · 2020-07-10 11:40

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.

查看更多
爷的心禁止访问
3楼-- · 2020-07-10 11:48

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.

查看更多
戒情不戒烟
4楼-- · 2020-07-10 11:58

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.
    });
查看更多
啃猪蹄的小仙女
5楼-- · 2020-07-10 12:01

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.

查看更多
登录 后发表回答