Как использовать задачу C# в приложении F # Xamarin.Forms? - PullRequest
4 голосов
/ 01 мая 2020

Я хотел изучить F #, поэтому решил создать приложение Xamarin.Forms с ним, но я довольно незнаком с асинхронным и функциональным программированием на F #. Я использую библиотеку Xam.Plugins.Media C#, чтобы попытаться сделать фотографию, но CrossMedia.Current.TakePhotoAsync() ведет себя не так, как если бы я использовал ее в C#.

Вот что у меня есть в F #:

    type MainPage() =
        inherit ContentPage()
        let _ = base.LoadFromXaml(typeof<MainPage>)

        let viewModel = base.BindingContext :?> MainViewModel
        let checkNull (x : 'T) =
            match x with 
            | null -> raise (NullReferenceException(String.Format("{0} is null", x)))
            | _ -> x

        member this.TakePhoto(sender : obj, e : EventArgs) =
            if not CrossMedia.Current.IsCameraAvailable || not CrossMedia.Current.IsTakePhotoSupported then
                raise (Exception("Camera not supported"))
            else 
                let mediaOptions = 
                    Plugin.Media.Abstractions.StoreCameraMediaOptions(
                        Directory = "Test",
                        SaveToAlbum = true,
                        CompressionQuality = 75,
                        CustomPhotoSize = 50,
                        PhotoSize = PhotoSize.MaxWidthHeight,
                        MaxWidthHeight = Nullable(2000),
                        DefaultCamera = CameraDevice.Front)
                let file = CrossMedia.Current.TakePhotoAsync(mediaOptions) |> Async.AwaitTask |> Async.RunSynchronously
                viewModel.Stream <- (checkNull file).GetStream()

Вот эквивалент C#:

takePhoto.Clicked += async (sender, args) =>
{
     if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
        return;

     var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
     {
        Directory = "Test",
        SaveToAlbum = true,
        CompressionQuality = 75,
        CustomPhotoSize = 50,
        PhotoSize = PhotoSize.MaxWidthHeight,
        MaxWidthHeight = 2000,
        DefaultCamera = CameraDevice.Front
     });

     if (file == null)
        return;

     viewModel.Stream = file.GetStream();
};

Я знаю, что я не использую F # в полностью функциональном аспект, но что мне не хватает, чтобы заставить его работать или есть какое-то ограничение, о котором я не знаю, когда использую задачу C# в F #?

Я пытался использовать выражение async {}, которое ничего не делало на кнопке щелкните, но я предполагаю, что это помещает TakePhotoAsync в фоновый поток, когда он, вероятно, должен быть запущен в основном потоке. То, что мне сейчас показалось, работает на полпути: показывать диалоговое окно с разрешениями и, в конечном итоге, показывать черный экран, как будто камера запускается, но он застревает там и фактически не показывает подачу камеры.

1 Ответ

0 голосов
/ 04 мая 2020

Я подозреваю, что проблема в том, что вы используете RunSynchronously, чтобы сделать фотографию, которая, вероятно, не делает того, чего вы ожидаете. Вы можете думать, что RunSynchronously похоже на вызов .Result для объекта задачи. Net. Таким образом, можно предположить, что это приводит к тупику где-то в стандартной библиотеке Xamarin.

Вместо этого, если вы хотите, чтобы ваш код F # был более похож на ваш код C#, то вам понадобится что-то вроде следующего:

member this.TakePhoto(sender: obj, e : EventArgs) =
    async {
       if not CrossMedia.Current.IsCameraAvailable ... then return ()
       else
           let! file = CrossMedia.Current.TakePhotoAsync(Plugin.Media.Abstractions.StoreCameraMediaOptions(
               Directory = "Test",
               SaveToAlbum = true,
               CompressionQuality = 75,
               CustomPhotoSize = 50,
               PhotoSize = PhotoSize.MaxWidthHeight,
               MaxWidthHeight = 2000,
               DefaultCamera = CameraDevice.Front)) |> Async.AwaitTask

           if isNull file then return ()
           else
               viewModel.Stream <- file.GetStream()
    } |> Async.StartAsTask

Блок asyn c можно также заменить выражением вычисления задачи, если вы хотите получить зависимость от внешней библиотеки

...