How to Keep Button Upright When Device Position Changes - xaml

I'd like to be able to allow a button that I have in my application to always remain looking like it is in the upright position, even when the device is rotated clockwise or counterclockwise. The standard app bar kind of does this with adjusting the application bar icons according to whether the device is in portrait or landscape mode, so I'd like to do something similar with a button on my page. How might I do something like this? Any recommendations into the methods? I'd like to either stick with something like what the app bar already does, or always rotate the button so it remains upright as the device rotates.
<Button x:Name="CameraButton" Grid.Row="1" Grid.Column="0" Margin="-48,0,0,-12"
Click="CameraButton_Click">
<Button.Content>
<Image Source="/Assets/Camera_Button1.png"/>
</Button.Content>
</Button>

If you do not care the rotate animation auto played by system, you can easily achieve by providing 3 different icons(Portrait, LandscapeLeft, LandscapeRight).
In Xaml, you first add your ApplicationBarIconButton into the page Resource, and change its IconUri later when OrientationChanged is fired. Hope it helps.
The project code can be downloaded here:
http://hdtp.synology.me/ApplicationBarIconDirection.zip
xaml code:
<phone:PhoneApplicationPage
x:Class="ApplicationBarIconDirection.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
OrientationChanged="PhoneApplicationPage_OrientationChanged">
<phone:PhoneApplicationPage.Resources>
<shell:ApplicationBarIconButton x:Key="icon_arrow" IconUri="/Assets/up.png" Text="FixedUp"/>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
</Grid>
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
</phone:PhoneApplicationPage>
xaml.cs code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Shell;
namespace ApplicationBarIconDirection
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.ApplicationBar.Buttons.Add(this.Resources["icon_arrow"] as ApplicationBarIconButton);
}
private void PhoneApplicationPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
if (e.Orientation == PageOrientation.LandscapeLeft)
{
(this.Resources["icon_arrow"] as ApplicationBarIconButton).IconUri = new Uri("/Assets/left.png", UriKind.Relative);
}
else if (e.Orientation == PageOrientation.LandscapeRight)
{
(this.Resources["icon_arrow"] as ApplicationBarIconButton).IconUri = new Uri("/Assets/right.png", UriKind.Relative);
}
else
{
(this.Resources["icon_arrow"] as ApplicationBarIconButton).IconUri = new Uri("/Assets/up.png", UriKind.Relative);
}
}
}
}

Application Bar has a property named SupportedOrientation which makes it change its orientation every time the orientation of the phone changes (see this ). But, If we look at button then we see that there is neither such property named SupportedOrientation nor any other property functioning analogous to that of SupportedOrientation for applicationbar. I would hence recommend you to make your own logic according to the changes in the phones orientation.
sample logic to change orientations-
logic to rotate a button
CompositeTransform ct=new CompositeTransform (){Rotation=90};
button.Rendertransform=ct;
Apply this on orientation changes like this
private void PhoneApplicationPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
if (e.Orientation == PageOrientation.LandscapeLeft)
{
//apply rotation with some angle say 90
}
else if (e.Orientation == PageOrientation.LandscapeRight)
{
//apply rotation 180
}
else if(e.Orientation == PageOrientation.PortraitUp)
{
//apply rotation 270
}
else if(e.Orientation == PageOrientation.PortraitDown)
{
//apply rotation 360
}
}
And for smoothness like that in applicationbar buttons you would have to make your hands dirty with the storyboards and animations using expression blend

Related

RegisterPropertyChangedCallback works only the first time

This is a follow up question related to a control that needs to intercept when common control properties changes (Inherit UserControl and hooking up to basic property events).
When any base property changes (foregound, font style, size...), I need to invalidate the canvas and redraw the content.
I've ended up with calling this method inside the UserControl constructor:
RegisterPropertyChangedCallback(DependencyProperty dp, DependencyPropertyChangedCallback callback);
For example:
RegisterPropertyChangedCallback(ForegroundProperty, OnPropertyChanged);
RegisterPropertyChangedCallback(FontFamilyProperty, OnPropertyChanged);
RegisterPropertyChangedCallback(FontSizeProperty, OnPropertyChanged);
Inside OnPropertyChanged I proceed to rewire some property and invalidate the canvas in order to update the picture.
The control is inside a DataTemplate referenced by a Pivot (PivotHeaderTemplate). The default Foreground is the unselected status (semi-transparent SystemControlForegroundBaseMediumBrush) and should get the default "white" when selected (SystemControlHighlightAltBaseHighBrush).
The control Foreground property is applied by a template, and according to the live tree it's updating, but OnPropertyChanges is called only the first time.
For example, the Pivot has 2 views: the first one starts selected and is white, and the second is unselected and is "grayish".
If I change the selection, the view changes, the Foreground property changes accordingly on both controls but OnPropertyChanges is not called, and my canvas doesn't change (because the invalidation logic is inside that method).
[UPDATE: as a workaround, I used the event LayoutUpdated to check what changed.]
For a test project, create a new UWP XAML project a new MyCanvas UserControl.
MyCanvas.xaml:
<UserControl x:Class="InheritControlProperty.Controls.MyCanvas"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:InheritControlProperty.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Canvas x:Name="content" Width="50" Height="50"/>
</UserControl>
MyCanvas.xaml.cs
using System;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
namespace InheritControlProperty.Controls
{
public sealed partial class MyCanvas : UserControl
{
private Color Value
{
get { return (Foreground as SolidColorBrush)?.Color ?? Colors.Black; }
}
public MyCanvas()
{
InitializeComponent();
RegisterPropertyChangedCallback(ForegroundProperty, OnPropertyChanged);
}
private void OnPropertyChanged(DependencyObject sender, DependencyProperty dp)
{
content.Background = new SolidColorBrush(Value);
//Logic for more complex canvas management here
}
}
}
MainPage.xaml
<Page x:Class="InheritControlProperty.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:InheritControlProperty"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cntr="using:InheritControlProperty.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="NameTemplate">
<Grid BorderBrush="Orange" BorderThickness="2">
<cntr:MyCanvas Width="100" Height="50"/>
<!-- Try using Foreground="Red" -->
</Grid>
</DataTemplate>
</Page.Resources>
<Pivot Margin="20" HeaderTemplate="{StaticResource NameTemplate}">
<PivotItem>Item 1</PivotItem>
<PivotItem>Item 2</PivotItem>
</Pivot>
</Page>

Change input scope for textbox programmatically multiple times on Windows Phone 8.1 app

I am trying to change the input scope for a textbox in Windows Phone 8.1 app programmatically at runtime, but the change only works the first time.
I have this xaml page:
<Page
x:Class="InputScopeTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:InputScopeTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Margin="53,117,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="302"/>
<Button x:Name="buttonAlpha" Content="Alpha" HorizontalAlignment="Left" Margin="53,229,0,0" VerticalAlignment="Top" Click="buttonAlpha_Click"/>
<Button x:Name="buttonNumeric" Content="Numeric" HorizontalAlignment="Left" Margin="246,229,0,0" VerticalAlignment="Top" Click="buttonNumeric_Click"/>
</Grid>
</Page>
And this .cs page:
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
namespace InputScopeTest
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void buttonAlpha_Click(object sender, RoutedEventArgs e)
{
InputScope scope = new InputScope();
InputScopeName name = new InputScopeName();
name.NameValue = InputScopeNameValue.AlphanumericFullWidth;
scope.Names.Add(name);
textBox.InputScope = scope;
}
private void buttonNumeric_Click(object sender, RoutedEventArgs e)
{
InputScope scope = new InputScope();
InputScopeName name = new InputScopeName();
name.NameValue = InputScopeNameValue.Number;
scope.Names.Add(name);
textBox.InputScope = scope;
}
}
}
On both the emulator and a windows phone 8.1 device the input scope for the textbox changes correctly only the first time you tap one of the buttons. For example if I tap the "Numeric" button first, the input scope changes to a numeric keyboard correctly. But if I then tap the "Alpha" button, the input scope does not change to an alphanumeric keyboard as it should.
The above code is taken from MSDN and it looks that it only works if you change the scope the first time. The second time the change is ignored.
Am I doing something wrong? Is there another way to set the input scope for a textbox programmatically multiple times?

Image rotation as animation

I am making a Windows 8 application in visual studio 2012 c#.
I am having an image '1.png' and I want to rotate it at any angle as an animation along its center point.
But i want to do it with the help of c# code rather than XAML code.
Thank You in Advance.
In your XAML, have the following image:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Image Source="/Assets/Logo.png" Width="300" RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<RotateTransform x:Name="rotateTransform"/>
</Image.RenderTransform>
</Image>
</Grid>
Then, in code, write the following when you want to animate (you create the Storyboard programmatically, then add to it a relevant Timeline. Note that you can also create the RotateTransform in code if you want.
async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(500);
Storyboard board = new Storyboard();
var timeline = new DoubleAnimationUsingKeyFrames();
Storyboard.SetTarget(timeline, rotateTransform);
Storyboard.SetTargetProperty(timeline, "Angle");
var frame = new EasingDoubleKeyFrame() { KeyTime = TimeSpan.FromSeconds(1), Value = 360, EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut } };
timeline.KeyFrames.Add(frame);
board.Children.Add(timeline);
board.Begin();
}
This will rotate the object 360 degrees.
BTW: I am writing a set of posts that show an even better way of animating. It's not done yet, but it will give you a general idea on how to get a framework for certain types of animations..
First part of the series
Thanks Shahar! I took your example and made a custom control. It's actually an infinite spinning of one ring image.
Spinner.xaml:
<UserControl x:Class="MyControls.Spinner"
...
<Grid >
<Image Source="/Assets/Images/spinner.png" Width="194" RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<RotateTransform x:Name="rotateTransform"/>
</Image.RenderTransform>
</Image>
</Grid>
</UserControl>
Spinner.cs:
namespace MyControls
{
public partial class Spinner: UserControl
{
public Spinner()
{
InitializeComponent();
this.Loaded += Spinner_Loaded;
}
private void PlayRotation()
{
Storyboard board = new Storyboard();
var timeline = new DoubleAnimationUsingKeyFrames();
Storyboard.SetTarget(timeline, rotateTransform);
Storyboard.SetTargetProperty(timeline, new PropertyPath("(Angle)"));
var frame = new EasingDoubleKeyFrame() { KeyTime = TimeSpan.FromSeconds(5), Value = 360, EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut } };
timeline.KeyFrames.Add(frame);
board.Children.Add(timeline);
board.RepeatBehavior = RepeatBehavior.Forever;
board.Begin();
}
private async void Spinner_Loaded(object sender, RoutedEventArgs e)
{
PlayRotation();
}
}
}
Then when you want to use Spinner in another xaml, it's very simple:
Just add a line for it inside any Grid etc:
<include:Spinner/>
(of course you need to define include as something like:
xmlns:include="MyControls"
on top of your xaml)

Auto-complete box under a text box in Windows 8 / Metro UI

I want to implement auto-complete on a textbox in a Windows 8 UI / Metro UI app using C#/XAML.
At the moment, when the soft / touch keyboard shows, it obscures the auto-complete box. However, on the text box focus, Windows 8 automatically scrolls the entire view up and ensures the text box is in focus.
In reality, all I want is the view to scroll up a little more (in fact, by the height of the auto-complete box).
I realise I can intercept the Showing event of InputPane.GetForCurrentView()
I can set InputPaneVisibilityEventArgs.EnsuredFocusedElementInView to true inside the Showing event fine (so Windows won't try to do anything).... however, how can I invoke the same scrolling functionality that Windows 8 would do, but ask it to scroll a little more!?
Here's the code for the main page:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,200,0,0">
<TextBlock HorizontalAlignment="Center" FontSize="60">App 1</TextBlock>
<TextBlock HorizontalAlignment="Center">Enter text below</TextBlock>
<TextBox HorizontalAlignment="Center" Margin="-10,0,10,0" Width="400" Height="30"/>
<ListBox HorizontalAlignment="Center" Width="400">
<ListBoxItem>Auto complete item 1</ListBoxItem>
<ListBoxItem>Auto complete item 2</ListBoxItem>
<ListBoxItem>Auto complete item 3</ListBoxItem>
<ListBoxItem>Auto complete item 4</ListBoxItem>
<ListBoxItem>Auto complete item 5</ListBoxItem>
</ListBox>
</StackPanel>
</Grid>
If you start up the simulator with the lowest resolution, use the hand to "touch" the textbox, this will bring up the soft keyboard. In the real app, the auto complete list will appear with items as the user enters text.
So in a nutshell, how can I move the screen up a bit more so the user can see the entire autocomplete list?
Bear in mind, in the real app, it'll be worse, as the user may not even notice the autocomplete list appearing "underneath" the keyboard.
I really would appreciate some advice, many thanks!
I have created an AutoCompleteBox for Windows Store apps, the nuget package is available at https://nuget.org/packages/AutoCompleteBoxWinRT
Ok, here is how I would tackle this since I cannot seem to find any way to control the scrolling of the app based on the appearance of the keyboard. I would create a user control that would form the basis for the auto-complete textbox.
<UserControl
x:Class="App6.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App6"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBox x:Name="textBox" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" GotFocus="textBox_GotFocus" LostFocus="textBox_LostFocus" />
<ListBox x:Name="listBox" Height="150" Margin="0,-150,0,0" VerticalAlignment="Top" Visibility="Collapsed"/>
</Grid>
This is an incredibly basic implementation, so you will have to tweak to meet your needs.
Then, I would add the following code-behind to the user control
public sealed partial class MyUserControl1 : UserControl
{
// Rect occludedRect;
bool hasFocus = false;
public MyUserControl1()
{
this.InitializeComponent();
InputPane.GetForCurrentView().Showing += MyUserControl1_Showing;
}
void MyUserControl1_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
if (hasFocus)
{
var occludedRect = args.OccludedRect;
var element = textBox.TransformToVisual(null);
var point = element.TransformPoint(new Point(0, 0));
if (occludedRect.Top < point.Y + textBox.ActualHeight + listBox.ActualHeight)
{
listBox.Margin = new Thickness(0, -listBox.ActualHeight, 0, 0); // Draw above
}
else
{
listBox.Margin = new Thickness(0, textBox.ActualHeight, 0, 0); // draw below
}
}
}
private void textBox_GotFocus(object sender, RoutedEventArgs e)
{
listBox.Visibility = Windows.UI.Xaml.Visibility.Visible;
hasFocus = true;
}
private void textBox_LostFocus(object sender, RoutedEventArgs e)
{
listBox.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
hasFocus = false;
}
}
Next steps would be to expose properties to pass data to be bound to the ListBox. Hard core would be ListBoxItem templating and more, depending on how reusable you wanted it to be.

Silverlight 4, with Bing Maps, Ellipse size

I have problem displaying big Ellipses in a MapLayer. The Ellipse is cut off.
In the XAML, I just add a Map with a Layer and an ellipse. In the code behind I locate the ellipse in the map. The problem appears when I pan the map to the north.
<UserControl x:Class="PruebaEllipse.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<m:Map x:Name="Mapa" CredentialsProvider="AlT1xaWmg1CctI7..." Mode="Road" Grid.Column="0" Grid.Row="1" ZoomLevel="10" Center="-33,-54" >
<m:MapLayer x:Name="NewPolygonLayer">
</m:MapLayer>
<m:MapLayer x:Name="Layer1" Loaded="Layer1_Loaded" >
<m:MapLayer.Children>
<Ellipse Height="1500" HorizontalAlignment="Left" Name="ellipse1" Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Width="900" Fill="#FF895D5D" />
</m:MapLayer.Children>
</m:MapLayer>
</m:Map>
</Grid>
</UserControl>
This is the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Maps.MapControl;
namespace PruebaEllipse
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void Layer1_Loaded(object sender, RoutedEventArgs e)
{
Location trkLoc2 = new Location(-32.5, -54.0);
MapLayer.SetPosition(ellipse1, trkLoc2);
}
}
}
This is a limitation of Silverlight. If you create any control that has a size that is larger than your view port window it will be clipped. If you want to draw a circle on Bing Maps you can use the MapPolyon class and calculate a bunch of points that make up the circle. This will give you a circle that scales as you zoom the map and that doesn't get clipped by the viewport window. Here is a blog post on how to do this: http://silverlightfoundry.blogspot.co.uk/2009/06/bing-live-maps-silverlight-control-part.html