Changing Height of ScrollViewer Content dynamically through code - wpf

I'm stuck at a point where I have to change the height of scroll viewer content irrespective of the size the contents inside it need.
The reason is that I will have a very long Image and I don't want to slow my GUI while scrolling. So,I cut the long image and I render stitched images of it depending on current scrollviewer vertical offset.
I tried to achieve it by putting a Hidden Long image so that it won't render on scroll and there won't be any lag. But I don't feel good about it.
Can someone help me to dynamically increase the ScrollViewer content Height?
The code is here:
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="9" Grid.RowSpan="12" Margin="10,60,10,0" >
<Image x:Name="StitchedImage" SnapsToDevicePixels="True" HorizontalAlignment="Stretch" Stretch="Fill"/>
<ScrollViewer x:Name="ImageScrollViewer" Background="Transparent" ScrollChanged="ImageScrollViewer_ScrollChanged" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled">
<Grid VerticalAlignment="Top" HorizontalAlignment="Left" >
<Image x:Name="longImageHidden" SnapsToDevicePixels="True" Visibility="Hidden"/>
</Grid>
</ScrollViewer>
</Grid >

I think It would be really cool if I can just give a blank image specifing just height and width without any source. Is that even possible?
Yes, it is:
<Image Height="1000" />
You could also set the Height of a ScrollViewer and any other FrameworkElement derived type.

Related

Prevent Scrollviewer from stopping in the middle of an element

I'm developing a touch application that uses a ScrollViewer and a StackPanel to make a carousel of images. The images are added to the stackpanel and the user slides them using fingers.
The problem is that I'm having a visual problem, what I want is to prevent the scrollviewer from stopping in the middle of two images. Just like this:
The idea is: when the inertia is stopping, automatically scroll to the nearest image.
Thats my XAML:
<ScrollViewer x:Name="crlGalleryPlayer1" Panel.ZIndex="303" HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" PanningMode="HorizontalOnly"
PanningDeceleration="0.01" PanningRatio="1"
ManipulationBoundaryFeedback="crlCarrusel_Viewer_ManipulationBoundaryFeedback"
IsManipulationEnabled="True" Width="1920" Height="1080" Margin="0,0,0,0" CanContentScroll="False" Visibility="Hidden">
<StackPanel Name="pnlCarrusel_ViewerPlayer1" Visibility="Visible"
ScrollViewer.IsDeferredScrollingEnabled="False" IsManipulationEnabled="True" Margin="0,0,0,0"
Orientation="Horizontal" Panel.ZIndex="303">
</StackPanel>
</ScrollViewer>
I've tried to start from the ManipulationCompleted event of the Scrollviewer but it's not getting fired, only ManipulationStarted
Thank you all.

Horizontal scrolling a row of ViewBox images in Xaml

I'm working on a UWP Windows 10 app with a XAML UI. One of my pages requires that images fill the height of the window (or screen in tablet mode) and uniformly scale as one long row of images from left to right (going off-screen). I've got this set up perfectly using ViewBoxes for the images inside of a StackPanel set to a Horizontal Orientation like so:
<StackPanel Orientation="Horizontal">
<Viewbox>
<Image Source="http://lorempixel.com/200/200/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/400/600/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/700/700/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/300/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/500/" />
</Viewbox>
</StackPanel>
The intention is for the images to flow off-screen with a horizontal scroll that allows the user to pan from left to right to see the gallery of images as one long row.
I've tried enabling HorizontalScrollMode on the parent StackPanel like so:
<StackPanel Orientation="Horizontal" ScrollViewer.HorizontalScrollMode="Enabled">
But this did not enable any scrolling at all.
I also tried to wrap everything inside of a ScrollViewer like so:
<ScrollViewer HorizontalScrollMode="Enabled">
<StackPanel Orientation="Horizontal">
<Viewbox>
<Image Source="http://lorempixel.com/200/200/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/400/600/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/700/700/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/300/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/500/" />
</Viewbox>
</StackPanel>
</ScrollViewer>
But this completly breaks my ViewBox layout by shrinking all the images so they fit within a small portion of the screen and no longer fill the window/tablet height.
I've tried a number of other variations with similar results. Does anyone have some suggestions for solving this? Let me know if you need more info.
You do need a ScrollViewer to enable scrolling, although you might need to set a few properties to make it only scroll horizontally as mentioned in Windows 8 ListView with horizontal item flow
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.ZoomMode="Disabled"
Now the Viewbox is not the most controllable... control. You could try using the
SquareGrid panel from my toolkit instead of the Viewboxes. Maybe simplify it a bit. If that isn't enough - you could add some bindable properties that would update when the size of your window changes and bind the Width and Height of these images to these properties. Note that you can't use ActualWidth or ActualHeight because these don't raise change notifications on size changes.
<GridView x:Name="ImageGridView"
SelectedItem="{x:Bind ViewModel.SelectedLocation, Mode=TwoWay}"
Margin="10,0"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.HorizontalScrollMode="Auto"
Grid.Row="4" Grid.ColumnSpan="5"
ItemsSource="{x:Bind ViewModel.CheckedLocations}"
ItemContainerStyle="{StaticResource PinsGridViewItemStyle}"
ItemTemplate="{StaticResource ImageOverlayGalleryFolderDataTemplate}" >
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid MaximumRowsOrColumns="1"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
This is my code to show 1 row of photos. You can adjust by the MaxiumRowsOrColumns. Also note that both HorizontalScrollBarVisibility and HorizontalScrollMode are present to custom as you want to.

WPF: Using Grids

I created a grid and inside this grid it contains a TextBlock. When I maximize or adjust the size of the window the content of the TextBlock doesn't stay in the center of the Grid.
Tried to keep this as short as possible. :>
<Grid>
<Grid HorizontalAlignment="Left" Height="46" VerticalAlignment="Top" Width="515">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Welcome! Use the functionalities below.</TextBlock>
</Grid>
</Grid>
Grids, by default, auto expand. That is, they take up as much area as they are allowed. Your outer grid will take up the entire client area of the window while the inner grid will stick to the top left of the outer grid, just as you have coded it to. If you want the inner grid to be centered, then do something like
<Grid>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="46" Width="515">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Welcome! Use the functionalities below.</TextBlock>
</Grid>
</Grid>
You don't even have to include the horizontal and vertical alignment as grids will automatically centre.
If you just want to centre the TextBlock ...
<Grid>
<Grid>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Height="46" Width="515">Welcome! Use the functionalities below.</TextBlock>
</Grid>
</Grid>
The text can be centred using the TextAlignment property.
I hope this helps

Why ZIndex is not considered in arrange override of a stackpanel

Here is what I tried to to:
Added a stackpanel to my window (Orientation: Horizontal)
Added a set of buttons to it
Set the first button's ZIndex to be higher than the second one
Increased the width of the first button
What I expected:
I expected the first button to be on top of the second button (atlest overlay)
StackPanel's width should not change unless the width of the first button is no more sufficient
What is happening actually:
First button's width increases and the second button moves towards the right accordingly. They stay on the same plane
StackPanel's width increases with increase in the first button's width
Here is my question:
I know that stackpanel has not considered ZIndex while arranging the items within itself, BUT WHY?? Shouldn't it consider the ZIndex of its children while arranging them???
The Stackpanel 'stacks' its children based on their widths, i.e. if you increase the width of an item (or increase its margin), the stackpanel will simply expand to accomodate this. If you want to force items within a stackpanel to overlap, you will have to change their location after the layout has been computed. You can perform this using a RenderTransform. See the example below:
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Button Content="One" Canvas.ZIndex="10">
<Button.RenderTransform>
<TranslateTransform X="10"/>
</Button.RenderTransform>
</Button>
<Button Content="One"/>
<Button Content="One"/>
<Button Content="One"/>
</StackPanel>
And yes, the ZIndex is respected. This is an attached proepry of Canvas, however, it seems to be used by the rendering engine directly rather than by Canvas, hence it works in the above code.
I tried to find some relevant info about how to set the z index of wpf layout elements and panels. Using a Canvas comes with a different set of positioning issues which I simply hadn't the time to investigate. Here is a simple solution using the Panel.ZIndex property in xaml.
<Grid>
<Border Width="100" Height="100" Margin="0,0,50,50" Panel.ZIndex="1" Background="Navy" Opacity="0.3"
VerticalAlignment="Top" HorizontalAlignment="Left">
</Border>
<Border Width="100" Height="100" Margin="50,50,0,0" Background="Fuchsia" Opacity="0.3">
</Border>
The resulting two square border elements will overlap. One can use stackpanels instead of borders and use this logic to overlap anything easily.
Here is the same code adapted to the button problem:
<Grid>
<StackPanel Panel.ZIndex="10" Margin="20,20,0,0" HorizontalAlignment="Left">
<Button Content="One" Width="50" Height="40">
</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="50,0,0,0" >
<Button Content="Two" Width="50" Height="40"/>
<Button Content="Three" Width="50" Height="40"/>
<Button Content="Four" Width="50" Height="40"/>
</StackPanel>
</Grid>

Perfect Center on WPF Canvas

Since the canvas requires a Top/Left for placement, if you want to center something, is adding a grid at the proper Canvas.Top with HorizontalAlignment="Center" the best way to do it, or is there a better way?
This snip is a 150X300 canvas, with some content centered in a grid ....
<Canvas Width="150" Height="300">
<Grid Canvas.Top="75" Width="106" HorizontalAlignment="Center">
{whatever you want centered}
</Grid>
</Canvas>
Guy's solution works, but you may have to tweak z-order and visibility if you're juggling hit testing.
Another alternative is having the Grid inside the Canvas (as you've specified in your XAML) with the Height/Width set to (or bound to) the Height/Width of the Canvas. Then setting HorizontalAlignment/VerticalAlignment to Center for the contents of your Grid.
I'm not sure if this will meet your exact requirement, but if you put both the canvas and the content inside a grid as peers, it will get you a centered result:
<Grid>
<Canvas Width="150" Height="300"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Width="106" Content="Click"/>
</Grid>

Resources