ViewportControl and Images - how to avoid endless scrolling - c#

I am coding a kind of map (for a very simple game) which is (for now) an image.
I want the user to be able to scroll the image around and resize it.
I tried a lot around, and currently i am using the ViewportControl, and the XAML looks more or less like the following (see below).
I works as it does all i need - BUT the image itself can be scrolled within the ViewportControll without any boundaries. As a result the image scrolls away.
Question: is there any way to prevent the Viewportcontroll to scroll the image out of a specified region?
<ViewportControl x:Name="viewport" Height="600" Width="440">
<Canvas x:Name="ParentCanvas" Background="Red" MaxHeight="600" MaxWidth="440">
<Image x:Name="MapImage" Source="/Artwork/map.png" RenderTransformOrigin="0.2,0.2" Stretch="UniformToFill" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="-200" Canvas.Top="-300" Width="1320" Height="1800">
<Image.RenderTransform>
<CompositeTransform x:Name="transform" />
</Image.RenderTransform>
</Image>
<tk:GestureService.GestureListener>
<tk:GestureListener PinchStarted="OnPinchStarted" PinchDelta="OnPinchDelta" />
</tk:GestureService.GestureListener>
</Canvas>
</ViewportControl>

After testing around i have a solution for my problem.
The most important part is to add the Bounds-tag to the ViewportControl as you can see below.
The scrolling is then handled by the ViewportControl itself. The pinch-gesture must be done manually, but it is possible, because the events will reach the correct UI-Element (here: the image).
In the example below, i used the Windows Phone Toolkit to handle the event more easily.
<ViewportControl x:Name="viewportCtrl" ScrollViewer.VerticalScrollBarVisibility="Visible" VerticalContentAlignment="Center" Bounds="0,0,1320,1800">
<Canvas x:Name="ParentCanvas" Background="Red" Height="1000" Width="800">
<Image x:Name="MapImage" Source="/Artwork/map.png" RenderTransformOrigin="0.2,0.2" >
<Image.RenderTransform>
<CompositeTransform x:Name="transform" />
</Image.RenderTransform>
</Image>
<Canvas x:Name="BlueCanvas" Top="100" Left="200" Background="Blue" Height="180" Width="180"></Canvas>
<tk:GestureService.GestureListener>
<tk:GestureListener PinchStarted="OnPinchStarted" PinchDelta="OnPinchDelta" />
</tk:GestureService.GestureListener>
</Canvas>
</ViewportControl>

Related

How to overflow a shadow from a fixed Height container

I found here that the DropShadows can't overflow over component with a fixed Height.
I tried to add somme ClipToBounds="False" in every containers but it doesn't seems to work.
you will see the problem if you create a new project with this code :
<Window .... >
<StackPanel x:Name="Sp2" Margin="20" Height="47" ClipToBounds="False">
<Rectangle Height="40" Fill="Purple" Margin="4" ClipToBounds="False">
<Rectangle.Effect>
<DropShadowEffect Opacity=".4" BlurRadius="13" ShadowDepth="4" Direction="288"/>
</Rectangle.Effect>
</Rectangle>
</StackPanel>
</Window>
Here is a screenshot of the problem :
On the left that's what i want, a shadow overflowing on a fixed height component, and on the right that's what i have.
As I work in a team and I'm developping every UI components for our application and I'd like other people of my team not to have to pay attention to these kind of details. How can we avoid this behavior, and if we can't what would be the guidelines to avoid this clipping effect ?
You can override GetLayoutClip() to stop most things clipping.
As explained here.
http://drwpf.com/blog/2007/12/28/cliptoboundsmaybe/
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace wpf_99
{
public class NoClipStackPanel : StackPanel
{
protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
return ClipToBounds ? base.GetLayoutClip(layoutSlotSize) : null;
}
}
}
Markup
<Grid>
<local:NoClipStackPanel x:Name="Sp2" Margin="20" Height="47" ClipToBounds="False">
<Rectangle Height="40" Fill="Purple" Margin="4" ClipToBounds="False">
<Rectangle.Effect>
<DropShadowEffect Opacity=".4" BlurRadius="13" ShadowDepth="4" Direction="288"/>
</Rectangle.Effect>
</Rectangle>
</local:NoClipStackPanel>
</Grid>
One possibility to solve this issue is similar to solution of another problem, when trying to apply effects to control containing text and text become blurred. The idea is to detach effect, by applying it to something else, while ensuring that visually it looks like effect was applied to where you need it.
In your case you can do something like this:
<Grid>
<Rectangle Width="{Binding ActualWidth, ElementName=rect}"
Height="{Binding ActualHeight, ElementName=rect}"
Fill="Green">
<Rectangle.Effect>
<DropShadowEffect ShadowDepth="30" />
</Rectangle.Effect>
</Rectangle>
<StackPanel Margin="20" Height="47">
<Rectangle x:Name="rect" Height="40" Fill="Purple" Margin="4">
<!-- remove effect from here -->
</Rectangle>
</StackPanel>
</Grid>
The effect is not anymore on nested rectangle, but on some element (I've used Rectangle) which is added below StackPanel (since its transparent) in visual tree. If you do it like this than shadow can now extend itself to whole grid (which takes whole window size).

Shadow effect and blurry text

I would like to add shadows outside tiles in my WPF application, but when I do, the text inside tiles is blurry. I tried this solution: WPF DropShadowEffect Causing Blurriness , but unfortunately shadow effect seems not to work at all. Are there any special attributtes in Rectangle which should be set? Could you give me some clues?
i got the same issue and solved this by using UseLayoutRounding="True"
EDIT
<Grid UseLayoutRounding="True" VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.Effect>
<DropShadowEffect ShadowDepth="0"
Color="Black"
BlurRadius="20" />
</Grid.Effect>
<!-- your content -->
<Grid Background="White">
<TextBlock Text="Test" FontSize="20" Margin="10" />
</Grid>
</Grid>
results this
hope that helps

Image cropping windows phone

I want to crop an image in wp7 such that only some part of it appears in an imagebox (or just the 'image' control). I used image.clip, but it actually retains the whole image, and just whitens the 'to-be-cropped' portion. How can i crop the image so that the cropped image is the resultant image?
Note: I am looking for a way in xaml
This is working for me just fine with Light and Dark themes. I'm showing a square image inside an ellipse clip and adding a border around it; it's not showing through.
<Grid>
<Ellipse
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="78"
Height="78"
StrokeThickness="5"
Stroke=""/>
<Image
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="UniformToFill"
Source="{Binding MediumPhotoUrl}"
Width="64"
Height="64" >
<Image.Clip>
<EllipseGeometry RadiusX="32" RadiusY="32" Center="32,32"/>
</Image.Clip>
</Image>
</Grid>
Are you sure you're doing clip correctly?
If you know the size of the initial image, you can calculate the required transforms, for example:
<Image Width="100" Height="100">
<Image.Clip>
<!-- Image after clipping takes only a small area in the center -->
<RectangleGeometry Rect="25,25,50,50"/>
</Image.Clip>
<Image.RenderTransform>
<!-- Transforms Rect(25,25,50,50) into Rect(0,0,100,100) -->
<TransformGroup>
<TranslateTransform X="-25" Y="-25"/>
<ScaleTransform ScaleX="2" ScaleY="2"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
I don't know a way of doing it automatically (without code behind).

How to place a slider over on top of a videobrush

I am attempting to create a slider control which will determine the scaletransform of a videobrush in my MainPage, and was wondering if it was possible to somehow place this slider on top of the videobrush (which I would like to be full screen)? Currently I am using a grid for my layout where a videobrush takes up the whole screen except for two buttons on the bottom of the screen, but I would like to possibly use a canvas and place this slider in a way that would account for the current and future screen sizes of a Windows Phone device. I am unsure of how to exactly accomplish this without setting constant dimensions for the slider. For instance the slider may be placed horizontally near the bottom of the screen in Portrait mode and would have a 50 pixel space between the left and right sides. Could someone assist with how this could be done?
EDIT
Placing a single child element over the videobrush works, although I would like to place more than one slider which gives an error. I also wanted to add information above and below each slider so I chose a stackpanel to do this (yet only one stackpanel as a child element is allowed?).
<Border x:Name="videoRectangle" Grid.Row="0" Grid.ColumnSpan="2" >
<Border.Background>
<VideoBrush x:Name="viewfinderBrush">
<VideoBrush.RelativeTransform>
<CompositeTransform x:Name="viewfinderBrushTransform" CenterX=".5" CenterY=".5" Rotation="90" />
</VideoBrush.RelativeTransform>
</VideoBrush>
</Border.Background>
<!--<StackPanel VerticalAlignment="Top">
<TextBlock x:Name="resolutionValueTextBlock" HorizontalAlignment="Center" Text="{Binding Value, ElementName=resolutionSlider}"/>
<Slider x:Name="resolutionSlider" HorizontalAlignment="Stretch" Margin="50,5,50,5"/>
<TextBlock x:Name="resolutionTextBlock" HorizontalAlignment="Center" Text="resolution"/>
</StackPanel>-->
<StackPanel VerticalAlignment="Bottom">
<TextBlock x:Name="zoomNumberTextBlock" HorizontalAlignment="Center" Text="{Binding Value, ElementName=zoomSlider}"/>
<Slider x:Name="zoomSlider" HorizontalAlignment="Stretch" Margin="50,5,50,5"/>
<TextBlock x:Name="zoomTextBlock" HorizontalAlignment="Center" Text="zoom"/>
</StackPanel>
</Border>
If possible I would like both stackpanels to be available, but if not I guess I would have to use the bottom one only.
Stay with the Grid:
<Grid>
<Rectangle>
<Rectangle.Fill>
<VideoBrush ... />
</Rectangle.Fill>
</Rectangle>
<Slider HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Margin="50,5,50,5"/>
</Grid>

Transforms on WriteableBitmap

I'm writing app in WP7 mango and try to transform image loaded from binded to WriteableBitmap in XAML for example like this:
<Grid>
<ScrollViewer>
<Image Source="{Binding SourceImage}">
<Image.RenderTransform>
<RotateTransform Angle="{Binding RotateAngle}"/>
</Image.RenderTransform>
</Image>
</ScrollViewer>
</Grid>
I bind Angle with my property Angle in my ViewModel, i change from slider this value but image doesn't rotate. Raising property changed is working correct.
When i do it with image loaded from contets file, static image, it works.
Really weird. I have yet to understand where the problem comes from, but you can get around using Projection instead of RenderTransform:
<Grid>
<ScrollViewer>
<Image Source="{Binding SourceImage}">
<Image.Projection>
<PlaneProjection RotationZ="{Binding RotateAngle}" />
</Image.Projection>
</Image>
</ScrollViewer>
</Grid>
Edit:
Ok, actually it seems the problem comes from the ScrollViewer rather than the Image. Set the RotateTransform directly on the ScrollViewer and it should work:
<Grid>
<ScrollViewer>
<ScrollViewer.RenderTransform>
<RotateTransform Angle="{Binding RotateAngle}" />
</ScrollViewer.RenderTransform>
<Image Source="{Binding SourceImage}" />
</ScrollViewer>
</Grid>
Or put the Image inside a Grid inside the ScrollPanel:
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Image Source="{Binding SourceImage}">
<Image.RenderTransform>
<RotateTransform Angle="{Binding RotateAngle}" />
</Image.RenderTransform>
</Image>
</Grid>
</ScrollViewer>
But I'm still clueless about why this happens.
Edit 2:
Ok I found the bug. It has been introduced in the last WP7 builds. Basically, the scrollviewer will overwrite the RenderTransform of its child if it's different than its own.
So you have three solutions:
Define the RenderTransform directly on the ScrollViewer
Wrap your child element in a dummy container element. This way, the dummy element's rendertransform get overwrited and not yours:
At initialization, replace the ScrollViewer's RotateTransform with the element's:
public MainPage()
{
this.InitializeComponent();
this.ScrollViewer.RenderTransform = this.Image.RenderTransform;
}
I would personnaly go with the first or second solution. I fear there could be unexpected side effects with the third solution.

Categories