Since I have updated to Xcode 12 and VS (2019 8.7.8 build 4) on my mac and updated Xamarin to the latest version (Xamarin.iOS 14.0.0.0), I find I am unable to use UIDatePicker.
DatePicker
Looking at the documentation, https://developer.apple.com/documentation/uikit/uidatepicker I need to set Style and maybe preferredDatePickerStyle but neither are these are properties I can set in the (Xamarin) code.
Has anyone found a way to get past this to enable the date to be selected?
It will work once we set the preferredDatePickerStyle and sizeToFit, Sample code is given below.
_datePicker = new UIDatePicker(new CGRect(0, 30, 0, 0));
_datePicker.AutoresizingMask = UIViewAutoresizing.FlexibleRightMargin;
_datePicker.Frame = new CGRect(_datePicker.Frame.Location, new CGSize(300, _datePicker.Frame.Size.Height));
_datePicker.Mode = _datePickerMode;
_datePicker.Date = (NSDate)_defaultDate;
_datePicker.PreferredDatePickerStyle = UIDatePickerStyle.Wheels; //Add this in ios14
_datePicker.SizeToFit(); //Add this in ios14
I have resolved it with this:
_datePicker.SetValueForKey(new NSNumber(1), new NSString("preferredDatePickerStyle"));
It is now showing the correct selector
Date Selector
I'm basically putting together all the details of the other answers into the corresponding implementation in public class CustomDatePickerRenderer : DatePickerRenderer for Xamarin Forms in iOS (omitted my implementation of CreateNativeControl, OnElementPropertyChanged, and custom methods for brevity):
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
const int iosVersionMajorForDatePickerStyleFeature = 13;
const int iosVersionMinorForDatePickerStyleFeature = 4;
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
//Switch back to the Wheels style for the datePicker (in iOS 14 the default of Automatic uses the Compact style)
if (UIDevice.CurrentDevice.CheckSystemVersion(iosVersionMajorForDatePickerStyleFeature, iosVersionMinorForDatePickerStyleFeature)
&& Control?.InputView is UIDatePicker datePicker && datePicker.PreferredDatePickerStyle != UIDatePickerStyle.Wheels)
{
datePicker.PreferredDatePickerStyle = UIDatePickerStyle.Wheels;
datePicker.SizeToFit();
}
//Do whatever other customization is desired
//UpdateBorderColor();
//UpdateBorderWidth();
//UpdateCornerRadius();
//UpdatePadding();
}
I also encountered this interesting problem recently. When we used Xcode 12 to package it, everything was normal, but when switching to Xcode 12 and above for packaging date selection and display, it would appear abnormal.
Because iOS 13.4 UIDatePicker has new features
#property (nonatomic, readwrite, assign) UIDatePickerStyle preferredDatePickerStyle API_AVAILABLE(ios(13.4)) API_UNAVAILABLE(tvos, watchos);
If you want to keep the original style, you can add this code
_datePick = [[UIDatePicker alloc] init];
if (#available(iOS 13.4, *)) {
_datePick.preferredDatePickerStyle = UIDatePickerStyleWheels;
}
Related
I've been struggling with the Samsung Galaxy numeric keyboard decimal separator nightmare.
I have an Entry on which I must only accept numeric values (including decimals), so in my xaml code I wrote <Entry Keyboard="Numeric"/> and it worked just fine on the VS19 android simulator. But when I run the app on my physical device (Samsung Galaxy S10+) the comma key is disabled and the .- key doesn't work (it types nothing).
I made some digging online and found a few solutions, but none worked for me.
SOLUTION 1
Forcing the app culture to pt-BR (because my phone system language is brazilian portuguese) by adding this code to App() (in App.xaml.cs):
private void SetCultureToPTBR()
{
CultureInfo br = new CultureInfo("pt-BR");
CultureInfo.DefaultThreadCurrentCulture = br;
}
But there were no changes.
SOLUTION 2
Setting input type on a custom renderer for entries:
[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))]
namespace AppCoperNitro.Droid.CustomRenderers
{
public class CustomEntryRenderer : EntryRenderer
{
public CustomEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control == null || e.NewElement == null)
return;
this.Control.KeyListener = DigitsKeyListener.GetInstance(true, true);
this.Control.InputType = Android.Text.InputTypes.ClassNumber | Android.Text.InputTypes.NumberFlagDecimal;
}
}
}
This method changed the .- key to a comma key, and it kinda works (it types a .), but the decimal place is ignored (if I type 2.3 the app receives it as 23).
SOLUTION 3
This solution, but the result was the same as solution 2.
I also tried combining these solutions and their variances, but nothing works the way it should. I don't even care if the entry shows a comma or a dot, I just need the decimal number to be received correctly.
We can change the ways. According to your SOLUTION 2, the entry can input the ., so you can do something in the Entry's UnFocused event. Such as: // Use Gabic's code
private void Entry_Unfocused(object sender, FocusEventArgs e)
{
Entry entry = sender as Entry;
if (entry.Text.Length > 0 && Android.OS.Build.Manufacturer.Equals("Samsung", StringComparison.CurrentCultureIgnoreCase))
{
string a = entry.Text.Replace('.', ',');
entry.Text = a;
}
}
And then add the event into the xaml:
<Entry Keyboard="Numeric" Unfocused="Entry_Unfocused"/>
I'm trying to customise an Entry field for iOS platform with Visual=Material enabled.
I tried via CustomRenderer but since is iOS platform I don't know to how to reach, for example, to modify the material bottom border color without modifying the whole text color for the control.
[assembly: ExportRenderer(typeof(Entry), typeof(CustomMaterialEntryRenderer), new[] { typeof(VisualMarker.MaterialVisual) })]
public class CustomMaterialEntryRenderer : MaterialEntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control == null || e.NewElement == null) return;
Layer.BorderColor = Color.FromHex("#cedee7").ToCGColor();
}
}
To be clear enough, just in case, I want the bottom line for example in red and text in black.
Thanks in advance!
It seems an existing issue that the CustomRenderer will never been called .We will focus on this issue .
Workaround 1:
If you just want to set the underline color of Entry . It is unnecessary to set Visual=Material.You just need to create a default Custom Renderer of Entry.
if (Control != null)
{
Control.BorderStyle = UITextBorderStyle.None;
UIView lineView = new UIView()
{
Frame = new CGRect(0, Element.HeightRequest - 1, Element.WidthRequest, 1),
BackgroundColor = UIColor.Red,
};
Control.AddSubview(lineView);
}
Don't forget to set the WidthRequest and HeightRequest in xaml.
Workaround 2
Fortunately,there are many plugin of Material Controls from nuget. And you can download and use it directly . For example MaterialFormControls
Download the package from Nuget Manager (make sure to check the include prerelease)
And set the property AccentColor to change the under line color
<local:MaterialEntry IsPassword="True" Placeholder="email" AccentColor="Red"/>
I have Xamarin forms time picker following custom renderer for IOS
[assembly: ExportRenderer(typeof(TimePicker), typeof(Time24PickerRenderer))]
namespace LabOraTimeStamp.iOS.Renderers
{
public class Time24PickerRenderer:TimePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
{
base.OnElementChanged(e);
var timePicker = (UIDatePicker)Control.InputView;
timePicker.Locale = new NSLocale("no_nb");
//Get the Done button
var toolbar = (UIToolbar)Control.InputAccessoryView;
var doneBtn = toolbar.Items[1];
//Set the Done to OK
doneBtn.Title = "OK";
}
}
}
I wanted to change the default "done" to "Ok".
1) How can I do that? the line mentioned above for setting the title does not affect anything.
2) I already implemented localization for xamarin forms.I just wanted to use existing Resx values from custom renderer to show the string for appropriate culture.How can I achieve that?
So the reason why your code isn't working is because the done button is created with the UIBarButtonSystemItem.Done style. It doesn't care about the Title property. Renderer code here.
To work around that issue you could try replacing the Xamarin created done button with your own custom Ok button.
//Get the Done button
var toolbar = (UIToolbar)Control.InputAccessoryView;
// Replace Xamarin's buttons with custom ones.
var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
var doneButton = new UIBarButtonItem();
doneButton.Title = "OK";
doneButton.Clicked += (o, a) => Control.ResignFirstResponder();
toolbar.SetItems(new [] { spacer, doneButton}, false);
Microsoft's own site does not explain in details how to use this interface. They claim that this is the way to get notified if the Fonts & Colors change in Visual Studio.
I tried what seemed to be an obvious choice and implemented the interface on my package, but there were no attributes mentioned I should set on my VSPackage. Unfortunately that doesn't seem to be enough.
Here's a sample of what I did:
public class SceVSIPackage : Package, IVsFontAndColorEvents
{
public int OnApply()
{
return VSConstants.S_OK;
}
public int OnFontChanged(ref Guid rguidCategory, FontInfo[] pInfo, LOGFONTW[] pLOGFONT, uint HFONT)
{
return VSConstants.S_OK;
}
public int OnItemChanged(ref Guid rguidCategory, string szItem, int iItem, ColorableItemInfo[] pInfo, uint crLiteralForeground, uint crLiteralBackground)
{
return VSConstants.S_OK;
}
public int OnReset(ref Guid rguidCategory)
{
return VSConstants.S_OK;
}
public int OnResetToBaseCategory(ref Guid rguidCategory)
{
return VSConstants.S_OK;
}
}
Unfortunately none of the IVsFontAndColorEvent members (all the methods above) get called.
Do I miss something else? Like an attribute? Or proffering the service?
I also tried serviceContainer.AddService(typeof(IVsFontAndColorEvent), this, true); but it didn't help either.
Workaround
Unfortunately I couldn't make IVsFontAndColorEvents working. However, I could achieve the same (getting notified when the Fonts change in Tools\Options\Fonts and Colors\Text Editor) with the code found here.
The idea is to use TextManagerEvents instead of IVsFontAndColorEvents:
//using Microsoft.VisualStudio.TextManager.Interop;
IVsTextManager textManager = GetService(typeof(SVsTextManager)) as IVsTextManager;
if (textManager != null)
{
IConnectionPointContainer container = textManager as IConnectionPointContainer;
if (container != null)
{
IConnectionPoint textManagerEventsConnection;
Guid eventGuid = typeof(IVsTextManagerEvents).GUID;
container.FindConnectionPoint(ref eventGuid, out textManagerEventsConnection);
if (textManagerEventsConnection != null)
{
TextManagerEvents textManagerEvents = new TextManagerEvents();
uint textManagerCookie;
textManagerEventsConnection.Advise(textManagerEvents, out textManagerCookie);
if (textManagerCookie != 0)
{
textManagerEvents.FontColorPreferencesChanged += OnFontColorPreferencesChanged;
}
}
}
}
Notes
1. OnFontColorPreferencesChanged
Just in case you are also interested in how to extract the font and color information, here is how I did it:
private FontInfo prevFontInfo; // Store previous FontInfo to prevent execution of the event handler multiple times.
private void OnFontColorPreferencesChanged(object sender, EventArgs e)
{
IVsFontAndColorStorage fontAndColorStorage = GetService(typeof(SVsFontAndColorStorage)) as IVsFontAndColorStorage;
if (fontAndColorStorage != null)
{
// GlobalValues.FontsAndColors_TextEditor is found in the registry: HKEY_USERS\.DEFAULT\Software\Microsoft\VisualStudio\[VS_VER]_Config\FontAndColors\Text Editor, where VS_VER is the actual Visual Studio version: 10.0, 11.0, 12.0, 14.0, etc.
if (fontAndColorStorage.OpenCategory(GlobalValues.FontsAndColors_TextEditor, (uint)__FCSTORAGEFLAGS.FCSF_LOADDEFAULTS) == VSConstants.S_OK)
{
LOGFONTW[] logFontw = new LOGFONTW[1]; // Only 1 item expected
FontInfo[] fontInfo = new FontInfo[1]; // Only 1 item expected
if (fontAndColorStorage.GetFont(logFontw, fontInfo) == VSConstants.S_OK &&
!prevFontInfo.Equals(fontInfo[0]))
{
prevFontInfo = fontInfo[0];
// FontInfo uses pixels as units, WPF uses points. Conversion between the two is required.
double fontSize = (double)new FontSizeConverter().ConvertFrom(string.Format("{0}pt", fontInfo.wPointSize));
FontFamily fontFamily = new FontFamily(fontInfo.bstrFaceName);
// There you go, you have the FontFamily and size ready to use.
}
fontAndColorStorage.CloseCategory();
}
}
}
2. Limitations
Although this solutions is a usable workaround for me, it has some problems:
when changing the font of the Text Editor, the OnFontColorPreferencesChanged event is raised multiple times. I can't tell if IVsFontAndColorEvents would raise the event only once or had the same problem (as I never got it working.) I solved this issue by introducing prevFontInfo and don't invoke my logic unless this value is different from fontInfo[0], the values I just read.
the event fires only when the Text Editor fonts and colors are changed, but not when any of the rest (e.g. Environment Font or Output Window)
the event does not fire when the bold option is changed. Nevertheless, the font weight is not seemed to be used by the IDE anyway...
the event does not fire when "Use Defaults" is selected in Options/Fonts and Colors. As a matter of fact it doesn't fire either when it's reset to the default values by manually entering them (e.g.: font size to 10)
I hope some of these might be useful for someone stumbling upon this question.
Admins: sorry if I asked this on the wrong forum, I'm new to the whole SE network.
I've been working on a project for some time now, and now I'm stuck. The project is a Universal Windows 8.1 runtime app programmed in C# and XAML. Below is a function from the Windows 8.1 part of the project called updateMapGUI(). It is called after an event is triggered and adds items to a pre-existing canvas on the page.
private void updateMapGUI()
{
while (mapCanvas.Children.Count > 0)
{
mapCanvas.Children.RemoveAt(0);
}
panelTracker = 0;
foreach (User i in activeUsers)
{
if(i == null)
{
return;
}
else
{
//Update the mapCanvas with data from the appropriate User object from the array activeUsers[]
dotpanels[panelTracker] = new Canvas();
mapCanvas.Children.Add(dotpanels[panelTracker]);
dotpanels[panelTracker].Height = 3;
dotpanels[panelTracker].Width = 3;
dotpanels[panelTracker].Left = getXOffset(i);
dotpanels[panelTracker].Up = getYOffset(i);
if (i.isSelected == false)
{
SolidColorBrush unselectedDot = new SolidColorBrush(Windows.UI.Colors.Blue);
dotpanels[panelTracker].Background = unselectedDot;
}
else
{
SolidColorBrush selectedDot = new SolidColorBrush(Windows.UI.Colors.Green);
dotpanels[panelTracker].Background = selectedDot;
}
panelTracker++;
}
}
}
dotpanels is a canvas[] array and panelTracker is an int[] array. The issue is with dotpanels[panelTracker].Left and dotpanels[panelTracker].Up. According to the documentation, this function can be called on any child of a canvas (not quite sure if I read that correctly). The compiler says that the Canvas in dotpanels[panelTracker] does not contain a definition for Up or Left. What am I doing wrong? If it's not possible, what I basically need to do is show some dots at specific X and Y locations. Thanks in advance for your responses!
Canvas.Left and Canvas.Top (not Up) are attached properties, which are accessed in code via static getter and setter methods:
Canvas.SetLeft(dotpanels[panelTracker], getXOffset(i));
Canvas.SetTop(dotpanels[panelTracker], getYOffset(i));