I am learning C# and Xamarin.Forms at the moment, so please treat me like a complete beginner.
I have an entry in my XML in Xamarin.Forms:
<Entry x:Name="temperature" Placeholder="Temperatur" Keyboard="Numeric" Margin="20,0"/>
In my CS:
double temp = double.Parse(temperature.Text);
This all works fine. But I need to use a different kind of entry for Android. Because Xamarin.Forms Entry do not handle decimal separators very well when it comes to Samsung phones.
On Android I want to use NuGet "NumericEditText-Xamarin.Android" like this:
<br.com.akamud.NumericEditText
android:id="#+id/txtNumeric"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number|numberDecimal" />
It should handle my entry input, and then of course handle the entry after that with the shared code of Xamarin.Forms. Is dependency service the best way to go about solving this?
Would someone be so kind to help me out in the right direction.
Dependency service is used to invoke native api on specific project, if the issue is related to UI, a custom renderer is the better choice.
You need to create a custom renderer for a custom view, and replace the view with NumericEditText inside the custom renderer.
Create Custom View
public class EntryView : View
{
}
Usage of Custom view in page
xmlns:local ="clr-namespace:App2"
<local:EntryView/>
Custom renderer
[assembly: ExportRenderer(typeof(EntryView), typeof(MyRenderer))]
namespace App2.Droid
{
public class MyRenderer : Xamarin.Forms.Platform.Android.ViewRenderer<EntryView, NumericEditText>
{
Context _context;
NumericEditText editView;
public MyRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<EntryView> e)
{
base.OnElementChanged(e);
if(e.NewElement != null)
{
if (Control == null)
{
editView = new NumericEditText(Context);
editView.InputType = InputTypes.NumberFlagDecimal|InputTypes.ClassNumber;
SetNativeControl(editView);
}
}
}
}
}
Refer to https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/view.
Related
I made a Xamarin.Forms project and I referenced my own font and then added an alias in the assembly like so:
[assembly: ExportFont("Samantha.ttf", Alias = "MyAwesomeCustomFont")]
Now... the thing is, this font which I have referenced is NOT being shown in the designer, instead I'm still getting the default font, even after referencing it:
<Button WidthRequest="70" Text="Click me" FontFamily="MyAwesomeCustomFont"/>
Now, this is frustrating because it is working when I deploy it and it showing the font
although I want the designer to illustrate exactly what's going on, so I do want the custom font to be shown in the designer... Is this possible?
Thanks,
I've solved my own problem. I created a custom renderer and it's showing in the preview:
[assembly: ExportRenderer(typeof(Xamarin.Forms.Button), typeof(CustomButtonRenderer))]
namespace Custombutton.Droid
{
class CustomButtonRenderer : ButtonRenderer
{
public CustomButtonRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SetAllCaps(false);
Typeface tf = Typeface.CreateFromAsset(Android.App.Application.Context.Assets, "NUNITO-BOLD.ttf");
Control.SetTypeface(tf, TypefaceStyle.Bold);
}
}
}
}
Result in designer:
So I am working on Android application. Right now I am creating MainPage where I insert Entry which has the bottom line as always. The bottom line on my previewer is White while on my phone it appears to be Black.
So to fix the issue I decided to play with renderers and see if I can fix it.
I created Class in App called CustomEntryRenderer which inherits from Entry.
Then I created Class in App.Android called CustomEntryRednererAndroid which is supposed to change the color of bottom entry line. But it doesn`t affect it. I tried doing the same with some custom renderers I found on the internet.
For example deleting bottom line didn`t affect the program as well:
removing line
Entry from MainPage.xaml:
<Entry
Grid.Row="4"
Grid.ColumnSpan="2"
TextColor="Silver"
Placeholder="Write Your nickname"
PlaceholderColor="Silver"
/>
CustomEntryRenderer:
public class CustomEntryRenderer : Entry
{
}
CustomEntryRendererAndroid:
[assembly: ExportRenderer(typeof(CustomEntryRenderer), typeof(MyEntryRenderer))]
namespace App3.Droid
{
public class MyEntryRenderer : EntryRenderer
{
public MyEntryRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control == null || e.NewElement == null) return;
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.White);
else
Control.Background.SetColorFilter(Android.Graphics.Color.White, PorterDuff.Mode.SrcAtop);
}
}
}
Top answer for Android
And for some reason also in CustomEntryRendererAndroid.cs I had to use Android.Graphic instead of Xamarin.Forms.Color. But I dont think that is the issue.
I have been trying for a couple of hours now and can`t find the way out of this situation.
Would appreciate any ideas.
In xaml you are using the default Entry control and not your CustomEntryRenderer which is what your renderer is affecting. Also, you might want to rename it because it is not actually your renderer, but your custom control.
To resolve your issue you can either change your renderer typeof(CustomEntryRenderer) to typeof(Entry) to affect all Android entries in your app by default. For example, this worked for my test app for all Entries:
[assembly: ExportRenderer(typeof(Entry), typeof(MyEntryRenderer))]
namespace YourNameSpace
{
public class MyEntryRenderer : EntryRenderer
{
public MyEntryRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control == null || e.NewElement == null) return;
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
Control.BackgroundTintList = ColorStateList.ValueOf(Android.Graphics.Color.White);
else
Control.Background.SetColorFilter(Android.Graphics.Color.White, PorterDuff.Mode.SrcAtop);
}
}
}
The other option is to switch your xaml code in MainPage to actually use your custom control. For example, <local:CustomEntryRenderer/>
Adding this in the Styles.xml repairs it globally
<item name="colorControlNormal">#1BB8A3</item>"
Today I updated to Xamarin.Forms 2.5.0 and saw, that I get the following warnings:
From the Android sub-project:
Warning CS0618 'Forms.Context' is obsolete: 'Context is obsolete as of version 2.5. Please use a local context instead.'
How can I get the local context instead of Forms.Context? Is the Android Context meant?
From the custom renderer:
Warning CS0618 'ButtonRenderer.ButtonRenderer()' is obsolete: 'This constructor is obsolete as of version 2.5. Please use ButtonRenderer(Context) instead.'
In my ButtonRenderer I only have the OnElementChanged() method, so what should I change here? Simply add a ButtonRenderer(Context) constructor? I still get the warning, if I do this in my platform renderer class. Does anyone have an example? The official documentation doesn't mention it and Google also doesn't bring some useful results, except the open source code of ButtonRenderer. This change also concerns many other renderer classes.
Does anyone had experienced other changes, which brakes plugins and so on?
PS: Also I didn't found out, when Device.Windows was deprecated. Now I replaced it with Device.UWP.
I had this same issue for a SearchBarRenderer and all I needed to do to fix it was add a constructor like so:
public ShowSearchBarRenderer(Context context) : base(context)
{
}
Hope that answers the second part of your question.
There are two questions here:
How do I update Custom Renderers to use a local context?
How can I access the current context now that Xamarin.Forms.Forms.Context is obsolete?
How to Update Custom Renderers
Add the overloaded Constructor to each custom renderer
Here is an example using a ButtonRenderer
[assembly: ExportRenderer(typeof(CustomButton), typeof(CustomButtonRenderer))]
namespace MyApp.Droid
{
public class CustomButtonRenderer : ButtonRenderer
{
public CustomButtonRenderer(Context context) : base(context)
{
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
//ToDo: Customize Button
}
}
}
How to Access The Current Context
Install Xamarin.Essentials NugGet Package.
Now, you can call Xamarin.Essentials.Platform.AppContext when you need to access the current activity.
Here's an example of how to open the App's Settings in Xamarin.Forms.
[assembly: Dependency(typeof(DeepLinks_Android))]
namespace MyApp.Droid
{
public class DeepLinks_Android : IDeepLinks
{
Context CurrentContext => Xamarin.Essentials.Platform.AppContext;
public Task OpenSettings()
{
var myAppSettingsIntent = new Intent(Settings.ActionApplicationDetailsSettings, Android.Net.Uri.Parse("package:" + CurrentContext.PackageName));
myAppSettingsIntent.AddCategory(Intent.CategoryDefault);
return Task.Run(() =>
{
try
{
CurrentContext.StartActivity(myAppSettingsIntent);
}
catch (Exception)
{
Toast.MakeText(CurrentContext.ApplicationContext, "Unable to open Settings", ToastLength.Short);
}
});
}
}
}
use Android.App.Application.Context
There is a discussion of this topic on the Forums
I'm trying to open a settings view in a Caliburn.Micro WinRT 8.1 app using VS2013 RC, but I keep getting an unhandled exception when opening it with the following message:
Value cannot be null. Parameter name: Could not parse the VisualElements from the app manifest.
I can reproduce the issues with the following steps:
create a new Windows Store app from VS2013 RC using the Blank app template.
add Caliburn.Micro via NuGet.
in App.xaml, change the base class to caliburn:CaliburnApplication (the namespace is declared as xmlns:caliburn="using:Caliburn.Micro").
in App.xaml.cs, change the class like this (for the CM-based settings I follow http://compiledexperience.com/blog/posts/settings-caliburn)
Code below:
public sealed partial class App
{
private WinRTContainer _container;
public App()
{
InitializeComponent();
}
protected override void Configure()
{
_container = new WinRTContainer();
_container.RegisterWinRTServices();
_container.PerRequest<MainViewModel>();
_container.PerRequest<SettingsViewModel>();
ISettingsService settings = _container.RegisterSettingsService();
settings.RegisterCommand<SettingsViewModel>("Test settings");
}
protected override object GetInstance(Type service, string key)
{
var instance = _container.GetInstance(service, key);
if (instance != null) return instance;
throw new Exception("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
protected override void PrepareViewFirst(Frame rootFrame)
{
_container.RegisterNavigationService(rootFrame);
}
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
DisplayRootView<MainView>();
}
}
Finally, create folders for Views and ViewModels in the solution add add to them the required items: MainViewModel, SettingsViewModel, MainView, SettingsView. The views just include a TextBlock with some text. MainViewModel derives from Screen, while SettingsViewModel derives from PropertyChangedBase. There is no relevant code in any of them.
When launching the app, I can see the main view; then I open the charms bar and click settings, and I find the label leading to my app settings; when I click it, I get the exception quoted above. Any hint?
You can find a full repro solution here: http://sdrv.ms/18GIMvB .
If you aren't ready to move to the alpha version of CM, you can update Callisto to 1.4.0 via NuGet. That fixed the error for me.
It seems that the new CM release (alpha 2) fixed the issue, so I'm adding some more information here to help other newcomers like me. Here is what I'm doing now:
In app's Configure I have some bootstrap code like:
...
ResourceLoader loader = ResourceLoader.GetForViewIndependentUse("Resources");
ISettingsService settings = _container.RegisterSettingsService();
settings.RegisterFlyoutCommand<ContentSettingsViewModel>(loader.GetString("SettingsContent"));
The ContentSettingsViewModel is a viewmodel for filtering some contents. The string got from resources is the label which will appear in the settings flyout (be sure there is an entry for this string, as passing an empty or null string triggers an exception). This VM is derived from CM Screen as I'm overriding OnActivate and OnDeactivate to load and save settings when the user opens or dismisses the settings page.
I am trying to implement MVVM pattern for my Windows Phone application. I have my Views in one project and App.xaml in another project. I added property IsTrial to App.xaml but I can't reach it from View by this code:
if ((Application.Current as App).IsTrial)
Because I didn't reference first project with App class but I can't do that because that would cause a circular dependency. What can I do? How can I access App class? Thanks
Create an interface in your Views project:
public interface ISupportTrial
{
bool IsTrial { get; }
}
Implement the interface in App.xaml.cs:
public class App: Application, ISupportTrial
{
...
}
Change the code where you access the app:
var trialApp = Application.Current as ISupportTrial;
if (trialApp == null)
{
throw new NotSupportedException("Application.Current should implement ISupportTrial");
}
else if (trialApp.IsTrial)
{
...
}
else
{
...
}
NOTE: Although this will probably work, I don't think that it's a good practice to access Application.Current. You might want to read some articles about Inversion of Control and Dependency injection.