Windows 8 Image UniformFill centered - xaml

I have a little problem, I have a group item which I want to give a background image which it should scale keeping it's correct dimensions but by default it shows the image from the top left, I want the image to be centered.
Here is an illustration to further explain my problem. (the gray part is what is clipped)
And I have this XAML
<Image Source="/url/to/image.jpg" Stretch="UniformToFill"/>

I managed to solve my problem, I made the image larger than the container it was placed in and vertical aligned it in the center.
<Grid HorizontalAlignment="Left" Width="250" Height="125">
<Image Source="/url/to/image.jpg" Stretch="UniformToFill" Height="250" Margin="0" VerticalAlignment="Center"/>
</Grid>
The overflow of the image was invisible :)

In case the size of the source image is unknown in advance I had to use different trick:
<Border Width="250" Height="250">
<Border.Background>
<ImageBrush ImageSource="/url/to/image.jpg"
Stretch="UniformToFill"/>
</Border.Background>
</Border>

I wrote a behavior for Silverlight/Windows Phone that treats a similar situation. The picture I have to show may be larger or higher and I must display it in a square.
The behavior calculates the width/height ratio of both the container and the picture. Depending on which is the largest/highest, I resize the Image control to have this clipping effect with the parent control.
Here is a sample XAML to use with my behavior.
<Border Height="150" Width="150">
<Image Source="{Binding BitmapImage}" Stretch="UniformToFill"
HorizontalAlignment="Center" VerticalAlignment="Center">
<i:Interaction.Behaviors>
<b:FillParentBehavior />
</i:Interaction.Behaviors>
</Image>
</Border>
Here is an excerpt from the C# code. The full code can be found here: FillParentBehavior.cs
double width = this.AssociatedObject.Width;
double height = this.AssociatedObject.Height;
var parentSize = new Size(this.parent.ActualWidth, this.parent.ActualHeight);
var parentRatio = parentSize.Width / parentSize.Height;
// determine optimal size
if (this.AssociatedObject is Image)
{
var image = (Image)this.AssociatedObject;
if (image.Source is BitmapImage)
{
var bitmap = (BitmapImage)image.Source;
var imageSize = new Size(bitmap.PixelWidth, bitmap.PixelHeight);
var imageRatio = imageSize.Width / imageSize.Height;
if (parentRatio <= imageRatio)
{
// picture has a greater width than necessary
// use its height
width = double.NaN;
height = parentSize.Height;
}
else
{
// picture has a greater height than necessary
// use its width
width = parentSize.Width;
height = double.NaN;
}
}
}
this.AssociatedObject.Width = width;
this.AssociatedObject.Height = height;

Related

ScaleTransform doesn't work correctly

My idea is to create custom Tile control which is used as Live Tile (rendering it to a bitmap image and use as tile background) and within my app in a LongListSelector. By using the same control for both scenarios it ensures that it will look identical.
Here a short sample of my custom Tile control:
public class Tile : ContentControl
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Content = GetTileLayout();
}
private Grid GetTileLayout()
{
double width = 336;
double height = 336;
StackPanel panel = new StackPanel();
TextBlock contentTextBlock = new TextBlock();
// ...
Grid layoutRoot = new Grid();
layoutRoot.Background = new SolidColorBrush(Colors.Red);
layoutRoot.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
layoutRoot.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
layoutRoot.Width = width;
layoutRoot.Height = height;
layoutRoot.Children.Add(panel);
layoutRoot.Measure(new Size(width, height));
layoutRoot.Arrange(new Rect(0, 0, width, height));
layoutRoot.UpdateLayout();
return layoutRoot;
}
}
If I want to use it in the LongListSelector then it needs to be scaled down from 336x336 to 200x200 pixels. I tried it with a simple ScaleTransform but the result is not the same as I expected.
<phone:LongListSelector x:Name="ItemList" ItemsSource="{Binding Items}" LayoutMode="Grid" GridCellSize="222,222">
<phone:LongListSelector.ItemTemplate>
<StackPanel Margin="12,12,0,0" Background="Blue">
<DataTemplate>
<common:Tile VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<common:Tile.RenderTransform>
<ScaleTransform ScaleX="0.5" ScaleY="0.5" />
</common:Tile.RenderTransform>
</common:Tile>
</DataTemplate>
</StackPanel>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
This is the result:
The red rectangle is my custom Tile control with the ScaleTransform. After the transformation it should be a square again, scaled down from 336x336 to 168x168 (just an example). In the image above the height is correct but the width is too small.
It's strange because if I change the size of my custom Tile control to 200x200 pixels and use the same ScaleTransform with factor 0.5 it will be scaled down correctly.
Your GridCellSize is killing the transform. Make it bigger, try (400, 400). Also get rid of that StackPanel.
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<Border BorderBrush="HotPink" BorderThickness="1,1,1,1" Tap="Border_Tap" HorizontalAlignment="Left" VerticalAlignment="Top">
<Custom:Tile RenderTransformOrigin="0,0" HorizontalAlignment="Left" VerticalAlignment="Top">
<Custom:Tile.RenderTransform>
<CompositeTransform ScaleX="0.5" ScaleY="0.5"/>
</Custom:Tile.RenderTransform>
</Custom:Tile>
</Border>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>

Windows Phone XAML: applying transformation matrix after pinch gesture

I move and scale a rectangle within a canvas element with touch gestures. The code is based on this reference: http://msdn.microsoft.com/en-us/magazine/gg650664.aspx
The XAML code is as follows:
<Canvas Name="canvas" >
<Rectangle x:Name="rectangle" Fill="Green" Height="300" Canvas.Left="0" Stroke="Red" Canvas.Top="0" Width="100" StrokeThickness="3" >
<Rectangle.RenderTransform>
<TransformGroup>
<MatrixTransform x:Name="previousTransform" />
<TransformGroup x:Name="currentTransform">
<ScaleTransform x:Name="scaleTransform" />
<TranslateTransform x:Name="translateTransform" />
</TransformGroup>
</TransformGroup>
</Rectangle.RenderTransform>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener DragStarted="OnGestureListenerDragStarted"
DragDelta="OnGestureListenerDragDelta"
DragCompleted="OnGestureListenerDragCompleted"
PinchStarted="OnGestureListenerPinchStarted"
PinchDelta="OnGestureListenerPinchDelta"
PinchCompleted="OnGestureListenerPinchCompleted" />
</toolkit:GestureService.GestureListener>
</Rectangle>
</Canvas>
Dragging:
void OnGestureListenerDragDelta(object sender, DragDeltaGestureEventArgs args)
{
translateTransform.X += args.HorizontalChange;
translateTransform.Y += args.VerticalChange;
}
During pinching the scale transform is updated:
void OnGestureListenerPinchDelta(object sender, PinchGestureEventArgs args)
{
scaleTransform.ScaleX = args.DistanceRatio;
scaleTransform.ScaleY = args.DistanceRatio;
}
When pinching completes I want to get the "real" size and position of the rectangle within the parent canvas. Therefore I tried to apply the transform to the rectangle boundaries:
void OnGestureListenerPinchCompleted(object sender, PinchGestureEventArgs args)
{
Rect r = currentTransform.TransformBounds(new Rect(Canvas.GetLeft(rectangle), Canvas.GetTop(rectangle), rectangle.Width, rectangle.Height));
rectangle.Width = r.Width;
rectangle.Height = r.Height;
Canvas.SetLeft(rectangle, r.X);
Canvas.SetTop(rectangle, r.Y);
// Reset transforms
previousTransform.Matrix = new Matrix();
rectangle.RenderTransformOrigin = new Point(0, 0);
scaleTransform.ScaleX = scaleTransform.ScaleY = 1;
scaleTransform.CenterX = scaleTransform.CenterY = 0;
translateTransform.X = translateTransform.Y = 0;
}
This works fine for translation, but scaling is wrong (the rectangle is scaled but the factor is too big when increasing the scale, and too little when decreasing the scale). How can I get the correct final size of the scaled rectangle?
Regards
I fixed the problem by ommitting the transformation matrix classes and using the scaling factor and translation values manually.
Nevertheless I would be interested in a solution for using the XAML code above.

How to set a background to a textBlock that changes its content

Background
I'm trying to put a textBlock control at the bottom of the screen (with a small margin below it), and I also wish to set a background for it, so that no matter what is shown behind the textBlock, it will be easy to read.
On Android, you could simply set the background to it, and tell it to have the width and height to be WRAP_CONTENT, so that it will take only the space it needs, but I can't find a similar thing on WP8.
Current status
This is the xaml I've created:
...
<Grid >
<Image x:Name="fullScreenImage" Stretch="Fill"
Visibility="Collapsed" />
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom"
Margin="0,0,0,200" FontSize="40" x:Name="pictureLabel" TextWrapping="Wrap"
Foreground="#ff000000" />
</Grid>
The problem
Since the textBlock doesn't have a background property, I had to use something that wraps it. However, since its content changes dynamically, I can't simply set a size for it.
The question
For now, I would like to simply set its background color.
I would also very appreciate if it would be possible to use a rounded corners rectangle for the background, or a 9-patch image.
How can I achieve setting a background for the textBlock?
The solution is very simple. Just set the HirizontalAlignment to Left. Hope this will work in your case.
<StackPanel HorizontalAlignment="Left">
<Border Background="#66FFFFFF">
<TextBlock/>
</Border>
</StackPanel>
According to this question (which is for Silverlight - but is xaml nonetheless), there is no way to explicitly set a background color for a TextBlock. Your best bet is to wrap your TextBlock in a Grid or Border.
If a Grid does not work, this article suggests a Border will do the trick:
A simple border will do, and by not setting its width and height
properties, it will shrink/grow based on the size of the TextBlock.
I've come up with the next solution , which works quite fine , but what i really would like to also have is a way to set a min font and max font size , so that if there is a single word , the font might need to be of some size, and if the text is too long , the font will be of a smaller size , all in a dynamic way.
code:
label.Text = label;
label.Measure(new Size(RenderSize.Width, RenderSize.Height));
border.Width = label.DesiredSize.Width + border.Padding.Left + border.Padding.Right + border.BorderThickness.Left + border.BorderThickness.Right;
border.Height = label.DesiredSize.Height + border.Padding.Top + border.Padding.Bottom + border.BorderThickness.Bottom + border.BorderThickness.Top;
and the xaml:
<Border BorderBrush="#ff000000" BorderThickness="2" CornerRadius="8" Visibility="Collapsed" Padding="5" Background="#bfff0000" Margin="10,0,10,200" VerticalAlignment="Bottom" x:Name="border">
<StackPanel>
<TextBlock FontSize="40" x:Name="pictureLabel" TextWrapping="Wrap" Foreground="#ff000000" />
</StackPanel>
</Border>

How to read a size of area "Grid"? or how to get data from xaml into C++/CX code?

I am using dynamic marking of area "Grid"
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<!--This Grid-->
<Grid Grid.Column="0" Name="gridImage" SizeChanged="gridImage_SizeChanged">
<Image Name="image" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
In my code I am drawing image pixel-by-pixel depending on size of "Grid" which contains this image. How can I read the size of "Grid" area in code?
Properties gridImage->Width and gridImage->Height lets me set values but not get them.
gridImage->Width = 100;//OK
gridImage->Height = 100;//OK
int width = gridImage->Width;//return -2 147 483 648
int height = gridImage->Height;//return -2 147 483 648
If I use SizeChanged event for Grid area then I can read size by using this code
void MainPage::gridImage_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e)
{
Size size = e->NewSize;
int width = size.Width;//OK
int height= size.Height;//OK
}
But this event becomes available only after changing of image object, it means that that becomes available after then I need it.
Use the ActualWidth and ActualHeight properties to get the actual width and height in pixels, respectively. Unlike the Height, Width, MinHeight, MinWidth, MaxHeight and MaxWidth, they actually contain the height and width.

Augmented reality with kinect

I am doing one project on kinect with windows SDK. I am trying to display the image on skeleton shoulder with the following code.
**
Xaml code
<Canvas Margin="250,0,0,0" Visibility="{Binding Path=Showcamera, Converter={StaticResource BVC}}" Background="Green" HorizontalAlignment="Stretch" VerticalAlignment="Top" Grid.Column="1" Grid.Row="1">
<Image MaxHeight="600" MaxWidth="950" x:Name="PART_KinectVideo" Visibility="{Binding Path=Showcamera, Converter={StaticResource BVC}}" />
<Canvas>
<Image RenderOptions.BitmapScalingMode="HighQuality" Visibility="{Binding Path=Showcamera, Converter={StaticResource BVC}}"
RenderOptions.EdgeMode="Aliased" Canvas.Top="60" Canvas.Left="350" Stretch="Uniform" x:Name="Jewel"/>
</Canvas>
Code Behind
var j1p = nui.MapSkeletonPointToDepth(closestSkeleton.Joints[JointType.ShoulderLeft].Position,DepthImageFormat.Resolution640x480Fps30);
Canvas.SetLeft(Jewel, j1p.X+90);
Canvas.SetTop(Jewel, j1p.Y-280);
by getting the j1p values i am trying to set the left and top position of the canvas for the Jewel image object.
Problem with the code is :
This is not correctly placing the image. When the user moves in the screen the image also moves out of body with left and right direction as user moves.
I need the image should be set to the position even when the user moves right or left.
How should I alter the code or is there any sample available to set the image in particular position of the kinect joints?
Maybe try this:
var left = (double)image.GetValue(Canvas.LeftProperty)
left += 90;
image.SetValue(Canvas.LeftProperty, left);
I wrote an app using that code and everything worked fine to me:)