Folder Picker .NET MAUI - c#

I would to do a downloader app that save pictures to a folder. The app should work on windows and macos, and may be later on android and ios.
I haven't found a way to pick the target folder. Any idea on how it can be achieve either with blazor or xaml .NET MAUI app?

I've made a start implementing this for Windows and macOS. You can review the code here: https://github.com/jfversluis/MauiFolderPickerSample and wrote a little blog post about this as well here: https://blog.verslu.is/maui/folder-picker-with-dotnet-maui/
This follows kind of the basic pattern you'd want to use if you want to access platform-specific APIs:
Define an interface
Implement interface on each supported platform
Consume functionality
For this I have created a very simple but effective interface
public interface IFolderPicker
{
Task<string> PickFolder();
}
Then we create an implementation for Windows, by adding a new file FilePicker.cs to the Platforms\Windows\ folder. This makes it specific to Windows and allows us to write Windows specific code. The file contains this code:
using WindowsFolderPicker = Windows.Storage.Pickers.FolderPicker;
namespace MauiFolderPickerSample.Platforms.Windows
{
public class FolderPicker : IFolderPicker
{
public async Task<string> PickFolder()
{
var folderPicker = new WindowsFolderPicker();
// Make it work for Windows 10
folderPicker.FileTypeFilter.Add("*");
// Get the current window's HWND by passing in the Window object
var hwnd = ((MauiWinUIWindow)App.Current.Windows[0].Handler.PlatformView).WindowHandle;
// Associate the HWND with the file picker
WinRT.Interop.InitializeWithWindow.Initialize(folderPicker, hwnd);
var result = await folderPicker.PickSingleFolderAsync();
return result.Path;
}
}
}
Because I chose FolderPicker as the name for my own object here, there is a naming conflict with the Windows FolderPicker that is why there is that weird using at the top. If you go for MyFolderPicker as your object name that wouldn't be needed.
Now we register this interface and implementation with the generic host builder in our MauiProgram.cs:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
// Note: this part was added
#if WINDOWS
builder.Services.AddTransient<IFolderPicker, Platforms.Windows.FolderPicker>();
#elif MACCATALYST
builder.Services.AddTransient<IFolderPicker, Platforms.MacCatalyst.FolderPicker>();
#endif
builder.Services.AddTransient<MainPage>();
builder.Services.AddTransient<App>();
// Note: end added part
return builder.Build();
}
}
Note that I also added MainPage and App here so that our constructor injection works (have a look at MainPage.xaml.cs in the linked repository).
Now we can consume our functionality as follows:
namespace MauiFolderPickerSample;
public partial class MainPage : ContentPage
{
private readonly IFolderPicker _folderPicker;
public MainPage(IFolderPicker folderPicker)
{
InitializeComponent();
_folderPicker = folderPicker;
}
private async void OnPickFolderClicked(object sender, EventArgs e)
{
var pickedFolder = await _folderPicker.PickFolder();
FolderLabel.Text = pickedFolder;
SemanticScreenReader.Announce(FolderLabel.Text);
}
}
Implementing other platforms would require you to implement the interface for the platform you want to support and register it in the generic host builder. This should get you started for Windows and macOS.
Actually calling this should not be any different between .NET MAUI (regular) or .NET MAUI Blazor.

Related

Xamarin.GooglePlayServices.Ads: how to add a bundle to the ad request

Considering I have an AdView in a Xamarin.Android project:
private AdView _bannerAd;
I usually initialize it like this:
_bannerAd = new AdView(this)
{
AdSize = AdSize.SmartBanner,
AdUnitId = adUnitId,
Visibility = ViewStates.Visible
};
Then, when I load the banner, I have to build the request. In this case I'd like to add an extra bundle, but when I have to call requestbuilder.AddCustomEventExtrasBundle, I don't know what to put as the first parameter.
var requestbuilder = new AdRequest.Builder();
var extras = new Bundle();
extras.PutString("npa", "1");
requestbuilder.AddCustomEventExtrasBundle(***, extras);
_bannerAd.LoadAd(requestbuilder.Build())
By reading the method definition, I really don't understand what could be an "adapter class".
[Register("addCustomEventExtrasBundle", "(Ljava/lang/Class;Landroid/os/Bundle;)Lcom/google/android/gms/ads/AdRequest$Builder;", "")]
public Builder AddCustomEventExtrasBundle(Class adapterClass, Bundle customEventExtras);
You need to pass the Java Class (via Java.Lang.Class.FromType) of your custom event (ICustomEventBanner).
In my case, I have one called SushiHangoverTextEventBanner that is registered with AdMob.
You need to implement ICustomEventBanner, assumably this is a stand alone object (in my case it is) as AdMob will instance it, inherit it from Java.Lang.Object so Xamarin will create the ACW (JNI/Java wrapper) for it.
public class SushiHangoverTextEventBanner : Java.Lang.Object, ICustomEventBanner
{
SushiHangoverTextAdView customAdView;
public void OnDestroy()
{
customAdView?.Dispose();
}
public void OnPause()
{
~~~
}
public void OnResume()
{
~~~
}
public void RequestBannerAd(Context context, ICustomEventBannerListener listener, string serverParameter, AdSize size, IMediationAdRequest mediationAdRequest, Bundle customEventExtras)
{
customAdView = new SushiHangoverTextAdView(context);
~~~
}
}
I also have a custom ad (SushiHangoverAdView based on a TextView) that is used within that ICustomEventBanner implementation.
Once you register it and implement the AdMob callbacks, you can just pass it to your AdRequest.Builder:
using (var requestbuilder = new AdRequest.Builder())
{
var extras = new Bundle();
extras.PutString("npa", "1");
requestbuilder.AddCustomEventExtrasBundle(Java.Lang.Class.FromType(typeof(SushiHangoverTextEventBanner)), extras);
}
I help recommend going through the Admob custom event example:
https://developers.google.com/admob/android/custom-events
It is expecting a class that extends from CustomEvent, per the Documentation
public AdRequest.Builder addCustomEventExtrasBundle (Class<? extends
CustomEvent> adapterClass, Bundle customEventExtras)
Here is a great tutorial on getting started with custom events, directly from Google, where they go over using the CustomEventBanner. It is in Java, but should be easy enough to port to C#

How is Setup class instantiated in MVVMCross in Xamarin?

I'm starting learning MVVM cross, In the android app, I have a splash screen class:
[Activity(MainLauncher = true,
Label = "#string/app_name",
Theme = "#style/Theme.Splash",
NoHistory = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
ScreenOrientation = ScreenOrientation.Portrait)]
public class SplashScreen : MvxSplashScreenActivity
{
public SplashScreen() : base(Resource.Layout.SplashScreen)
{
}
}
and this is the Setup class:
public class Setup : MvxAndroidSetup
{
protected Setup(Context applicationContext) : base(applicationContext)
{
}
protected override IMvxApplication CreateApp()
{
return null;
}
}
the problem is that the debugger doesn't hit the constructor of the Setup Class, instead I get "An unhandled exception" after the constructor of the splash screen
EDIT
I've already defined the App class in the PCL project:
public class App : MvxApplication
{
public override void Initialize()
{
base.Initialize();
}
also defined the AppStart:
public class AppStart : MvxNavigatingObject, IMvxAppStart
{
public async void Start(object hint = null)
{
//hardcoded login for this demo
//var userService = Mvx.Resolve<IUserDataService>();
//await userService.Login("gillcleeren", "123456");
ShowViewModel<MainViewModel>();
}
}
The main reason behind this project is to understand the sequence of code required and executed by MVVM Cross, so I provide the minimum code till it runs successfully without runtime errors.
Update
I have read your code again more thoroughly and I can see the issue now. You defined the constructor of the Setup class as protected, which makes it invisible for activation.
On MvvmCross for Android the magic happens inside MvxAndroidSetupSingleton class (see the source code here) which searches for the Setup type you defined. The FindSetupType method looks for your defined Setup class first and then inside the CreateSetup method Activator.CreateInstance is used to build the Setup instance. The CreateInstance method variant used however searches only for public constructors, which means it doesn't find your protected one. The result is that it cannot build the Setup class and crashes.
Original answer
The reason this happens is that you have no Core libary that would define the MvvmCross App class and would initialize other required setup. I suggest you to start with a simple tutorial or to look into the official sample projects to see what is necessary to make MvvmCross work in a Xamarin.Android app.

Connect to SignalR hub in Unity3d (For Hololens)

Is it possible to connect to a SignalR server using Unity?
I'm making a Hololens application(so I build to windows store app) that need to communicate with a SignalR hub to post information. This server is already made and I have only the URI, hub name and the hub interface endpoints.
Current issues:
SignalR framework requires .net 4.5 which Unity's current mono version doesn't work with. Even if I manage to get the reference in, Unity won't be able to build it to a windows store app as Unity reverts the solutions version to 3.5 unity subset which doesn't include async functionality that is introduced in .net 4.0.
https://github.com/robink-teleopti/SignalR simply exploded with errors in unity.
https://github.com/jenyayel/SignalR.Client.20 lacks a bunch of definitions in Thread, Stream, LongLength, HTTPWebRequest.
Nivot.SignalR.Client.Net35 uses async functionality that Unity doesn't understand.
You can connect from the UW application to connect to signalR and use a bridge to communicate with the Unity application:
(App/Web-server) bridge:
internal class UnityBridge
{
private static UnityBridge _instance;
private UnityBridge()
{
SelectHandler.SelectedHandler = CallExternal;
}
public static UnityBridge Create()
{
return _instance ?? (_instance = new UnityBridge());
}
private void CallExternal(string nameTag)
{
var conn = new HubConnection("http://xxx.azurewebsites.net");
var proxy = conn.CreateHubProxy("MyHub");
conn.Start().Wait();
proxy.Invoke("Send", new EngineerAction {ExecutedAction = nameTag});
}
}
(App/Web-server) In your App.cs
private AppCallbacks m_AppCallbacks;
private UnityBridge _unityBridge;
public App()
{
m_AppCallbacks = new AppCallbacks();
// Allow clients of this class to append their own callbacks.
AddAppCallbacks(m_AppCallbacks);
}
virtual protected void AddAppCallbacks(AppCallbacks appCallbacks)
{
_unityBridge = UnityBridge.Create();
}
(Unity game-client/server) And in your unity scripts:
public class SelectHandler : MonoBehaviour
{
public delegate void SelectedHandlerDelegate(string nameTag);
public static SelectedHandlerDelegate SelectedHandler;
....
//call
UnityEngine.WSA.Application.InvokeOnUIThread(() => SelectedHandler("teststring"), false);

Example of using Shared Services. Prism

I have 5 modules and I am using EventAggregator pattern to communicate between modules. And it seems to me that my code becomes ugly and it is bad design to use EventAggregator in my project.
There are three ways to communicate between modules:
Loosely coupled events
Shared services
Shared resources
I would like to know more about communication by Shared Services. What I've found is an article about StockTrader application from Prism ToolKit.
Is there some more lightweight and clearer example of using Shared Services in Prism where it is possible to see talking between modules using Shared Services? (downloadable code would be highly appreciated)
In which way is your code getting ugly? The EventAggregator is a shared service, if you like.
You put a service interface in a shared assembly, and then one module can, say, push data into the service while another module get's the data from the service.
Edit:
Shared assembly
public interface IMySharedService
{
void AddData( object newData );
object GetData();
event System.Action<object> DataArrived;
}
First communicating module
// this class has to be resolved from the unity container, perhaps via AutoWireViewModel
internal class SomeClass
{
public SomeClass( IMySharedService sharedService )
{
_sharedService = sharedService;
}
public void PerformImport( IEnumerable data )
{
foreach (var item in data)
_sharedService.AddData( item );
}
private readonly IMySharedService _sharedService;
}
Second communicating module
// this class has to be resolved from the same unity container as SomeClass (see above)
internal class SomeOtherClass
{
public SomeOtherClass( IMySharedService sharedService )
{
_sharedService = sharedService;
_sharedService.DataArrived += OnNewData;
}
public void ProcessData()
{
var item = _sharedService.GetData();
if (item == null)
return;
// Do something with the item...
}
private readonly IMySharedService _sharedService;
private void OnNewData( object item )
{
// Do something with the item...
}
}
Some other module's initialization
// this provides the instance of the shared service that will be injected in SomeClass and SomeOtherClass
_unityContainer.RegisterType<IMySharedService,MySharedServiceImplementation>( new ContainerControlledLifetimeManager() );
The Prism Library repo on GitHub has an up to date version of the Stock Trader sample application, which includes service examples and source code for you to look at, and download.
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/StockTraderRI

Write device platform specific code in Xamarin.Forms

I have the following Xamarin.Forms.ContentPage class structure
public class MyPage : ContentPage
{
public MyPage()
{
//do work to initialize MyPage
}
public void LogIn(object sender, EventArgs eventArgs)
{
bool isAuthenticated = false;
string accessToken = string.Empty;
//do work to use authentication API to validate users
if(isAuthenticated)
{
//I would to write device specific code to write to the access token to the device
//Example of saving the access token to iOS device
NSUserDefaults.StandardUserDefaults.SetString(accessToken, "AccessToken");
//Example of saving the access token to Android device
var prefs = Application.Context.GetSharedPreferences("MySharedPrefs", FileCreationMode.Private);
var prefsEditor = prefs.Edit();
prefEditor.PutString("AccessToken", accessToken);
prefEditor.Commit();
}
}
}
I would like to write platform specific code in the MyPage LogIn method to save the access token based on which device OS they are using my application on.
How do I only run device specific code when the user uses my application on their device?
This is a scenario which is easily resolved with dependency injection.
Have a interface with the desired methods on your shared or PCL code, like:
public interface IUserPreferences
{
void SetString(string key, string value);
string GetString(string key);
}
Have a property on your App class of that interface:
public class App
{
public static IUserPreferences UserPreferences { get; private set; }
public static void Init(IUserPreferences userPreferencesImpl)
{
App.UserPreferences = userPreferencesImpl;
}
(...)
}
Create platform-specific implementations on your target projects:
iOS:
public class iOSUserPreferences : IUserPreferences
{
public void SetString(string key, string value)
{
NSUserDefaults.StandardUserDefaults.SetString(value, key);
}
public string GetString(string key)
{
return NSUserDefaults.StandardUserDefaults.StringForKey(key);
}
}
Android:
public class AndroidUserPreferences : IUserPreferences
{
public void SetString(string key, string value)
{
var prefs = Application.Context.GetSharedPreferences("MySharedPrefs", FileCreationMode.Private);
var prefsEditor = prefs.Edit();
prefEditor.PutString(key, value);
prefEditor.Commit();
}
public string GetString(string key)
{
(...)
}
}
Then on each platform-specific project create an implementation of IUserPreferences and set it using either App.Init(new iOSUserPrefernces()) and App.Init(new AndroidUserPrefernces()) methods.
Finally, you could change your code to:
public class MyPage : ContentPage
{
public MyPage()
{
//do work to initialize MyPage
}
public void LogIn(object sender, EventArgs eventArgs)
{
bool isAuthenticated = false;
string accessToken = string.Empty;
//do work to use authentication API to validate users
if(isAuthenticated)
{
App.UserPreferences.SetString("AccessToken", accessToken);
}
}
}
Xamarin.Forms 2.3.4 introduced a new method for this:
if (Device.RuntimePlatform == Device.Android)
{
// Android specific code
}
else if (Device.RuntimePlatform == Device.iOS)
{
// iOS specific code
}
else if (Device.RuntimePlatform == Device.UWP)
{
// UWP specific code
}
There are also other platforms to choose from, you can type in Device. in Visual Studio and it will show you the options.
There are multiple answers, depending on what you want to achieve, and the kind of project you have:
Execute different Xamarin.Forms code on different platforms.
Use this e.g. if you want different font sizes on different platforms:
label.Font = Device.OnPlatform<int> (12, 14, 14);
Execute platform specific code in a shared (PCL) project
The common pattern is to use DI (dependency injection) for this. Xamarin.Forms provides a simple DependencyService for this, but use whatever you want.
Execute platform specific code in shared (Shared Asset Project) project
As the code is compiled per platform, you can wrap your platform specific code in #if __PLATFORM__ #endif and have all the code in the same file. The platform project should define __IOS__, __ANDROID__ and __WINDOWS_PHONE__. Note that a shared asset project containing Xaml and code won't work well for iOS on Xamarin.Studio, and that having compiler directives makes your code harder to read and to test.
Xamarin.Forms has a built-in dependency injector if you take a look at their guide in the developer area of their website (http://developer.xamarin.com/guides/cross-platform/xamarin-forms/dependency-service/)
There's also a wonderful library you can pull from NuGet/Github (https://github.com/aritchie/acr-xamarin-forms) that will handle the storage requirement you are looking for... take a look at the Settings service in there, it will even handle serialization of more complex objects.
This seems less about Xamarin.Forms and more about using defaults in a PCL. Check out James Montemagno's github repo for doing cross-platform defaults.
Then just call his static methods for setting/retrieving. The nuget package is Xam.Plugins.Settings.
It can be used like this:
using Refractored.Xam.Settings;
...
CrossSettings.Current.AddOrUpdateValue("AccessToken", accessToken);
var value = CrossSettings.Current.GetValueOrDefault<string>("AccessToken");

Categories

Resources