I am making an app using .NET MAUI and I am trying to implement custom handlers for specific instances of controls (ex. some entries should use a custom handler I created). To achieve this I followed the official MS docs for this. The following is the setup they tell me to use:
1.First make a subclass of the Entry control:
using Microsoft.Maui.Controls;
namespace MyMauiApp
{
public class MyEntry : Entry
{
}
}
2.I then customize the EntryHandler to perform the desired modification to MyEntry instances:
using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
namespace MauiApp1
{
public partial class App : Application
{
public App()
{
InitializeComponent();
Microsoft.Maui.Handlers.EntryHandler.EntryMapper[nameof(IView.Background)] = (handler, view) =>
{
if (view is MyEntry)
{
#if __ANDROID__
handler.NativeView.SetBackgroundColor(Colors.Red.ToNative());
#elif __IOS__
handler.NativeView.BackgroundColor = Colors.Red.ToNative();
handler.NativeView.BorderStyle = UIKit.UITextBorderStyle.Line;
#elif WINDOWS
handler.NativeView.Background = Colors.Red.ToNative();
#endif
}
};
}
}
}
PROBLEM: This gives me the following error:
Severity Code Description Project File Line Suppression State
Error CS0021 Cannot apply indexing with [] to an expression of type
'IPropertyMapper<IEntry, EntryHandler>' MyMauiApp (net6.0-android),
MyMauiApp (net6.0-ios), MyMauiApp
(net6.0-windows10.0.19041) C:\Users\xxxxxx\source\repos\MyMauiApp\MyMauiApp\App.xaml.cs 24 Active
As I said I followed the docs completely but still this error. I have read that other people have this issue too. Can anyone help?
It seems some breaking changes have been made in this area by the means of this pr here and here.
From what it looks like this has been done so that you can cascade customizations in mappers with AppendToMapping and PrependToMapping or modify the whole mapping altogether with ModifyMapping.
Without explaining all the variations here, let's focus on your situation. This means that instead of this line Microsoft.Maui.Handlers.EntryHandler.EntryMapper[nameof(IView.Background)] = (handler, view) =>
You should now declare this as: Microsoft.Maui.Handlers.EntryHandler.EntryMapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
Note that you should now add a ) on the closing bracket too, making the full code:
Microsoft.Maui.Handlers.EntryHandler.EntryMapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
{
if (view is MyEntry)
{
#if __ANDROID__
handler.NativeView.SetBackgroundColor(Colors.Red.ToNative());
#elif __IOS__
handler.NativeView.BackgroundColor = Colors.Red.ToNative();
handler.NativeView.BorderStyle = UIKit.UITextBorderStyle.Line;
#elif WINDOWS
handler.NativeView.Background = Colors.Red.ToNative();
#endif
}
});
I will see if I can update the Docs here and there and hopefully this won't break again ;)
Edit: Updated the wiki page for this
Related
Is it possible to "gray out" the code if it doesn't exist like we do in Platform Dependent compilation (ex: #If UNITY_EDITOR)?
For example I don't want the compiler to complain if the code doesn't exist in the project. Specifically, I want to "hide" "GoogleMobileAds.Api;" package, which I don't have in the project, but it may be in the future.
Preprocessors are not an invention of Unity but is a c# thing.
If there are no specific defines listed in the Platform dependent compilation first make sure that your packages don't bring their own custom defines. Photon PUN e.g. actually does bring own defines like PHOTON_UNITY_NETWORKING, PUN_2_OR_NEWER, etc. But that's totally up to the providers of such libraries.
You can see/check this in the Edit -> Project Settings -> Player Settings -> Other Settings -> Scripting Define Symbols
Scripting Define Symbols
Set custom compilation flags. For more details, see the documentation on Platform dependent compilation.
Then you can just invent your own ones and add them to your code like e.g. USE_GOOGLE, USE_FIREBASE, etc
#if USE_GOOGLE
// some Google API related stuff
#endif
and later once you actually have according package in your project add these defines to the Scripting Define Symbols mentioned above.
If you know the full qualified assembly name of one type contained in the optional package you are looking for you can use e.g.
[assembly: OptionalDependency("Namespace.SubNameSpace.TypeName", "YOUR_DEFINE_SYMBOL_HERE")]
which is well hidden in Unity.XRTools.Utils!
This will internally on compile time check if the type Namespace.SubNameSpace.TypeName exists and if so define YOUR_DEFINE_SYMBOL_HERE so you can again in your code wrap the optional stuff in
#if YOUR_DEFINE_SYMBOL_HERE
...
#else
...
#endif
Alternative
For packages you own/implemen yourself there is a way to do this kind of automatically I used in the past. The following script as soon as it exists in a project it automatically adds the given define to the PlayerSettings if it doesn't exists yet
#if !USE_GOOGLE
public static class UseGoogleDefineSetter
{
const string DEFINE = "USE_GOOGLE";
[InitializeOnLoadMethod]
private void Init()
{
// Get current defines
var currentDefinesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
// Split at ;
var defines = currentDefinesString.Split(';').ToList();
// check if defines already exist given define
if (!defines.Contains(DEFINE))
{
// if not add it at the end with a leading ; separator
currentDefinesString += $";{DEFINE}";
// write the new defines back to the PlayerSettings
// This will cause a recompilation of your scripts
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, currentDefinesString);
}
}
}
#endif
Update Unity 2020+
The method was semi replaced by a better one operating on a list/array instead of an entire string which is expensive. And in the newest version also the build pipeline slightly changed.
So in newer Unity versions I would rather use something like e.g. (assuming 2020 is the oldest version you want to support/use)
#if !USE_GOOGLE
public static class UseGoogleDefineSetter
{
const string DEFINE = "USE_GOOGLE";
[InitializeOnLoadMethod]
private void Init()
{
EditorUtils.AddScriptingSymbol(DEFINE);
}
}
#endif
and to make it easy and general
public static class EditorUtils
{
#if UNITY_2021_2_OR_NEWER
private static NamedBuildTarget GetActiveNamedBuildTarget()
{
var buildTargetGroup = GetActiveBuildTargetGroup();
var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup);
return namedBuildTarget;
}
#endif
private static BuildTargetGroup GetActiveBuildTargetGroup()
{
var buildTarget = EditorUserBuildSettings.activeBuildTarget;
var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);
return buildTargetGroup;
}
public static void AddScriptingSymbol(string define)
{
#if UNITY_2021_2_OR_NEWER
var namedBuildTarget = GetActiveNamedBuildTarget();
PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget, out var defines);
#else
var buildTargetGroup = GetActiveBuildTargetGroup();
PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup, out var defines);
#endif
var defineList = defines.ToList();
if (!defineList.Contains(define))
{
defineList.Add(define);
}
#if UNITY_2021_2_OR_NEWER
PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, defineList.ToArray());
#else
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, defineList.ToArray());
#endif
}
}
Both scripts go of course either in a folder called Editor and/or in an assembly only compiled for the Unity Editor or need to be wrapped additionally in #if UNITY_EDITOR
I want to make a USSD call in a xamarin crossplatform app using C# and i have no idea where to start. All the examples i have seen is done in java. Is it possible to successfully dial a USSD code like *270# within my app without opening the dialer? If yes, please how? I'll be very grateful for any help
To do it with Xamarin forms you have to create a custom renderer for android :
Create an interface in your shared project :
public interface IUssdRenderer
{
void StartTransaction();
}
then in your android project :
using System;
using Android.Content;
using Android.OS;
using ussd.Renderers;
[assembly: Xamarin.Forms.Dependency(typeof(IUssdRenderer))]
namespace ussd.Droid.Renderers
{
public class UssdRenderer : IUssdRenderer
{
public Android.Net.Uri createUriFromString(string ussd)
{
String uri = "tel:";
foreach (char c in ussd.ToCharArray())
{
if (c == '#')
{
uri += Android.Net.Uri.Encode("#");
}
else
{
uri += c;
}
}
return Android.Net.Uri.Parse(uri);
}
public void StartTransaction()
{
var intent = new Intent(Intent.ActionCall, createUriFromString("*270#"));
Context ctx = Xamarin.Forms.Forms.Context;
ctx.StartActivity(intent);
}
}
}
You'll also have to register you interface, I'm using prism :
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register< IUssdRenderer, UssdRenderer>();
}
The last bit is to make sure you have Call permissions enabled :
Right click on Android Project > Options > Android application
make sure CallPhone is selected
You may use Xamarin.Essentials PhoneDialer, to make a call, note that since USSD contains '#' you need to URL-encode it. example:
string code = "*123#";
PhoneDialer.Open(HttpUtility.UrlEncode(code));
After collecting from left and right I finally found the solution to my question with CrossMessaging plugin.
The steps are as follow:
Create your project and give the name
Install from nuget Xam.Plugins.Messaging in your project
Add below line in Android project MainActivity's OnCreate method:
CrossMessaging.Current.Settings().Phone.AutoDial = true;
Add android.permission.CALL_PHONE to the manifest file.
Make calls as follow
try
{
var phonedialer = CrossMessaging.Current.PhoneDialer;
if (phonedialer.CanMakePhoneCall)
{
//Ussd call's
phonedialer.MakePhoneCall(HttpUtility.UrlEncode("#150#"));
//For normal calls
phonedialer.MakePhoneCall("9111111111")
}
}
catch (Exception exc)
{
await DisplayAlert("Error!!!!", exc.ToString(), "ok");
}
I am trying to load a html file that is in the same path as class i'm working with Via a web view in xamarin forms when I run the app I get a white screen and nothing loads here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace App.Plan
{
public partial class Tornado : ContentPage
{
public Tornado()
{
InitializeComponent();
var browser = new WebView
{
Source = "local.html"
};
}
}
}
I realize this is old, but the documentation is maybe not entirely clear when it comes to local files so I thought I'd share my thoughts. Hopefully it's helpful to anyone who stumbles here.
The documentation states:
To display local content using a WebView, you'll need to open the HTML file like any other, then load the contents as a string into the Html property of an HtmlWebViewSource.
The key thing to note is that the WebView's Source property is ONLY for external URLs or HtmlWebViewSource. You cannot put a local URL in the Source property. The phrasing "you'll need to open the HTML file like any other" means (as it states shortly after) that you need to load the contents of the file from disk into a variable using a full file path, not URL.
The question then becomes "What about links to other local files?" That's where the BaseUrl property of HtmlWebViewSource comes into play. The documentation addresses this by stating the following:
Although the first page has been loaded, the WebView has no knowledge of where the HTML came from. That is a problem when dealing with pages that reference local resources. Examples of when that might happen include when local pages link to each other, a page makes use of a separate JavaScript file, or a page links to a CSS stylesheet.
In other words, any links to local resources in your HTML will be prepended with the BaseUrl by the Webview automatically.
To sum things up
Create the IBaseUrl Interface in your shared project
public interface IBaseUrl {
string Get();
}
Create the platform-specific implementations of IBaseUrl in each platform project
iOS:
[assembly: Dependency (typeof (BaseUrl_iOS))]
namespace WorkingWithWebview.iOS
{
public class BaseUrl_iOS : IBaseUrl
{
public string Get()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
Android
[assembly: Dependency (typeof(BaseUrl_Android))]
namespace WorkingWithWebview.Android
{
public class BaseUrl_Android : IBaseUrl
{
public string Get()
{
return "file:///android_asset/";
}
}
}
UWP
[assembly: Dependency(typeof(BaseUrl))]
namespace WorkingWithWebview.UWP
{
public class BaseUrl : IBaseUrl
{
public string Get()
{
return "ms-appx-web:///";
}
}
}
Make sure your HTML files are in the appropriate folders and have the proper Build Action
iOS: Resources, build action: "BundleResource"
Android: Assets, build action: "AndroidAsset"
UWP: project root, build action: "content"
Make sure the WebView has a height and width request or it may not render:
It may be necessary to set the WidthRequest and HeightRequest properties of the WebView to see the HTML content, depending upon the layout the WebView is a child of. For example, this is required in a StackLayout.
Once that setup is done, you can put it into action in your shared project. Here's a simplified example:
// Use DI function to get the BaseUrl for the platform
var baseUrl = DependencyService.Get<IBaseUrl>().Get();
// You could append subfolders here if you don't
// want all the HTML files mixed with other resources:
// var baseUrl = System.IO.Path.Combine(DependencyService.Get<IBaseUrl>().Get(), "subfolder");
// Define the location of your initial HTML page using the base url
var initialHtmlPath = System.IO.Path.Combine(baseUrl, "index.html");
// Create the viewsource, loading the first HTML file as a string
var localHtmlViewSource = new HtmlWebViewSource();
localHtmlViewSource.BaseUrl = baseUrl;
localHtmlViewSource.Html = System.IO.File.ReadAllText(initialHtmlPath);
// Set the webview to use the local source
HelpWebView.Source = localHtmlViewSource;
Xamarin has docs in relation to this:
https://developer.xamarin.com/guides/xamarin-forms/user-interface/webview/
var browser = new WebView();
var htmlSource = new HtmlWebViewSource();
htmlSource.Html = #"<html><body>
<h1>Xamarin.Forms</h1>
<p>Welcome to WebView.</p>
</body></html>";
browser.Source = htmlSource;
Here is official sample in github
WorkingWithWebview
tabs.Children.Add (new LocalHtml {Title = "Local" });
tabs.Children.Add (new LocalHtmlBaseUrl {Title = "BaseUrl" });
tabs.Children.Add (new WebPage { Title = "Web Page"});
tabs.Children.Add (new WebAppPage {Title ="External"});
Second tab may help.
I have an application that uses Unity. In one area of the application, the code scans the assembly to register the types (auto-registration) using this code:
public class CommonRegistry : UnityRegistry
{
public CommonRegistry()
{
const StringComparison strCmp = StringComparison.InvariantCultureIgnoreCase;
Register<ITenantStore, TenantStore>();
Scan(scanner =>
{
scanner.AssembliesInBaseDirectory(asm => asm.GetName().Name.StartsWith(#"Infrastructure", strCmp));
scanner.ForRegistries();
});
}
}
The above code uses the UnityRegistry class of the UnityConfiguration assembly.
But in the Unity documentation, the example shows this code:
unityContainer.RegisterTypes(AllClasses.FromLoadedAssemblies(), ...
What is the difference between the two?
I think its same with here.
register dependency Unity
the difference is the UnityRegistry is for lower versions.
while the other one is for 3.5 up.
but both are to register dependencies.
I am working on adding Nancy Framework to my C# console application (followed the very short tutorial here and it loads a blank page when I go to http://localhost:1234 so I know it is starting, but it doesn't show my text properly. I have gone over my code various times, but don't see any issues.
I have added both Nancy and Nancy.Hosting.Self to my project.
static void Main(string[] args)
{
var nancyHost = new Nancy.Hosting.Self.NancyHost(new Uri("http://localhost:1234"));
nancyHost.Start();
Console.ReadLine();
nancyHost.Stop();
}
namespace DaemonApp
{
class MainModule : Nancy.NancyModule
{
public MainModule()
{
Get["/"] = parameters =>
{
return "Hello world!";
};
}
}
}
I added some print lines, and it never calls the module when I visit the page. Does anyone have any clue what the issue is?
I didn't make the Module class public, that fixed it instantly =/