Search code examples
c#wpfxamlviewbox

Set a Delay to a WPF Viewbox?


I have a Window with a Viewbox in it. Based on the content of my Viewbox resizing the Window can get quite 'stuttery'. I don't need to have the Viewbox resize its content in realtime, it would be sufficient to update the Viewbox once the user is done resizing the Window.

I couldn't find anything about this topic on google by myself.

Is there a way to 'delay' my Viewbox?

edit: if not, what would be the best way to simulate this behavior?

Best i could think about was to create a grid around the viewbox, create 2 Properties for width and height, bind them to both window and grid with a twoway Binding and then set a delay in the binding, so the grid and the viewbox in it will resize after the delay, but then I would have to set a pre-defined starting size for my window due to the properties.


Solution

  • You can use a Canvas as the container for the ViewBox. (The direct container has to be a Grid, which gives the ViewBox a boundary to resize.)

    Unlike Grid, Canvas uses absolute positioning, and it does not resize with the Window, so do its children.

    <Grid x:Name="root">
        <Canvas>
            <Grid x:Name="innerGrid">
                <Viewbox>
                    <Content here />
                </Viewbox>
            </Grid>
        </Canvas>
    </Grid>
    

    Then you can control when to resize the ViewBox (by resizing its direct container).

    The following code is inspired by the comments. It uses a one-time Timer, the timer starts when user has finished manipulating, and the resizing takes place when the timer interval elapsed.

    System.Timers.Timer timer; //Declare it as a class member, not a local field, so it won't get GC'ed. 
    public MainWindow()
    {
        InitializeComponent();
        timer = new System.Timers.Timer(1000);
        timer.AutoReset = false; //the Elapsed event should be one-shot
        timer.Elapsed += (o, e) =>
        {
            //Since this is running on a background thread you need to marshal it back to the UI thread.
            Dispatcher.BeginInvoke(new Action(() => {
                innerGrid.Width = root.ActualWidth;
                innerGrid.Height = root.ActualHeight;
            }));
        };
    
        this.SizeChanged += (o, e) =>
        {
            //restart the time if user is still manipulating the window             
            timer.Stop(); 
            timer.Start();
        };
    }