Rendering a text block with WriteableBitmap and RotateTransform on WP7 - windows-phone-7

it seems that there is a really annoying bug in WriteableBitmap for Silverlight for Windows Phone. I have the following code and xaml:
public partial class MainPage : PhoneApplicationPage
{
CompositeTransform rotate = new CompositeTransform();
public MainPage()
{
InitializeComponent();
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
this.rotate.Rotation += 15;
WriteableBitmap bmp = new WriteableBitmap(this.button, rotate);
this.image.Source = bmp;
Dispatcher.BeginInvoke(() => Debug.WriteLine("{0}, {1}", bmp.PixelWidth, bmp.PixelHeight));
}
}
Here is the xaml:
<Grid>
<Button VerticalAlignment="Top"
HorizontalAlignment="Center"
Content="This is a textblock inside a layout"
x:Name="button"/>
<Image VerticalAlignment="Center"
HorizontalAlignment="Center"
x:Name="image"/>
<Button VerticalAlignment="Bottom"
Content="Rotate"
Click="Button_Click"/>
</Grid>
When you click the bottom button, the top button is rendered with the writeable bitmap using the composite transform. After every render, the resulting image of the top button is larger than the previous one. Also, the PixelWith and PixelHeight property values of the writeable bitmap differ wildly from the RenderSize of the Image object. Does anyone have any idea what is going on?

I don't fully understand what's going on, but I believe the controls size are adjusted because of the horizontal and vertical alignment, and somehow it causes the issue you mentioned.
You can bypass it by setting the Stretch property of the Image control to None. This way, the displayed picture will always keep its original size, no matter the size of the image control.
<Image VerticalAlignment="Center"
HorizontalAlignment="Center"
Stretch="None"
x:Name="image"/>

Related

Xamarin forms breadcrumbs with horizontal scroll layout

I have a xamarin.forms app in which I am trying achieve a specific UI.Please find the attched image.
.
As you can see It is a list view and have a breadcrumbs below it. What I am trying to achieve is when user click any of the other items such as "stores" or "users" in breadcrumbs, then the upper layout horizontally slide and show another list view.Where I am stuck is I want to fix the breadcrumbs at the bottom and the change only needs the upper layout i.e.; the list view layout. How can I achieve this. Any ideas will be much helpfull.
What I am thinking is putting four listview inside horizontal scroll view.But is it the better approach?
This could be achieved by simple Translate animation.
A simple implementation of the idea of using translation. Change as per need.
XAML layout:
<StackLayout>
<Grid x:Name="rotatingView">
<ListView
...../>
<ListView
TranslationX="{Binding Width, Source={x:Reference rotatingView}}"
...../>
<ListView
TranslationX="{Binding Width, Source={x:Reference rotatingView}}"
...../>
<ListView
TranslationX="{Binding Width, Source={x:Reference rotatingView}}"
...../>
</Grid>
<Button
Text="0"
Clicked="Button_Clicked"/>
<Button
Text="1"
Clicked="Button_Clicked"/>
<Button
Text="2"
Clicked="Button_Clicked"/>
<Button
Text="3"
Clicked="Button_Clicked"/>
</StackLayout>
Xaml.cs clicked:
int previousSelectedIndex = 0;
private async void Button_Clicked(System.Object sender, System.EventArgs e)
{
Button selectedtab = (sender as Button);
int selectedViewIndex = int.Parse(selectedtab.Text);
VisualElement previousView = rotatingView.Children[previousSelectedIndex];
VisualElement selectedView = rotatingView.Children[selectedViewIndex];
bool isMovingForward = true;
if (previousSelectedIndex < selectedViewIndex)
{
isMovingForward = true;
}
else if(previousSelectedIndex > selectedViewIndex)
{
isMovingForward = false;
}
if (selectedViewIndex != previousSelectedIndex)
{
selectedView.TranslationX = rotatingView.Width * (isMovingForward ? 1 : -1);
await Task.WhenAll(
selectedView.TranslateTo(0, 0),
previousView.TranslateTo(rotatingView.Width * (isMovingForward ? -1 : 1), 0));
}
this.previousSelectedIndex = selectedViewIndex;
}
Here I have used the text of buttons to select index of the view. Hope this could help.
if you are looking for a breadcrumb navigation control.
I have created a control that will generate one automatically, and it's highly customisable.
https://github.com/IeuanWalker/Xamarin.Forms.Breadcrumb

How to make ActivityIndicator overlay full screen?

I have a StackLayout and a number of elements inside (buttons, texts etc).
I want the ActivityIndicator to overlay the entire screen and make it not able to do anything to those elements.
I have put ActivityIndicator inside the StackLayout but wrapped it with AbsoluteLayout thinking that AbsoluteLayout can easitly overlap everything:
<StackLayout>
<AbsoluteLayout>
<ActivityIndicator ... />
</AbsoluteLayout>
<...other elements...>
</StackLayout>
Instead activity indicator is displayed at the top of the StackLayout and other elements are available for affecting. I'm new in Xamarin and layouts, what am I doing wrong? All samples in the Internet have single ActivityIndicator per page...
It is better said that an AbsoluteLayout's children can easily overlap each other. Just as a StackLayout lets you stack controls inside , vertically or horizontally, an AbsoluteLayout lets you position controls inside using absolute or proportional values, thus if two controls have the same absolute positioning set, they will overlap 100%.
Therefore, you want to wrap your StackLayout and another StackLayout that has your ActivityIndicator inside an AbsoluteLayout using proportional sizing, e.g:
<AbsoluteLayout>
<StackLayout
x:Name="mainLayout"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All" >
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="Do Something"
Clicked="DoSomethingBtn_Clicked" />
</StackLayout>
<StackLayout
x:Name="aiLayout"
IsVisible="False"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
BackgroundColor="Gray" Opacity="0.5">
<ActivityIndicator
x:Name="ai"
IsRunning="False"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Color="Black"/>
</StackLayout>
</AbsoluteLayout>
The above sets the two StackLayouts to both take up the full size of the parent container of the AbsoluteLayout, which is presumably a Page. The StackLayout that has the indicator is initially hidden. IN the page code behind for the above example, I show the second StackLayout and start the activity indicator and show it for 2 seconds, and then hide it again:
private async void DoSomethingBtn_Clicked(object sender, EventArgs e)
{
ai.IsRunning = true;
aiLayout.IsVisible = true;
await Task.Delay(2000);
aiLayout.IsVisible = false;
ai.IsRunning = false;
}
Here is what it looks like:
And since the second StackLayout completely covers the first, none of the controls in the first StackLayout are clickable.
Might be worth going over the docs for the AbsoluteLayout to understand the AbsoluteLayout.LayoutBounds and AbsoluteLayout.LayoutFlags:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/absolute-layout
If you want to "overlap", you need to be outside of the StackLayout. A Grid is the most common control for this:
<Grid>
<StackLayout>
<...other elements...>
</StackLayout>
<ActivityIndicator ... />
</Grid>
Here's a hacked-up control for making things full-screen via the horribly-named RelativeLayout (tested in Android only)
[ContentProperty("ContentInner")]
public class FullScreenLayout : ContentView
{
public View ContentInner
{
get => ((RelativeLayout) Content).Children[0];
set
{
var display = DeviceDisplay.MainDisplayInfo;
var screenWidth = display.Width / display.Density;
var screenHeight = display.Height / display.Density;
var wrapper = new RelativeLayout();
wrapper.Children.Add(value, () => new Rectangle(0, 0, screenWidth, screenHeight));
Content = wrapper;
}
}
}
It can be used like this:
<controls:FullScreenLayout>
<!-- Anything you want fullscreen here -->
</controls:FullScreenLayout>
Unfortunately, if you use NavigationPage, this won't overlap the navigation bar. Every other solution currently on this page has the same issue. According to this question, it's not possible to solve this without using platform-specific customer renderers. Ugh.
If you don't mind the page being dimmed, you can use Rg.Plugins.Popup which implements the custom renderers needed.
I ended up solving my similar problem (dimming most of the screen) by implementing a custom renderer for the navigation page itself.

Auto Set WP Image controls height and width based on the databound Source

I have a WP application in which I want to display a list of images that are databound to an observablecollection that is populated from a web service. I want the list of images to show-up with the same height and width from where they are retrieved. So, how do I get the image control to adjust its height and width automatically to the databound source.
Using the following code, the image doesn't show-up.
<Grid x:Name="LayoutRoot">
<Image Source="{Binding AlbumPicture}" Stretch="Fill" Width="Auto" Height="Auto"/>
</Grid>
The following works, but the height and width are different for each image. so, they don't look good.
<Grid x:Name="LayoutRoot">
<Image Source="{Binding AlbumPicture}" Stretch="Fill" Width="200" Height="120"/>
</Grid>
I tried the following and they haven't worked.
set the Stretch="None"
Databind Image controls Height, Width properties to those of the
image using the following and the height and width are always 0.
BitmapImage bmp = new BitmapImage(new Uri(updatedAlbum.AlbumPicture, UriKind.RelativeOrAbsolute));
album.AlbumHeight = bmp.PixelHeight;
album.AlbumWidth = bmp.PixelWidth;
This code will work only after the image is loaded.
album.AlbumHeight = bmp.PixelHeight;
album.AlbumWidth = bmp.PixelWidth;
You need just wait for it, and then bind(or set directly) this values to your image control.
For example you can use method
<Image x:Name="img" ImageOpened="Img_OnImageOpened"/>
private void Img_OnImageOpened(object sender, RoutedEventArgs e)
{
//start from here to set width and height.
//you can do it directly, or by the Binding
}

Why can't I tap/click blank areas inside of a Border/ContentControl without setting the child's background to transparent?

I finally was able to create an "easy" transparent button control, based off a ContentControl. However, can someone explain why I couldn't click/tap any blank areas of the control until I set the background of the child element to transparent? I ran into this issue also when:
I tried to use Border
I set the ControlTemplate of a button rather than the ContentTemplate.
Here's my "button" class:
public class TransparentButton : ContentControl {
public TransparentButton() {
HorizontalContentAlignment = HorizontalAlignment.Stretch;
}
public override void OnApplyTemplate() {
var child = Content as Grid;
if (child != null) {
child.Background = new SolidColorBrush(Colors.Transparent);
}
base.OnApplyTemplate();
}
}
It's pretty specific to my cases when using (assuming a Grid child) but it works. The reason I use it is for lists (non-ListBox) with the TiltEffect enabled.
Context of the issue:
<ItemsControl x:Name="Items" toolkit:TiltEffect.IsTiltEnabled="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:TransparentButton
cal:Message.Attach="[Event Tap] = [Action Go($dataContext)]">
<Grid>
<StackPanel HorizontalAlignment="Left">
<TextBlock Text="{Binding Test}" />
</StackPanel>
<StackPanel HorizontalAlignment="Right">
<TextBlock Text="{Binding Test2}" />
</StackPanel>
</Grid>
</controls:TransparentButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you click between the StackPanels inside the item, no event gets fired and nothing happens. Only when the Grid's Background is Transparent does it "take up space".
I come from a web background so this is confusing; a containing element should be "hit testable" even when it's background isn't set.
An object with no background is usually called as hollow or non-hittable in XAML terms. So it is must to set a background to make the object respond to hits. To achieve hit test for an transparent object, you should set the background to transparent.
More information about hit testing
http://msdn.microsoft.com/en-us/library/ms752097.aspx

Is it possible to access an external class' member variable in page.xaml.cs?

I'm developing a Windows Phone 7 app, and I have two xaml pages. From the first one, I embed two app bar links to select an image from gallery or capture an image using the camera. I would like the image chosen on the first page to be displayed on a second page, with the app bar buttons showing a confirm yes or no. As of now, I have an image control on the first page (barcodeImage) that gets updated with the choice.
MainPage.xaml
<controls:PanoramaItem Header="welcome">
<ScrollViewer Name="sv1" VerticalScrollBarVisibility="Auto">
<StackPanel Height="1100">
<TextBlock TextWrapping="Wrap">Random text here.
</TextBlock>
<Grid x:Name="Grid2" Grid.Row="1" Margin="12,0,12,0">
<Image Height="150" Margin="28,30,168,0" Name="barcodeImage" Stretch="Fill" VerticalAlignment="Top" d:LayoutOverrides="VerticalAlignment" />
</Grid>
</StackPanel>
</ScrollViewer>
</controls:PanoramaItem>
MainPage.xaml.cs
void cameraCaptureTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(e.ChosenPhoto);
barcodeImage.Source = bmp;
}
}
Confirm.xaml
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Image Margin="64,36,57,100" x:Name="barcodeImageFinal" Stretch="Fill" />
</Grid>
I'd like barcodeImageFinal to display the final bitmap. How can I make this work? Thanks for looking :)
As I understand your question, you want to create a bitmap in a member of MainPage and then access it from Confirm. One approach would be to create a public static property of some class for your bitmap. For example, maybe create public static BitmapImage FinalBitmap in your App. Then you could set the value of the property in your cameraCaptureTask_Completed and then create a Loaded handler in your Confirm class that sets image source to the stored bitmap.
I think the answer to your question title is yes if you make the member static, although the other class isn't really "external". A normal class member won't be accessible because you don't have an instance of that class.

Resources