Restrict zoom-out to "Fit"?

Started by ruland, September 11, 2017, 08:52:04 AM

Previous topic - Next topic

ruland

I want to make sure users cannot zoom out too far, i.e. "shrink" the image to a size smaller than the image box itself. I'm using ZoomToFit() to initialize the image, and I want to limit zooming-out to that factor.

Any simple way of achieving this?

ruland

#1
The solution I found so far is:

1. Get zoom factor after ZoomToFit:

        private void frmMain_Load(object sender, EventArgs e)
        {
            imgMap.ZoomToFit();
            AppData.SetMinZoom (imgMap.Zoom);
        }


2. Check in _ZoomChanged:

        private void imgMap_ZoomChanged(object sender, EventArgs e)
        {
            if (imgMap.Zoom < AppData.GetMinZoom())
                imgMap.Zoom = AppData.GetMinZoom();
       }

Not sure if this is the best way though.

Richard Moss

Hello,

In regards to getting the best fit zoom factor unfortunately I didn't expose this via a method or property so there isn't a way of getting it, save what you're already doing.

However, I think we could expand the load handler a touch to avoid having to have zoom changed handler as no doubt there's a bit of jank when the zoom gets set and then reset.

The ImageBox control has a ZoomLevels property which is a collection of all the incremental values that will be used when zooming in or out. What you could do is find the minimum zoom factor, then remove all values lower than this, and then insert that minimum zoom factor to the start of the list. That should then prevent ImageBox from automatically zooming beyond this.

Give that a go and let me know if it works, if so perhaps I'll add a FAQ entry or expand the demo.

Although this doesn't sound like quite the same issue, you could also check this issue on GitHub which has some sample code and may be of interest.

Regards;
Richard Moss
Read "Before You Post" before posting (https://forums.cyotek.com/cyotek-webcopy/before-you-post/). Do not send me private messages. Do not expect instant replies.

All responses are hand crafted. No AI involved. Possibly no I either.

ruland

Thanks a lot for that tip. However, I guess I'm doing it wrong.
Here is what I'm trying after loading a new image:

QuoteLoadImage();                     // Load a new image to ImageBox
            imgMap.ZoomToFit();              // Zoom out to fit image

            int zoomlevel;
            for (int level = 0; level < imgMap.ZoomLevels.Count;  level++)
                {
                zoomlevel = imgMap.ZoomLevels[level];
                if (zoomlevel < imgMap.Zoom)
                {
                    imgMap.ZoomLevels[level] = imgMap.Zoom;
                }
            }

Unfortunately modifying the first list element (imgMap.ZoomLevels[0]) only removes this element from the list instead of changing it - resulting in an error in the next loop cycle.

I probably made some silly mistake and I apologize for bothering you. Any help with this would be appreciated. Thanks  :)

Richard Moss

I tested this on the General form of the main demo and it seems to do what you want

ZoomLevelCollection levels;
int minimumZoom;

imageBox.ZoomToFit(); // HACK: no current way to get minimum zoom
minimumZoom = imageBox.Zoom;
imageBox.ActualSize();

levels = imageBox.ZoomLevels;

for (int i = levels.Count; i >0; i--)
{
  if (levels[i-1] < minimumZoom)
  {
    levels.RemoveAt(i-1);
  }
}

levels.Add(minimumZoom); // oops, forgot to implement Insert, but at least the list auto sorts


Hope that helps!

Regards;
Richard Moss
Read "Before You Post" before posting (https://forums.cyotek.com/cyotek-webcopy/before-you-post/). Do not send me private messages. Do not expect instant replies.

All responses are hand crafted. No AI involved. Possibly no I either.

ruland

Thanks, I'll try. I'm sure it will work - but unfortunately it will not do exactly what I need. I need the lowest list element to be equal to minimumZoom, while your solution only removes all entries that are smaller than that. So after applying the code the lowest element will be larger than minimumZoom and the image will not completely zoom out.

That's why I tried to replace the first elements by the value of minimumZoom. I still have no idea why it didn't work. For some reason

imgMap.ZoomLevels[level] = imgMap.Zoom;

just removed that list element, instead of replacing it.

Richard Moss

Not sure if you noticed, but I do add the minimum - it's right at the end (the code box scrolls).

As I said, I tested this and it seems to do exactly what you wanted - let me know if that's not the case.

Edit: Haven't tried doing the assignments, maybe there's a bug - I'll look into that.
Read "Before You Post" before posting (https://forums.cyotek.com/cyotek-webcopy/before-you-post/). Do not send me private messages. Do not expect instant replies.

All responses are hand crafted. No AI involved. Possibly no I either.

ruland

Sorry, my fault! I didn't scroll down!  :-[

Thanks again!

ruland

#8
I added your code now, and it works fine ... for the initial image.

However, when I load a new image into the imageBox (by assigning a new value to the Image property) it looks like the ZoomLevels collection is not reset. Debugging shows that the list still contains the same values, including the minimumZoom value as item 0.

The line

levels.Add(minimumZoom);

then produces an error, of course, because the value to be added is already present (in case the new image has the same size).

Is there a clever way to reset the imageBox entirely before assigning a new image to it? Or force the ZoomLevels list to be re-calculated?

Richard Moss

Normally it doesn't make sense to have custom zoom levels on a per-image basis, so no there's no neat trick. However, it's not too much trouble to do, just replace the for loop (and that floating Add) in my previous example with this

levels.Clear();

levels.Add(minimumZoom);

foreach (int level in ZoomLevelCollection.Default)
{
  if (level > minimumZoom)
  {
    levels.Add(level);
  }
}



I tested this and it also looks to be working perfectly.

Regards;
Richard Moss
Read "Before You Post" before posting (https://forums.cyotek.com/cyotek-webcopy/before-you-post/). Do not send me private messages. Do not expect instant replies.

All responses are hand crafted. No AI involved. Possibly no I either.

ruland

Perfect! (I wasn't aware that there is a ZoomLevelCollection.Default :) )

Thank you very much for your excellent support and assistance!