custom entry box with icon for xamarin form (android,ios) - xaml

Entry box should be rounded with an icon to the left or right in it. I'm using the code presented here to create this custom entry.

1. Remove the rectangular border of Entry
Used CustomRender to achieve this.
Forms
public class NoUnderlineEntry : Entry
{
public NoUnderlineEntry()
{
}
}
Android
Set Background to null
public class NoUnderLineEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
this.Control.Background = null;
}
}
iOS
Set BorderStyle to None
public class NoUnderlineEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
this.Control.BorderStyle = UIKit.UITextBorderStyle.None;
}
}
2. Placing Image next to Entry
Adding Image and Entry to the same Grid in two columns.
3. Adding Rounded border to the Entry and Image
Add them inside a Frame with CornerRadius.
XAML
<StackLayout>
<Frame
Padding="10, 5, 10, 5"
HasShadow="False"
BorderColor="Gray"
CornerRadius="30">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<local:NoUnderlineEntry/>
<Image Source="icon.png" Grid.Column="1" WidthRequest="50" Aspect="AspectFit"/>
</Grid>
</Frame>
</StackLayout>
UI result:

Please note: I won't present a copy-and-paste-able answer, but rather an outline on how to add the images. You'll have to integrate the code in your solution by yourself.
On iOS
There already is an answered question on how to achieve this with Swift on iOS, you can find it here.
Basically what to do is to set the right view (or left view respectively) on the UITextField from your custom renderer (in OnElementChanged).
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
var imageView = new UIImageView(new CGRect(0, 0, 20, 20));
var image = UIImage.FromFile("ic_location.png");
imageView.Image = image;
this.Control.RightView = imageView;
this.Control.RightViewMode = UITextFieldViewMode.Always;
}
This sets the view in the right of the UITextField to a UIImageView. If you wanted to show the icon before the text instead, you'd have to set LeftView and LeftViewMode instead. This is how it looks like. (I intentionally did not inline the image, because it rendered the answer less redable.)
Of course the file ic_location.png has to be in your platform projects resources.
You may need some fine tuning, but basically that's it.
On Android
The TextView.setCompoundDrawablesWithIntrinsicBounds
Sets the Drawables (if any) to appear to the left of, above, to the right of, and below the text. Use null if you do not want a Drawable there. The Drawables' bounds will be set to their intrinsic bounds. (source)
By loading the icon from the resource and setting it with SetCompoundDrawablesWithIntrinsicBounds (uppercase now, since we're now on C#) you can display the Entry with the icon:
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
this.Control.SetCompoundDrawablesWithIntrinsicBounds(null, null, this.GetIcon(), null);
this.Control.CompoundDrawablePadding = 25;
}
private Drawable GetIcon()
{
int resID = Resources.GetIdentifier("ic_location", "drawable", this.Context.PackageName);
var drawable = ContextCompat.GetDrawable(this.Context, resID);
var bitmap = ((BitmapDrawable)drawable).Bitmap;
return new BitmapDrawable(Resources, Bitmap.CreateScaledBitmap(bitmap, 20, 20, true));
}
This is how the Android version looks like.
For showing the icon on left left, pass the drawable to the first parameter instead of the third.

Related

UWP: Content dialog width stays the same

I have tried to set the width and also min height and min width but still the dialogue wont change to full screen. tried window.bounds too but teh dialog wont expand beyond a fixed width.
public sealed partial class ContentDialog1 : ContentDialog
{
public ContentDialog1()
{
this.InitializeComponent();
this.MinWidth = Window.Current.Bounds.Width;
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
}
}
<ContentDialog
x:Class="PowerUp.UWP.View.ContentDialog1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PowerUp.UWP.View"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="TITLE"
PrimaryButtonText="Button1"
SecondaryButtonText="Button2"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
SecondaryButtonClick="ContentDialog_SecondaryButtonClick"
MinWidth ="2000">
<Grid x:Name="cd1" >
</Grid>
This is what I want
This is how content dialog is shown in my application
It is actually very simple, did a bit research and found the simplest answer, you can keep doing what you were already doing in the first place and just set the FullSizeDesired property of your ContentDialog to true.
Popup
Or you can try it with popup.
var c = Window.Current.Bounds;
var okButton=new Button{Content="Ok"};
okButton.Click += okButtonClicked; // now where you have this okButtonClicked event you can execute any code you want including, closing the popup.
var g = new Grid
{
Width = c.Width,
Height = c.Height,
Background = new SolidColorBrush(Color.FromArgb(0x20, 0, 0, 0)),
Children =
{
new StackPanel
{
Width = 400,
Height = 200,
Background = new SolidColorBrush(Colors.White),
Children=
{
new TextBlock{Text="Title"},
new TextBlocl{Text="description"},
okButton
}
}
}
};
var p = new Popup
{
HorizontalOffset = 0,
VerticalOffset = 0,
Width = c.Width,
Height = c.Height,
Child = g
};
p.IsOpen = true; // open when ready
Notice that Child of popup is g which is a grid, you can put your content within this grid or you can use a StackPanel instead of this grid and then put your contents within that StackPanel, whatever you want to do here is your decision, putting elements in popup is exactly like putting elements in a ContentDialog.
Achieving the same with a simple Grid alongside your frame
<Grid>
<Grid Horizontallignment="Stretch" VerticalAlignment="Stretch" Visibility="Collapsed" x:Name="ContentGrid" canvas.ZIndex="5"><--this grid will act as content dialog, just toggle its visibility to the backend-->
<--put all your content here along with your ok and cancel buttons-->
</Grid>
<Frame/> <--this is the code where you have your frame-->
</Grid>
in above code the frame and your actually content grid will be parallel to each other, and whenever content grid is visible only it will be shown in the app because it has ZIndex greater than 0 and your frame will hide behind it, and whenever its visibility is collapsed it will not be shown and you will be able to see your frame normally.

UWP/c++-cx - Moving a button on the vertical axis by dragging it

I have a Grid which is as high as the application and has the width of 50. I have got a button in it on the left top with the width of 50 also. I want to move this button along the vertical left axis by dragging it with the mouse. But it should be stil able to be clicked normally. I tried to do this with the drag-and-drop sample by microsoft but the procedure I want to implement is not quite drag-and-drop. How can I implement this by using XAML and c++-cx as code behind in an universal windows app ?
My XAML-Code for the Grid/Button:
<Grid x:Name="Grid1" Width="50" >
<Button x:Name="btnMove" Content="Move me!" Background="PaleGreen" Click="btnMove_Click" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50" Height="50"></Button>
</Grid>
For your requirement, you could move the button on the vertical axis by using ManipulationDelta class. And you could achieve it with the following code.
For more please refer to Handle pointer input. Here is official code sample.
MainPage::MainPage()
{
InitializeComponent();
InitManipulationTransforms();
btnMove->ManipulationDelta += ref new ManipulationDeltaEventHandler(this, &MainPage::btnMove_ManipulationDelta);
btnMove->ManipulationMode = ManipulationModes::TranslateX;
}
void App14::MainPage::InitManipulationTransforms()
{
transforms = ref new TransformGroup();
previousTransform = ref new MatrixTransform();
previousTransform->Matrix = Matrix::Identity;
deltaTransform = ref new CompositeTransform();
transforms->Children->Append(previousTransform);
transforms->Children->Append(deltaTransform);
// Set the render transform on the rect
btnMove->RenderTransform = transforms;
}
void App14::MainPage::btnMove_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
}
void MainPage::btnMove_ManipulationDelta(Platform::Object^ sender, ManipulationDeltaRoutedEventArgs^ e)
{
previousTransform->Matrix = transforms->Value;
// Get center point for rotation
Point center = previousTransform->TransformPoint(Point(e->Position.X, e->Position.Y));
deltaTransform->CenterX = center.X;
deltaTransform->CenterY = center.Y;
// Look at the Delta property of the ManipulationDeltaRoutedEventArgs to retrieve
// the rotation, scale, X, and Y changes
deltaTransform->Rotation = e->Delta.Rotation;
deltaTransform->TranslateX = e->Delta.Translation.X;
deltaTransform->TranslateY = e->Delta.Translation.Y;
}
You could change the scrolling direction of the button by modifying the ManipulationMode of button.
btnMove->ManipulationMode = ManipulationModes::TranslateY;

XAML Slider inside ScrollViewer

For Windows 10 UWP app I have such XAML structure:
<ScrollViewer>
<StackPanel>
<Slider />
<Slider />
...
</StackPanel>
</ScrollViewer>
I would like to create such user experience:
When the user begins horizontal swipe gesture, the slider under the touch should receive the input and start changing its value, while vertical scrolling is completely disabled (even when the user continue draw circle motions)
When the user begins vertical swipe gesture, the scrollviewer should receive the input and start scrolling vertically, while sliders under the touch should stay intact (even when the user continue draw circle motions).
Is it possible to implement this behavior in pure XAML? I think I have tried all possible combinations of properties related to scroll... No luck. Any idea anybody?
After testing on mobile emulator with OS version 10586, I found that when the ScrollViewer is vertically scrolled, it won't effect the Slider inside even if I draw circle, and when the Slider is swiped horizontally, only when its value reaches the max value, the vertical scrolling of the ScrollViewer will be effected if I draw circle.
Is it possible to implement this behavior in pure XAML?
Yes, it is possible.
When the user begins horizontal swipe gesture, the slider under the touch should receive the input and start changing its value, while vertical scrolling is completely disabled (even when the user continue draw circle motions)
You can install the Microsoft.Xaml.Behaviors.Uwp.Managed package in your project, then use its DataTriggerBehavior for example like this:
<ScrollViewer x:Name="scrollViewer" HorizontalScrollMode="Disabled" VerticalScrollMode="Auto">
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding ElementName=slider1,Path=FocusState}" ComparisonCondition="NotEqual" Value="Unfocused">
<Core:ChangePropertyAction PropertyName="VerticalScrollMode" Value="Disabled" />
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding ElementName=slider1,Path=FocusState}" ComparisonCondition="Equal" Value="Unfocused">
<Core:ChangePropertyAction PropertyName="VerticalScrollMode" Value="Auto" />
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding ElementName=slider2,Path=FocusState}" ComparisonCondition="NotEqual" Value="Unfocused">
<Core:ChangePropertyAction PropertyName="VerticalScrollMode" Value="Disabled" />
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding ElementName=slider2,Path=FocusState}" ComparisonCondition="Equal" Value="Unfocused">
<Core:ChangePropertyAction PropertyName="VerticalScrollMode" Value="Auto" />
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<StackPanel Height="1300">
<Slider Margin="0,200" x:Name="slider1" />
<Slider x:Name="slider2" />
</StackPanel>
</ScrollViewer>
When using this package in xaml, you will need to declare it in the header for example like this:
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
xmlns:Media="using:Microsoft.Xaml.Interactions.Media"
As you can see in my code, I compared FocusState property of the Slider, if its value is Unfocused, then the vertical scroll mode of the ScrollViewer is enabled. So when user interact with this layout, after swiping on the Slider, he will need to click on the blank part to make the Slider lost focus first, then the vertical scroll mode can be enabled.
When the user begins vertical swipe gesture, the scrollviewer should receive the input and start scrolling vertically, while sliders under the touch should stay intact (even when the user continue draw circle motions).
Base on my test, I think this gesture is ensured by default, so I didn't code for this. If it is not by your side, please leave a comment and provide your device type and OS version so can I have a test.
I have a very similar issue and was able to resolve it with the following custom control. This is a CommandSlider that also allows you to send commands like a button after the sliding is complete (not what you need) but the ScrollViewer manipulation code is in there also. See if this helps your situation. It allows you to do all the work in complete XAML like you've requested.
NOTE: The slider must have ManipulationMode="TranslateX" or "TranslateY"depending on the orientation for this to work also.
public sealed class CommandSlider : Slider
{
public CommandSlider()
{
IsThumbToolTipEnabled = false;
PointerCaptureLost += (s, e) => (Command?.CanExecute(CommandParameter)).GetValueOrDefault().Switch(() => Command?.Execute(CommandParameter));
Loaded += (s, e) => ParentScrollViewer = this.GetParent<ScrollViewer>();
}
private ScrollViewer ParentScrollViewer { get; set; }
protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e)
{
base.OnManipulationDelta(e);
if (ParentScrollViewer != null)
{
var scrollX = Orientation == Orientation.Vertical
? e.Position.X * -1 + ParentScrollViewer.HorizontalOffset
: ParentScrollViewer.HorizontalOffset;
var scrollY = Orientation == Orientation.Horizontal
? e.Position.Y * -1 + ParentScrollViewer.VerticalOffset
: ParentScrollViewer.VerticalOffset;
var zoom = ParentScrollViewer.ZoomFactor;
ParentScrollViewer.ChangeView(scrollX, scrollY, zoom);
}
}
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(nameof(CommandParameter), typeof(object), typeof(CommandSlider), new PropertyMetadata(null));
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(nameof(Command), typeof(ICommand), typeof(CommandSlider), new PropertyMetadata(null));
public double SourceValue
{
get => (double)GetValue(SourceValueProperty);
set => SetValue(SourceValueProperty, value);
}
public static readonly DependencyProperty SourceValueProperty =
DependencyProperty.Register(nameof(SourceValue), typeof(double), typeof(CommandSlider), new PropertyMetadata(0d,
(s, e) => (s as CommandSlider).Value = (double)e.NewValue));
}
Custom Extension Method
public static class XAMLExtensions
{
public static T GetParent<T>(this DependencyObject dependencyObject) where T : DependencyObject
{
var parentDependencyObject = VisualTreeHelper.GetParent(dependencyObject);
switch (parentDependencyObject)
{
case null:
return null;
case T parent:
return parent;
default:
return GetParent<T>(parentDependencyObject);
}
}
}

RenderTargetBitmap messes up the WebView scaling

I have a very simple repro case of RenderTargetBitmap.RenderAsync overload messing up the WebView scaling. All I have on the MainPage is a WebView and a button:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<WebView Source="http://bing.com"></WebView>
<Button Content="Render me"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Click="ButtonBase_OnClick" />
</Grid>
In code behind there's only a simple event handler
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
RenderTargetBitmap rtb = new RenderTargetBitmap();
await rtb.RenderAsync(this, 1280, 720);
}
This is what the page looks like before RenderAsync call:
and this is what happens after the call:
Any idea why and how to prevent this? Note that it only happens if I call
await rtb.RenderAsync(this, 1280, 720);
but NOT if I call the overload without the scaling
await rtb.RenderAsync(this);
EDIT: Due to the first answer I received, I wanted to clarify why the aspect ratio is not the problem here, but only serves the purpose of proving that there actually is a problem. Think of the following scenario - very high DPI screen where only a lower resolution screenshot is needed - even if you scale it down with the RIGHT ratio, it still messes up the WebView. Also, for my scenario, resizing the screenshot manually afterwards is not an option - the RenderAsync overload with scaled dimensions is much much faster and I would really prefer to use that method.
Very strange behavior...
I found one very dirty (!) fix to this. I basically hide and show the webview (wv) again.
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
RenderTargetBitmap rtb = new RenderTargetBitmap();
await rtb.RenderAsync(wv, 1280, 720);
wv.Visibility = Visibility.Collapsed;
await Task.Delay(100);
wv.Visibility = Visibility.Visible;
}
I'm not proud of this solution and the webview flashes, but at least it's not 'blown up' any more...
This is a bit of a hack too, but I found that if you set the contents of another control through a WebViewBrush and then render that control, then the source WebView doesn't get any scaling. I have modified the XAML you provided so it looks like this:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border x:Name="Target" Width="1280" Height="720" />
<WebView x:Name="webView" Source="http://bing.com" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></WebView>
<Button Content="Render me" Grid.Row="1"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Click="ButtonBase_OnClick" />
</Grid>
In your case, you should opt to set the Border control behind your WebView (however, don't change its Visibility or put it outside of the window bounds, as RenderAsync will fail). Then, on the code behind, set the Background of the target control to an instance of a WebViewBrush that feeds on the WebView:
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
WebViewBrush brush = new WebViewBrush();
brush.SetSource(webView);
Target.Background = brush;
Target.InvalidateMeasure();
Target.InvalidateArrange();
RenderTargetBitmap rtb = new RenderTargetBitmap();
await rtb.RenderAsync(Target, 1280, 720);
var pixels = await rtb.GetPixelsAsync();
}
You will get your final image without any issues caused to the source WebView (however, note that the final image will look distorted since the aspect ratios don't match). However this comes with a few caveats, the most important one being that the WebView size must match the one of the RenderTargetBitmap or you will get empty areas.
Instead of using fixed values, use VisibleBounds to get the current window size.
Here's the code:
private async void pressMe_Click(object sender, RoutedEventArgs e)
{
var windowBounds = ApplicationView.GetForCurrentView().VisibleBounds;
RenderTargetBitmap rtb = new RenderTargetBitmap();
await rtb.RenderAsync(this, (int)windowBounds.Width, (int)windowBounds.Height);
}

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)