0

I want to achieve a very well known behavior seen in the browser when you have an image to display that is larger then the monitor:

  1. Originally, the image is displayed fitting inside the window area, and the mouse cursor is a magnifying glass with a "+" icon;
  2. If you click, two things happen: a. The image is displayed with its native pixel size; b. Scroll bars appear;

I want this effect with a larger-than-screen UniformGrid. For that, I can use ViewBox. I have already got what I want putting the control inside a ViewBox with Stretch.Uniform property, and upon MouseLeftButtonDown event it toggles between Stretch.None and Stretch.Uniform, just like the large image in browser analogy, only without scroll bars.

Now if I add the ScrollViewer (ViewBox -> ScrollViewer -> UniformGrid), the effect doesn't work anymore, because the ScrollViewer always displays the (larger than window) MyUserControl with its native resolution, that is, clipped and with scroll bars activated, while I would like to alternate between this and a "fitting in ViewBox" version.

Here is how I get the resizing, but the ScrollViewer never displays:

        <Viewbox x:Name="vbox" Stretch="None">
            <ScrollViewer x:Name="scroll" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
                <UniformGrid x:Name="ugrid" Columns="2" MouseLeftButtonDown="UniformGrid_MouseLeftButtonDown">
                    <local:AtlasMasculinoAnterior/>
                    <local:AtlasMasculinoPosterior/>
                </UniformGrid>      
            </ScrollViewer>     
        </Viewbox>          

And if change the order, then the Scroll bars always display and the zoom doesn't toggle upon mouse click (although the event fires):

    <ScrollViewer x:Name="scroll" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
        <Viewbox x:Name="vbox" Stretch="None">
            <UniformGrid x:Name="ugrid" Columns="2" MouseLeftButtonDown="UniformGrid_MouseLeftButtonDown">
                <local:AtlasMasculinoAnterior/>
                <local:AtlasMasculinoPosterior/>
            </UniformGrid>      
        </Viewbox>      
    </ScrollViewer> 

And here the code behind event:

    private void UniformGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (vbox.Stretch == Stretch.None)
        {                
            vbox.Stretch = Stretch.Uniform;
        }
        else
            vbox.Stretch = Stretch.None;
    }

So what am I doing wrong, or what should I do so that the intended behavior works?

The way I see it, I would like to alternate between having the control in a ViewBox (Stretch.Uniform) and having the control inside a ScrollViewer, but I wonder how to have the same effect with both elements being part of the layout tree (one inside another), or even if I should, move the UniformGrid in and out of containers I would manipulate programmatically in code behind.

heltonbiker
  • 26,657
  • 28
  • 137
  • 252

2 Answers2

0

Got it to work in sort of a hackish way, by having a Grid with both a ViewBox and a ScrollViewer, and putting the UniformGrid inside one of them in XAML. Then, in code-behind, I programmatically detach the UniformGrid from its present container, and attach it to the other (using a boolean flag to control where it is, but that is debatable):

    <Grid x:Name="grid">
        <ScrollViewer x:Name="scroll" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
        <Viewbox x:Name="viewbox" Stretch="Uniform">
            <UniformGrid x:Name="ugrid" Columns="2" MouseLeftButtonDown="UniformGrid_MouseLeftButtonDown">
                <local:AtlasMasculinoAnterior/>
                <local:AtlasMasculinoPosterior/>
            </UniformGrid>  
        </Viewbox>  
    </Grid> 

and

    bool atlasfullscreen = false;        

    private void UniformGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {   
        UniformGrid ug = sender as UniformGrid;

        if (atlasfullscreen)
        {
            scroll.Content = null;
            viewbox.Child = ug;               
            atlasfullscreen = false;
        }
        else
        {
            viewbox.Child = null;
            scroll.Content = ug;
            atlasfullscreen = true;
        }
    }
heltonbiker
  • 26,657
  • 28
  • 137
  • 252
0

I had a similar use case where I had an item that I needed to alternate between Stretch.None and Stretch.Uniform, and when Stretch.None, I needed the scrollbars to be visible.

What I finally figured out was that when I set Stretch.None, I needed to set the ScrollViewer's Width & Height to the ViewBox's parent ActualWidth / Height, and when Stretch.Uniform, I needed to clear the ScollViewer's width and height.

So using your original XAML, plus the new Grid, here's the new XAML:

<Grid x:Name="grid">
    <Viewbox x:Name="vbox"
             Stretch="Uniform">
        <ScrollViewer x:Name="scroll"
                      HorizontalScrollBarVisibility="Auto"
                      VerticalScrollBarVisibility="Auto">
            <UniformGrid x:Name="ugrid"
                         Columns="2"
                         MouseLeftButtonDown="UniformGrid_MouseLeftButtonDown">
                <local:AtlasMasculinoAnterior />
                <local:AtlasMasculinoPosterior />
            </UniformGrid>
        </ScrollViewer>
    </Viewbox>
</Grid>

New code behind:

private void UniformGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (vbox.Stretch == Stretch.None)
    {                
        vbox.Stretch = Stretch.Uniform;
        scroll.Width = double.NaN;
        scroll.Height = double.NaN;
    }
    else
    {
        vbox.Stretch = Stretch.None;
        scroll.Width = grid.ActualWidth;
        scroll.Height = grid.ActualHeight;
    }
}

You might need to tweak the above example for how the Viewbox now being in a grid - but for my use case with similar XAML / code I got mine working without having to constantly move the child from the Viewbox to another control and back again.

So in summary: when Viewbox.Stretch = Uniform, set scrollviewer's width / height to double.NaN, and when Viewbox.Stretch = None, set scrollviewer's width / height to Viewbox.Parent.ActualWidth / Height.

B.O.B.
  • 704
  • 4
  • 10