How to create a calendar type date picker in Xamarin forms UWP app.
So far I have tried this:
<Image Grid.Row="3" Grid.Column="1" Source="Images/datePicker.png" WidthRequest="20" HeightRequest="20" Margin="-40,0,40,0" HorizontalOptions="End">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnSelectDOBCommand}"/>
</Image.GestureRecognizers>
</Image>
public void OnSelectDOB(object obj)
{
dateOfBuildDatePicker = UserDialogs.Instance.DatePrompt(new DatePromptConfig { MaximumDate = DateTime.Today, OnAction = (result) => SetDateOfBirth(result), IsCancellable = true });
}
But this displays the date picker control on top left corner of the screen.
Is there a way to customize it so that it should display the date picker right next to the field where I click or is there any other control which can help me achieve this functionality.(Below image is the behavior that I am expecting)
Any help is appreciated]1
If you want to consume a special control on UWP platform, you can try custom renderer.
Firstly, create a custom control:
public class CustomDatePicker : DatePicker
{
}
Then make a custom renderer on UWP:
[assembly: ExportRenderer(typeof(CustomDatePicker), typeof(CustomDatePickerRenderer))]
namespace Demo.UWP
{
public class CustomDatePickerRenderer : ViewRenderer<DatePicker, Windows.UI.Xaml.Controls.CalendarDatePicker>
{
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
if (Control == null)
{
Windows.UI.Xaml.Controls.CalendarDatePicker datePicker = new Windows.UI.Xaml.Controls.CalendarDatePicker();
SetNativeControl(datePicker);
}
}
}
}
At last, use it on your forms project:
<StackLayout>
<local:CustomDatePicker HorizontalOptions="Start"/>
</StackLayout>
Alternatively, you can try native view: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/native-views/
date value is always set to the current date even you selected a data. how to resolve this?
Changing the date value requires working with the date change events. See my working solution here:
https://stackoverflow.com/a/60449912/9523062
Related
Im creating an App where I want have multiple icons in my titlebar (they should be clickable). One Icon should be on the Left of the title text like so (this icon is static and will not change):
The other Icons should be on the right (these icons should be dynamic, as they will be used to alert the user if anything important happens):
I'm using a simple shell, so no Navigationpage, thus I don't think <NavigationPage.Titleview> can be used in this case. Is there any way to achieve something like this in Maui?
You can use the shell title view as stated in this answer, it is pointing to Xamarin documentation but it is also valid for MAUI.
In your case you will have something like this inside your AppShell.xaml :
<Shell.TitleView>
<Grid ColumnDefinitions="auto,*,auto">
<Label Text="Title" BackgroundColor="Red"></Label>
<HorizontalStackLayout Grid.Column="2">
<Label Text="Icon" BackgroundColor="Red"></Label>
</HorizontalStackLayout>
</Grid>
</Shell.TitleView>
Which look like this (very trendy design) :
How to update the icons can be done by data binding or creating directly the object from C# :
<HorizontalStackLayout Grid.Column="2" x:Name="IconsContainer">
<Label Text="Icon" BackgroundColor="Red" IsVisible="{Binding Icon1Visible}"></Label>
</HorizontalStackLayout>
And in the C# :
public partial class AppShell : Shell
{
public bool Icon1Visible { get; set; } = false;
public AppShell()
{
InitializeComponent();
// An object containing the binding data
BindingContext = this;
// Directly create a new icon
AddIcon();
// Show an icon through binding
ShowIcon();
}
public void ShowIcon()
{
Icon1Visible = true;
}
public void AddIcon()
{
var icon = new Label();
icon.Text = "And another one";
IconsContainer.Children.Add(icon);
}
}
To get whenever you want to update the icons I would go with dependency injection and either pass directly a view model to bind the icons on or using events.
I really don't know how to get control dimensions in Xamarin Forms. I thought it would be as easy as:
View control = GetControlSomehow();
var bounds = control.Bounds;
But View.Bounds returns different things depending on wether the control is inside a grid or not; or wether the main layout is AbsoluteLayout or some other one.
For example if I do something like:
<AbsoluteLayout>
<Button AbsoluteLayout.LayoutBounds="200,200" x:Name="btn" Text="Btn"/>
</AbsoluteLayout>
then I am not able to read actual height or width of that button. Although MSDN says that Height & Width properties should return these values.
I tried even:
var width = ctrl.Width;
or
var width = ctrl.GetValue(WidthProperty);
But really non of these worked. I always get -1
So how can I get the control dimensions that could be reliable?
Complete example showing how to pass button itself, using data binding.
AbsoluteLayoutPage.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestBugs.AbsoluteLayoutPage">
<ContentPage.Content>
<AbsoluteLayout>
<Button AbsoluteLayout.LayoutBounds="200,200" x:Name="btn" Text="Btn"
Command="{Binding ButtonCommand}" CommandParameter="{x:Reference btn}"/>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
AbsoluteLayoutPage.xaml.cs:
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TestBugs
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AbsoluteLayoutPage : ContentPage
{
public AbsoluteLayoutPage()
{
ButtonCommand = new Command(ButtonClick);
InitializeComponent();
BindingContext = this;
}
private void ButtonClick(object ob)
{
var button = ob as View;
var bounds = btn.Bounds;
var width = btn.Width;
var height = btn.Height;
Debug.WriteLine($"--- ButtonClick {bounds}, ({width},{height})---");
}
public Command ButtonCommand { get; set; }
}
}
When click button, in VS Output pane:
[0:] --- ButtonClick {X=200 Y=200 Width=88 Height=48}, (88,48)---
this works for me
<Button Text="Click" Clicked="Clicked" />
public void Clicked(object sender, EventArgs a)
{
var btn = (Button)sender;
var w = btn.Width;
var h = btn.Height;
DisplayAlert("Dimesions", $"{w}x{h}","OK");
}
Sorry guys, my bad.
I was testing it on two pages - the real one and the test one. The test page is built in a different way and I confirm that the problem was in time when I retrieved these values. Control wasn't painted yet.
So I am really sorry to bother you and thank you all for investing your time into this.
I am stuck with this annoying problem in iOS(works perfectly fine in Android) and I can't seem to find a solution at all. Could someone please help me? Thanks.
I have tabbed pages in which I have a Message Tab which consists of a collection view which shows the user messages. When the app starts I download about 5 messages and then wait for the remainingitemthresholdreached to fire so I can go ahead and fetch more data but unfortunately, it doesn't fire and I am stuck with the initial 5 messages.
XAML
<CollectionView Grid.Row="1" ItemsSource="{Binding AllMessages, Mode=TwoWay}" x:Name="myMessagesCV" SelectionMode="Single" SelectionChanged="MyMessagesCV_SelectionChanged" RemainingItemsThresholdReached="MyMessagesCV_RemainingItemsThresholdReached" RemainingItemsThreshold="5">
<CollectionView.ItemTemplate>
<DataTemplate>
// Some Code here
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Code Behind
private void MyMessagesCV_RemainingItemsThresholdReached(object sender, EventArgs e)
{
if (IsBusy)
return;
IsBusy = true;
if (!LoadedAllMyMessages)
{
GetMyMessages();
}
if (LoadedAllMyMessages)
{
myMessagesCV.RemainingItemsThreshold = -1;
}
}
Please someone save me. Cheers guys!
The RemainingItemsThresholdReached could be triggered with enough items.
You could check it in IncrementalLoadingPage of Scrolling. Or you could use the RemainingItemsThresholdReachedCommand
Here is the example for yoru reference. https://github.com/xamarin/xamarin-forms-samples/tree/master/UserInterface/CollectionViewDemos
I have a popup page in my application which includes some checkboxes, and I also have a ContentPage in my MainPage which contains some stacklayouts, I want to bind the IsVisible Property of my stacklayout to the IsChecked property of my checkbox (which is in another page), but i don't know how to do it, can anybody please help?
I've tried this, but it doesn't work
here's the code in my popup page:
<StackLayout Orientation="Horizontal" Spacing="60">
<local:Checkbox x:Name="va1Checkbox" Text="VA1"/>
<local:Checkbox x:Name="va2Checkbox" Text="VA2"/>
</StackLayout>
and this is the piece of code i have in my MainPage:
<StackLayout IsVisible="{Binding Source={x:Reference
va1Checkbox},Path=IsChecked}">
</StackLayout>
Thanks in advance
<StackLayout Orientation="Horizontal" Spacing="60">
<local:Checkbox x:Name="va1Checkbox" Text="VA1"/>
<local:Checkbox x:Name="va2Checkbox" Text="VA2"/>
</StackLayout>
I believe your popup represents some kind of settings based on which you control your StackLayout in Main Page.
If this is the case, there are two possible solutions.
Create a separate class which is binding for both checkbox and layout. You can use this either with MVVM or without it.
Value Passing: open popup from Main Page and register to its close event. When popup is closed you can use its checkbox's value to enable/disable the layout.
In your Popup.xaml.cs :
create an event action
public event Action<Popup> OnClose;
I believe you will have a way to close it. I don't know you are closing it so I will just use OnBackButtonPressed() here:
protected override bool OnBackButtonPressed()
{
OnClose?.Invoke(this);
return base.OnBackButtonPressed();
}
In your MainPage.xaml.cs :
private void OpenPopup()
{
var popup = new PopupPage();
popup.OnClose += OnPopupClosed;
}
void OnPopupClosed(Popup popup)
{
yourStackLayout.IsVisible = popup.va1Checkbox.Value;
}
UPDATE:
You can pass custom data class instead of popup object:
public class PopupData
{
public bool va1CheckboxValue;
public bool va1CheckboxValue;
// other data which you need to access in other page.
}
then
public event Action<PopupData> OnClose;
and
protected override bool OnBackButtonPressed()
{
PopupData data = new PopupData() { va1CheckboxValue = va1Checkbox.Value; }
OnClose?.Invoke(data);
return base.OnBackButtonPressed();
}
and in main page:
void OnPopupClosed(PopupData data)
{
yourStackLayout.IsVisible = data.va1CheckboxValue;
}
Hope this helps.
I don't know very well XAML and MVVM, but I think you can pass MainPage's ViewModel to Popup... if you change MainPage's ViewModel properties in Popup I think these changes will reflect also to MainPage's binded controls.
If you are not using MVVM (you should...), I think you have to pass a reference to properties used in MainPage to Popup... in this way, Popup can change MainPage's properties and its UI could change
I have to create a form in which the user must input his age. I would like to use a numeric keyboard:
<Entry
x:Name="AgeEntry"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Keyboard="Numeric"
/>
but it shows even the decimal point character, I'd like to show only numbers...
To restrict the Entry to only accept numbers you could use a Behavior or a Trigger.
Both of those will react to a user typing into them. So for your use, you could have the trigger or behavior look for any characters that are not numbers and remove them.
Something like this for a behavior (note that I wrote all this on SO and did not try compiling it, let me know if it does not work):
using System.Linq;
using Xamarin.Forms;
namespace MyApp {
public class NumericValidationBehavior : Behavior<Entry> {
protected override void OnAttachedTo(Entry entry) {
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry) {
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
private static void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
if(!string.IsNullOrWhiteSpace(args.NewTextValue))
{
bool isValid = args.NewTextValue.ToCharArray().All(x=>char.IsDigit(x)); //Make sure all characters are numbers
((Entry)sender).Text = isValid ? args.NewTextValue : args.NewTextValue.Remove(args.NewTextValue.Length - 1);
}
}
}
}
Then in your XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyApp;assembly=MyApp"> <!-- Add the local namespace so it can be used below, change MyApp to your actual namespace -->
<Entry x:Name="AgeEntry"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Keyboard="Numeric">
<Entry.Behaviors>
<local:NumericValidationBehavior />
</Entry.Behaviors>
</Entry>
</ContentPage>
To me the simple solution is just to add a TextChanged handle to the Entry (this is UI specific code so does not break MVVM)
<Entry Text="{Binding BoundValue}" Keyboard="Numeric" TextChanged="OnTextChanged"/>
Then in code behind
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
//lets the Entry be empty
if ( string.IsNullOrEmpty(e.NewTextValue) ) return;
if ( !double.TryParse(e.NewTextValue, out double value) )
{
((Entry) sender).Text = e.OldTextValue;
}
}
Change in int.Parse if need an integer
I was facing this too, but the best way to implement this is through behaviors, here is an article which shows you in detail how to implement this and even more, like limiting the value of the number entered to a given value you set in your XAML.
if (!string.IsNullOrWhiteSpace(args.NewTextValue))
{
bool isValid = args.NewTextValue.ToCharArray().All(char.IsDigit);
((Editor)sender).Text = isValid ? args.NewTextValue : args.OldTextValue;
}
This prevents adding any non digit char into the Editor no matter where you added it.
So if your cursor was in the middle of your number and you got for example 21h3 in #hvaughan3's answer you would just remove the last char giving you 21h with the non digit still being present.
My answer will just use the value you had before adding the non digit char (213).
But keep in mind!
If you put this in a behavior and react to editor.TextChanged Event, this is still gonna change the Text for a brief second to the invalid value since this event triggers when it ALREADY was set. this is no OnTextChanging event.
Same for #hvaughan3's answer.
To prevent any issues you might get because of this, for example because you're also listening to OnTextChanged in Code behind, just add this line before working with the new text value
if (!editor.Text.All(char.IsDigit)) return;