none
ScrollViewer.ActualWidth being screwy RRS feed

  • Question

  • So I am working on something where I want to detect and auto scroll a scrollviewer.  It should be really straight forward, and I have some code that was similar and works perfectly.  It is frustrating me to no end though because in the working code example ScrollViewer.ActualWidth is providing me with what appears to be the width of the visible area.  In the case where the code is not working, the actual width is always the same as desired width and content width and extent width.

    <ScrollViewer HorizontalAlignment="Stretch"
    
    										 HorizontalScrollBarVisibility="Auto"
    
    										 VerticalScrollBarVisibility="Auto"
    
    										 VerticalAlignment="Stretch"
    
    										 x:Name="FoundMe">
    
    								<tt:TimeLineControl Height="80"
    
    													ItemTemplate="{StaticResource UsedTemplateProperty}"
    
    													HorizontalAlignment="Left"
    
    													x:Name="TimeLine2"
    
    													UnitSize="10"/>
    
    							</ScrollViewer>
    
    

    So there is the scrollviewer that I am correctly detecting in my code, but for some reason it is not providing me with a useful actual width.

    Point scrollPos = e.GetPosition(_scrollViewer);
    
    				Double scrollMargin = 50;
    
    				
    
    				if (scrollPos.X >= _scrollViewer.ActualWidth- scrollMargin &&
    
    					_scrollViewer.HorizontalOffset <= _scrollViewer.ExtentWidth - _scrollViewer.ViewportWidth)
    
    				{
    
    					_scrollViewer.LineRight();
    
    				}
    
    

    There is the code where I am checking to see if I should scroll right.  I think it is an excercise in futility, but here is the markup and code that is actually giving me a valid actual width...  This code came from gong tools drag drop library, but I tweaked it just slightly for my needs.

    <ScrollViewer Grid.Row="1"
    
    										 Grid.Column="0"
    
    										 HorizontalAlignment="Stretch"
    
    										 VerticalAlignment="Stretch"
    
    										 HorizontalScrollBarVisibility="Auto"
    
    										 VerticalScrollBarVisibility="Auto">
    
    								<ListBox ItemsSource="{Binding Path=Books}"
    
    										 HorizontalAlignment="Stretch"
    
    										 VerticalAlignment="Stretch"
    
    										 HorizontalContentAlignment="Stretch"
    
    										 SelectionMode="Single"
    
    										 ItemTemplate="{StaticResource BookOutlineTemplateCompact}"
    
    										 dd:DragDrop.IsDropTarget="True"
    
    										 dd:DragDrop.IsDragSource="True"
    
    										 dd:DragDrop.DropHandler="{Binding}"
    
    										 dd:DragDrop.DragAdornerTemplate="{StaticResource BookOutlineTemplateCompact}">
    
    									<ListBox.ItemsPanel>
    
    										<ItemsPanelTemplate>
    
    											<StackPanel Orientation="Vertical" />
    
    										</ItemsPanelTemplate>
    
    									</ListBox.ItemsPanel>
    
    								</ListBox>
    
    							</ScrollViewer>
    
    

    Here is the code for that scroll:

     Point position = e.GetPosition(scrollViewer);
    
    				double scrollMargin = Math.Min(scrollViewer.FontSize * 4, scrollViewer.ActualHeight / 2);
    
    
    
        if (position.X >= scrollViewer.ActualWidth - scrollMargin &&
    
         scrollViewer.HorizontalOffset < scrollViewer.ExtentWidth - scrollViewer.ViewportWidth)
    
        {
    
         scrollViewer.LineRight();
    
        }
    
    

    So when I debug through the first bit of code my actual width is always always the full width of the contained content.  When I step through the bottom code the actual width is what I presume is the visible area's width.  So the bottom code makes it automatically scroll very nicely on drag drop work on that listbox.  The top code however never detects the scroll need because my actual width is working wrong.

    Any ideas?

     

    thanks

     

    Danny


    All that is gold does not glitter, Not all those who wander are lost
    • Edited by DannyStaten Monday, November 1, 2010 6:36 PM fix a typo
    Monday, November 1, 2010 6:35 PM

Answers

  • Thank you so much for your help.  I started by trying to call measure and arrange on the timeline control itself.  Calling those things on the parent scrollviewer did the trick!  It is finally working like I need.  Seriously, a million thanks for the help.  When I get some time after I finish the project I am doing this timeline for, I will probably put it up on codeproject or somewhere to share it and let other people use it.  It is a really handy control that I am very excited to have working.  The very final thing to polish it off was dealing with the scrollviewer when reordering.  So thanks so much for your help. 

    if ((oldW != Width)&&(_scrollViewer!=null))
    			{
    				var available = LayoutInformation.GetLayoutSlot(_scrollViewer);
    				Size s = new Size(available.Width, available.Height);
    				_scrollViewer.Measure(s);
    				_scrollViewer.Arrange(available);
    			}
    
    So there is the final code in my control.  _scrollviewer is instantiated in an on render event.  So if the control is housed inside a scrollviewer the code finds the first parent scrollviewer.  Then this is in code that rebuilds the background because that has to run any time the size of the timeline changes.  I hadn't thought to call mearusre on the scrollviewer.  My initial thoughts with the suggestion to use measure and arrange was to measure my timeline and I thought that it would then bubble information up to any parent elements.  So the round about path getting me to measure on the scrollviewer got me to the answer.  Thanks again.
    All that is gold does not glitter, Not all those who wander are lost
    • Marked as answer by DannyStaten Friday, November 5, 2010 2:57 AM
    Friday, November 5, 2010 2:57 AM
  • Quote:

    So when the content is larger than the scrollviewer and we are scrolling:
    ActualWidth = ?? Width of the visible content ... that's what I observed in the working code when debugging
    ExtentWidth = ?? Width of the content itself
    ViewportWidth =?? not sure what this one is

    ActualWidth is the actual width of the Scrollviewer always. Not the visible content. ViewportWidth is the width of the visible content. Whatever the size of the inner element, if you do not change the size of scrollviewer, the ActualWidth will not be changed. But the ExtentWidth and the ViewportWidth dependent the size of the inner element. Please see below diagram:

    Why ScrollViewer.ActualWidth equals ScrollViewer.ExtentWidth? Since the right edge of the inner element is just against the right edge of the Scrollviewer. Is it? If not, so should update the layout of the scrollviewer, the inner element. If you invoke the Scrollviewer.Measure and Arrange, you should transfer the Scrollviewer.RenderSize, if invoke methods of the inner element, you need the render size of the lement.

    Hope this helps.

    Sincerely,
    Bob Bao


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download all in one code framework !
    • Marked as answer by DannyStaten Friday, November 5, 2010 2:50 AM
    Thursday, November 4, 2010 8:51 AM
    Moderator

All replies

  • Hi DannyStaten,

    What is the actual width of the tt:TimeLineControl ? Could you please try to specify the Width property of the tt:TimeLineControl and then test to check the ActualWidth of the scroll viewer? Perhaps, you may try to set the Height/Width of the ScrollViewer to the outside panel:

    <Grid Name="Container">
     <ScrollViewer Name="ScrollViewer" HorizontalScrollBarVisibility="Auto"
            Height="{Binding ElementName=Container, Path=ActualHeight}"
            Width="{Binding ElementName=Container, Path=ActualWidth}">
       ...
     </ScrollViewer>
    </Grid>
    

    If the ScrollViewer.ActualWidth equals ScrollViewer.ExtentWidth, means the content of the ScrollViewer just fills the Width, the ActualWidth of the inner element equals the ScrollViewer.RenderSize.Width, no horizontal Scollbar can display.

    Sincerely,
    Bob Bao

    MSDN Subscriber Support in Forum 

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download all in one code framework !
    Tuesday, November 2, 2010 5:45 AM
    Moderator
  • The timeline control is an extended canvas and I am setting the Width property directly.  The scroll viewer works properly in that it properly detects that it should scroll and scrolls correctly.  The problem is that my ActualWidth when I pull it from the scrollviewer is always the same as the full width of the timeline control.  Somehow the scrollviewer knows that it should scroll, but doesn't expose any value to me that tells me the width of the visible portion of the scrolled contents.

    Basically, as I update the timeline control, I redraw a rectangle with my background brush to be the width of the full timeline and then set the Width property of the timeline control to the width of that rectangle.

    So I know that my timeline control's width property is directly set.  Still ActualWidth is not reporting properly for me for some reason.


    All that is gold does not glitter, Not all those who wander are lost
    Tuesday, November 2, 2010 5:56 AM
  • Hi Danny,

    I am still not sure if the ActualWidth of the ScrollViewer is calculated by the layout system to the extent width. "The problem is that my ActualWidth when I pull it from the scrollviewer is always the same as the full width of the timeline control." As we know, ActualWidth is changed by layout, so does the pulled element invoke the layout change (especially the Measure and Arrange methods). Simply, you could try to invoke the LayoutUpdate() after pulling the element.

    Sincerely,
    Bob Bao


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download all in one code framework !
    Tuesday, November 2, 2010 8:55 AM
    Moderator
  • I am invoking them now, but my scrollviewer is still behaving differently in my scenario than the very similar scenario that is behaving correctly.  My question on both measure and arrange is that they both pass something in indicating their available space.  So do I have to go up the visual tree to the containing parent and do something to try and find out the available space?  My first attempt calling measure and arrange is just passing the full width and height of the control in.

    Size s = new Size(bg.Width, bg.Height);//bg is the rectangle whose width is the full width and height of the timeline control
    			Measure(s);
    			Arrange(new Rect(new Point(0,0),new Point(bg.Width,bg.Height)));
    

    So what is the best way to go about actually determining the correct size and then rect to send to measure and arrange?  That aspect of using arrange and measure has been unclear to me so any guidance would be very handy.

    As it is right there, there is no difference between actual width, extent width and viewport width.  So that means that scrolling code that is working in other similar scenarios won't work in mine. 


    All that is gold does not glitter, Not all those who wander are lost
    Tuesday, November 2, 2010 10:11 PM
  • Hi Danny,

    I think we need to transfer the Scrollviewer.RenderSize to the Measure and the Arrange methods, since we need to update the layout of the scrollviewer and re-calculate the size of the scrollviewer.

    Regarding to the relationship of the actual width, extent width and viewport width, if the inner element (here is the timeline control) just fills the area of the scrollview, the three values are same. For the actual width, it is the rendered width of the element, or we can say it is the actual width we can see. The extent width is the value that contains the horizontal size of the extent, it is related with the scrollbar value. And the viewport width is the width value of the content's viewport.

    Sincerely,
    Bob Bao


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download all in one code framework !
    Wednesday, November 3, 2010 11:17 AM
    Moderator
  • So what exactly do you mean when you say "transfer the Scrollviewer.RenderSize to the Measure and Arrange methods"?

    So when the content is larger than the scrollviewer and we are scrolling:
    ActualWidth = ?? Width of the visible content ... that's what I observed in the working code when debugging
    ExtentWidth = ?? Width of the content itself
    ViewportWidth =?? not sure what this one is

    What I don't get is in my non-working code, the scrollbar correctly scrolls and auto enables and disables.  So I would think that the scrollbar would know the values it needs to scroll just fine, and as such those values would be reflected properly in the properties.  However when I look at those properties it always has them just the same as if the scrollviewer didn't think it had to scroll at all.

    thanks for your help btw.  I spent some time reading up on the arrange code and trying all sorts of variations of it last night and got nowhere.  Hopefully I can get this working with your help.

     

     


    All that is gold does not glitter, Not all those who wander are lost
    Wednesday, November 3, 2010 2:42 PM
  • Quote:

    So when the content is larger than the scrollviewer and we are scrolling:
    ActualWidth = ?? Width of the visible content ... that's what I observed in the working code when debugging
    ExtentWidth = ?? Width of the content itself
    ViewportWidth =?? not sure what this one is

    ActualWidth is the actual width of the Scrollviewer always. Not the visible content. ViewportWidth is the width of the visible content. Whatever the size of the inner element, if you do not change the size of scrollviewer, the ActualWidth will not be changed. But the ExtentWidth and the ViewportWidth dependent the size of the inner element. Please see below diagram:

    Why ScrollViewer.ActualWidth equals ScrollViewer.ExtentWidth? Since the right edge of the inner element is just against the right edge of the Scrollviewer. Is it? If not, so should update the layout of the scrollviewer, the inner element. If you invoke the Scrollviewer.Measure and Arrange, you should transfer the Scrollviewer.RenderSize, if invoke methods of the inner element, you need the render size of the lement.

    Hope this helps.

    Sincerely,
    Bob Bao


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download all in one code framework !
    • Marked as answer by DannyStaten Friday, November 5, 2010 2:50 AM
    Thursday, November 4, 2010 8:51 AM
    Moderator
  • Thanks for taking the time to post that image.  That helps me keep it straight very nicely.  When I get home from work this evening. Up until now I was calling measure and arrange on my timeline control not on the scrollviewer.  I will take a look at that and give it a shot when I get home from work this evening.

    Thanks for being so helpful.


    All that is gold does not glitter, Not all those who wander are lost
    Thursday, November 4, 2010 2:33 PM
  • Thank you so much for your help.  I started by trying to call measure and arrange on the timeline control itself.  Calling those things on the parent scrollviewer did the trick!  It is finally working like I need.  Seriously, a million thanks for the help.  When I get some time after I finish the project I am doing this timeline for, I will probably put it up on codeproject or somewhere to share it and let other people use it.  It is a really handy control that I am very excited to have working.  The very final thing to polish it off was dealing with the scrollviewer when reordering.  So thanks so much for your help. 

    if ((oldW != Width)&&(_scrollViewer!=null))
    			{
    				var available = LayoutInformation.GetLayoutSlot(_scrollViewer);
    				Size s = new Size(available.Width, available.Height);
    				_scrollViewer.Measure(s);
    				_scrollViewer.Arrange(available);
    			}
    
    So there is the final code in my control.  _scrollviewer is instantiated in an on render event.  So if the control is housed inside a scrollviewer the code finds the first parent scrollviewer.  Then this is in code that rebuilds the background because that has to run any time the size of the timeline changes.  I hadn't thought to call mearusre on the scrollviewer.  My initial thoughts with the suggestion to use measure and arrange was to measure my timeline and I thought that it would then bubble information up to any parent elements.  So the round about path getting me to measure on the scrollviewer got me to the answer.  Thanks again.
    All that is gold does not glitter, Not all those who wander are lost
    • Marked as answer by DannyStaten Friday, November 5, 2010 2:57 AM
    Friday, November 5, 2010 2:57 AM
  • That is great, yesterday, I start to know what is a TimeControl, that is a Time line we could record the evens/news on a specific time, and we could view the whole event from the Time line, very nice. Cool.
    Best day, Best life
    Friday, November 5, 2010 3:13 AM
  • Yeah there are a lot of different takes on timelines.  My control is more for editing items start and end times relative to each other.  There are some timeline controls out there that probably do what you are looking for.  Mine probably could work for you as well, but I don't know.  Mine is for managing time line data and editing when events or items start and end.  It is gearing towards helping manage things like screenplays, novels and the like.  So I am setting up an application where you can manage things and see what is happening at any given time etc.
    All that is gold does not glitter, Not all those who wander are lost
    Friday, November 5, 2010 3:17 AM