Wpf Scroll Image using ScrollViewer with dynamic Stretch -


i working on simple imageviewer app. control stretch property on binding based on viewmodel property.

the problem occurs when change stretch attribute based on 'combobox', bound viewmodel, , image 'cuts off' corners of wide image when using 'uniformtofill'. hence use of scrollviewer able scroll image content.

the problem scrollviewer doesn't seem show scrollbars me able scroll.

wpf markup:

<grid.columndefinitions>     <columndefinition width="auto" />     <columndefinition width="auto"/>     <columndefinition width="*"/> </grid.columndefinitions> <grid.rowdefinitions>     <rowdefinition height="auto"/>     <rowdefinition height="*"/> </grid.rowdefinitions>  <!-- other grids removed -->  <grid name="container" grid.column="2" grid.row="0" grid.rowspan="2">     <scrollviewer horizontalscrollbarvisibility="visible" verticalscrollbarvisibility="visible">                             <image source="{binding selectedphoto.value.image}"                  stretch="{binding imagestretch}" name="photoimage" />                    </scrollviewer>            </grid> 

i understand if set fixed height , width scrollviewer , image, work. want dynamically:

  • the scrollview have height , width parent 'grid(contaioner)' control.
  • the image have height , width itself, take stretch account in calculation.

possible solve actualheight, actualwidth? , dependecyproperty?

this impossible, or should doesn't make lot of sense expect scrollviewer know boundaries of image stretch = uniformtofill. according msdn:

uniformtofill: content (your image) resized fill destination dimensions (window or grid) while preserves native aspect ratio. if aspect ratio of destination rectangle differs source, source content clipped fit in destination dimensions (therefore image cutted off).

so think need here use uniform + proper scaling instead of uniformtofill.

the solution when stretch set uniformtofill must set uniform , image.width = image actual width * scalingparam , image.height= image actual height * scalingparam, scalingparam = grid.width (or height) / image actual width (or height). way scrollviewer boundaries same image scaled size.

i've provided working solution give idea, i'm not sure how suitable case here is:

first defined simple view-model images:

    public class imageviewmodel: inotifypropertychanged     {      // implementation of inotifypropertychanged ...      private bitmapframe _bitmapframe;      public imageviewmodel(string path, stretch stretch)     {          // determining actual size of image.         _bitmapframe = bitmapframe.create(new uri(path), bitmapcreateoptions.delaycreation, bitmapcacheoption.none);          width = _bitmapframe.pixelwidth;         height = _bitmapframe.pixelheight;         scale = 1;         stretch = stretch;     }      public int width { get; set; }     public int height { get; set; }      double _scale;     public double scale     {                 {             return _scale;         }         set         {             _scale = value;             onpropertychanged("scale");         }     }     stretch _stretch;     public stretch stretch     {                 {             return _stretch;         }         set         {             _stretch = value;             onpropertychanged("stretch");         }     } } 

in above code bitmapframe used determine actual size of image. did initializations in mainwindow (or main view-model):

    // displaying image     imageviewmodel _imagevm;     public imageviewmodel imagevm     {                 {             return _imagevm;         }         set         {             _imagevm = value;             onpropertychanged("imagevm");         }     }      // selected stretch type     stretch _stretch;     public stretch currentstretch     {                 {             return _stretch;         }         set         {             _stretch = value;             //imagevm should notified refresh ui bindings             imagevm.stretch = _stretch;             onpropertychanged("imagevm");             onpropertychanged("currentstretch");         }     }      // list of stretch types     public list<stretch> stretchlist { get; set; }     public string imagepath { get; set; }     public mainwindow()     {         initializecomponent();         datacontext = this;          // sample image path         imagepath = @"c:\users\...\yourfile.png";          stretchlist = new list<stretch>();         stretchlist.add( stretch.none);         stretchlist.add( stretch.fill);         stretchlist.add( stretch.uniform);         stretchlist.add( stretch.uniformtofill);          imagevm = new imageviewmodel(imagepath, stretch.none);          currentstretch = stretchlist[0];      } 

my xaml looks this:

<grid>     <grid.rowdefinitions>         <rowdefinition height="*"/>         <rowdefinition height="auto"/>         <rowdefinition height="auto"/>     </grid.rowdefinitions>     <grid.columndefinitions>         <columndefinition width="*"/>     </grid.columndefinitions>     <grid grid.row="0" grid.column="0" >         <grid.resources>             <local:multiconverter x:key="multic"/>         </grid.resources>         <scrollviewer horizontalscrollbarvisibility="visible" verticalscrollbarvisibility="visible">             <image source="{binding imagepath}" name="photoimage">                 <image.stretch>                     <multibinding converter="{staticresource multic}">                         <binding path="imagevm" />                         <binding relativesource="{relativesource ancestortype=window}" path="actualwidth"/>                         <binding relativesource="{relativesource ancestortype=window}" path="actualheight"/>                     </multibinding>                 </image.stretch>                 <image.layouttransform>                     <scaletransform scalex="{binding imagevm.scale}" scaley="{binding imagevm.scale}"                         centerx="0.5" centery="0.5" />             </image.layouttransform>         </image>         </scrollviewer>     </grid>     <combobox grid.row="2" grid.column="0" itemssource="{binding stretchlist}" selecteditem="{binding currentstretch}" displaymemberpath="."/> </grid> 

as can see, i've used multi-value converter takes 3 arguments: current image view-model , window width , height. arguments used calculate current size of area image fills. i've used scaletransform scale area calculated size. code multi-value converter:

public class multiconverter : imultivalueconverter {     public object convert(         object[] values, type targettype, object parameter, cultureinfo culture)     {         if (values[0] imageviewmodel)         {             var imagevm = (imageviewmodel)values[0];              // if user selects uniformtofill             if (imagevm.stretch == stretch.uniformtofill)             {                 var windowwidth = (double)values[1];                 var windowheight = (double)values[2];                  var scalex = windowwidth / (double)imagevm.width;                 var scaley = windowheight / (double)imagevm.height;                  // since it's "uniform" max(scalex, scaley) used scaling in both horizontal , vertical directions                 imagevm.scale = math.max(scalex, scaley);                  // "uniformtofill" "uniform + proper scaling"                 return stretch.uniform;             }             // if user selects other stretch types             // remove scaling             imagevm.scale = 1;             return imagevm.stretch;         }         return binding.donothing;     }      public object[] convertback(         object value, type[] targettypes, object parameter, cultureinfo culture)     {         throw new notsupportedexception();     } } 

Comments

Popular posts from this blog

javascript - Using jquery append to add option values into a select element not working -

Android soft keyboard reverts to default keyboard on orientation change -

Rendering JButton to get the JCheckBox behavior in a JTable by using images does not update my table -