add generator, make simplificiations
This commit is contained in:
Родитель
6f1110a198
Коммит
0bd291718b
|
@ -0,0 +1,8 @@
|
|||
obj
|
||||
bin
|
||||
*.bak
|
||||
.vs
|
||||
.fake
|
||||
packages
|
||||
paket-files
|
||||
*.dll
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2018 Elmish.XamarinForms contributors. See LICENSE.md for license.
|
||||
namespace Elmish.XamarinForms
|
||||
|
||||
open Elmish
|
||||
open FSharp.Control
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Cmd =
|
||||
|
||||
let ofAsyncCallback (task: 'a -> ('b -> unit) -> Async<_>)
|
||||
(arg: 'a)
|
||||
(callback: 'b -> 'msg)
|
||||
(ofSuccess: _ -> 'msg)
|
||||
(ofError: _ -> 'msg) : Cmd<'msg> =
|
||||
let bind dispatch =
|
||||
async {
|
||||
let cb = callback >> dispatch
|
||||
let! r = task arg cb |> Async.Catch
|
||||
dispatch (match r with
|
||||
| Choice1Of2 x -> ofSuccess x
|
||||
| Choice2Of2 x -> ofError x)
|
||||
}
|
||||
[bind >> Async.StartImmediate]
|
||||
|
||||
let dispatch d (cmd: Cmd<_>) = for sub in cmd do sub d
|
||||
|
||||
let ofAsyncSeq p : Cmd<_> =
|
||||
[ fun dispatch -> p |> AsyncSeq.iter dispatch |> Async.StartImmediate ]
|
||||
|
||||
type CmdBuilder() =
|
||||
inherit AsyncSeq.AsyncSeqBuilder()
|
||||
member x.Run(p: AsyncSeq<_>) = ofAsyncSeq p
|
||||
|
||||
[<AutoOpen>]
|
||||
module CommandBuidler =
|
||||
let cmd = Cmd.CmdBuilder()
|
||||
|
|
@ -1,635 +0,0 @@
|
|||
// Copyright 2018 Elmish.XamarinForms contributors. See LICENSE.md for license.
|
||||
namespace Elmish.XamarinForms
|
||||
|
||||
open Xamarin.Forms
|
||||
|
||||
open Elmish
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.DynamicViews
|
||||
|
||||
/// Represents a model when you don't have a model. A Clayton's model.
|
||||
type NoModel =
|
||||
| NoModel
|
||||
|
||||
type Update<'model, 'msg> = 'msg -> 'model -> 'model * Cmd<'msg>
|
||||
type Update<'model, 'msg, 'extmsg> = 'msg -> 'model -> 'model * Cmd<'msg> * 'extmsg
|
||||
type StaticView<'model, 'msg, 'page> = unit -> 'page * ViewBindings<'model, 'msg>
|
||||
type DynamicView<'model, 'msg, 'page> = unit -> 'page * ViewBindings<'model, 'msg> * ('model -> 'msg -> View)
|
||||
|
||||
[<AutoOpen>]
|
||||
module Values =
|
||||
let NoCmd<'a> : Cmd<'a> = Cmd.none
|
||||
let mutable currentPage : Page = null
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Nav =
|
||||
|
||||
// TODO: modify the Elmish framework we use to remove this global state and pass it into all commands??
|
||||
let mutable globalNavMap : Map<System.IComparable, Page> = Map.empty
|
||||
|
||||
/// Push new location into history and navigate there
|
||||
let push (fromPageTag: ('navTarget :> System.IComparable)) (toPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let toPage = globalNavMap.[toPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PushAsync toPage |> ignore ]
|
||||
|
||||
/// Push new location into history and navigate there
|
||||
let pushModal (fromPageTag: ('navTarget :> System.IComparable)) (toPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let toPage = globalNavMap.[toPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PushModalAsync toPage |> ignore ]
|
||||
|
||||
let popToRoot (fromPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PopToRootAsync() |> ignore ]
|
||||
|
||||
let popModal (fromPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PopModalAsync() |> ignore ]
|
||||
|
||||
let pop (fromPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PopAsync() |> ignore ]
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Init =
|
||||
let combo2 init1 init2 () =
|
||||
let model1 = init1()
|
||||
let model2 = init2()
|
||||
(model1, model2)
|
||||
|
||||
let combo3 init1 init2 init3 () =
|
||||
let model1 = init1()
|
||||
let model2 = init2()
|
||||
let model3 = init3()
|
||||
(model1, model2, model3)
|
||||
|
||||
let combo4 init1 init2 init3 init4 () =
|
||||
let model1 = init1()
|
||||
let model2 = init2()
|
||||
let model3 = init3()
|
||||
let model4 = init4()
|
||||
(model1, model2, model3, model4)
|
||||
|
||||
let combo5 init1 init2 init3 init4 init5 () =
|
||||
let model1 = init1()
|
||||
let model2 = init2()
|
||||
let model3 = init3()
|
||||
let model4 = init4()
|
||||
let model5 = init5()
|
||||
(model1, model2, model3, model4, model5)
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module InitCmd =
|
||||
let combo2 init1 init2 () =
|
||||
let model1, cmd1 = init1()
|
||||
let model2, cmd2 = init2()
|
||||
(model1, model2), Cmd.batch [cmd1; cmd2]
|
||||
|
||||
let combo3 init1 init2 init3 () =
|
||||
let model1, cmd1 = init1()
|
||||
let model2, cmd2 = init2()
|
||||
let model3, cmd3 = init3()
|
||||
(model1, model2, model3), Cmd.batch [cmd1; cmd2; cmd3]
|
||||
|
||||
let combo4 init1 init2 init3 init4 () =
|
||||
let model1, cmd1 = init1()
|
||||
let model2, cmd2 = init2()
|
||||
let model3, cmd3 = init3()
|
||||
let model4, cmd4 = init4()
|
||||
(model1, model2, model3, model4), Cmd.batch [cmd1; cmd2; cmd3; cmd4]
|
||||
|
||||
let combo5 init1 init2 init3 init4 init5 () =
|
||||
let model1, cmd1 = init1()
|
||||
let model2, cmd2 = init2()
|
||||
let model3, cmd3 = init3()
|
||||
let model4, cmd4 = init4()
|
||||
let model5, cmd5 = init5()
|
||||
(model1, model2, model3, model4, model5), Cmd.batch [cmd1; cmd2; cmd3; cmd4; cmd5]
|
||||
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Update =
|
||||
|
||||
let combo2 (update1: Update<_, _, _>) (update2: Update<_, _, _>) : Update<_,_,_> = fun msg (model1, model2) ->
|
||||
match msg with
|
||||
| Choice1Of2 msg1 -> let newModel, cmds, extmsg = update1 msg1 model1 in (newModel, model2), Cmd.map Choice1Of2 cmds, extmsg
|
||||
| Choice2Of2 msg2 -> let newModel, cmds, extmsg = update2 msg2 model2 in (model1, newModel), Cmd.map Choice2Of2 cmds, extmsg
|
||||
|
||||
let combo3 (update1: Update<_, _, _>) (update2: Update<_, _, _>) (update3: Update<_, _, _>) : Update<_,_,_> = fun msg (model1, model2, model3) ->
|
||||
match msg with
|
||||
| Choice1Of3 msg1 -> let newModel, cmds, extmsg = update1 msg1 model1 in (newModel, model2, model3), Cmd.map Choice1Of3 cmds, extmsg
|
||||
| Choice2Of3 msg2 -> let newModel, cmds, extmsg = update2 msg2 model2 in (model1, newModel, model3), Cmd.map Choice2Of3 cmds, extmsg
|
||||
| Choice3Of3 msg3 -> let newModel, cmds, extmsg = update3 msg3 model3 in (model1, model2, newModel), Cmd.map Choice3Of3 cmds, extmsg
|
||||
|
||||
let combo4 (update1: Update<_, _, _>) (update2: Update<_, _, _>) (update3: Update<_, _, _>) (update4: Update<_, _, _>) : Update<_,_,_> = fun msg (model1, model2, model3, model4) ->
|
||||
match msg with
|
||||
| Choice1Of4 msg1 -> let newModel, cmds, extmsg = update1 msg1 model1 in (newModel, model2, model3, model4), Cmd.map Choice1Of4 cmds, extmsg
|
||||
| Choice2Of4 msg2 -> let newModel, cmds, extmsg = update2 msg2 model2 in (model1, newModel, model3, model4), Cmd.map Choice2Of4 cmds, extmsg
|
||||
| Choice3Of4 msg3 -> let newModel, cmds, extmsg = update3 msg3 model3 in (model1, model2, newModel, model4), Cmd.map Choice3Of4 cmds, extmsg
|
||||
| Choice4Of4 msg4 -> let newModel, cmds, extmsg = update4 msg4 model4 in (model1, model2, model3, newModel), Cmd.map Choice4Of4 cmds, extmsg
|
||||
|
||||
let combo5 (update1: Update<_, _, _>) (update2: Update<_, _, _>) (update3: Update<_, _, _>) (update4: Update<_, _, _>) (update5: Update<_, _, _>) : Update<_,_,_> = fun msg (model1, model2, model3, model4, model5) ->
|
||||
match msg with
|
||||
| Choice1Of5 msg1 -> let newModel, cmds, extmsg = update1 msg1 model1 in (newModel, model2, model3, model4, model5), Cmd.map Choice1Of5 cmds, extmsg
|
||||
| Choice2Of5 msg2 -> let newModel, cmds, extmsg = update2 msg2 model2 in (model1, newModel, model3, model4, model5), Cmd.map Choice2Of5 cmds, extmsg
|
||||
| Choice3Of5 msg3 -> let newModel, cmds, extmsg = update3 msg3 model3 in (model1, model2, newModel, model4, model5), Cmd.map Choice3Of5 cmds, extmsg
|
||||
| Choice4Of5 msg4 -> let newModel, cmds, extmsg = update4 msg4 model4 in (model1, model2, model3, newModel, model5), Cmd.map Choice4Of5 cmds, extmsg
|
||||
| Choice5Of5 msg5 -> let newModel, cmds, extmsg = update5 msg5 model5 in (model1, model2, model3, model4, newModel), Cmd.map Choice5Of5 cmds, extmsg
|
||||
|
||||
/// Reconcile external messages by turning them into changes in the composed model and/or commands
|
||||
let reconcile f (update: Update<'model,'msg,'extmsg>) : Update<'model,'msg> = fun msg model ->
|
||||
let newModel, cmds, extmsg = update msg model
|
||||
let newModel2, cmds2 = f extmsg newModel
|
||||
newModel2, Cmd.batch [cmds; cmds2]
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module StaticView =
|
||||
|
||||
let internal setBindingContextsUntyped (bindings: ViewBindings<'model, 'msg>) (viewModel: ViewModel<obj, obj>) =
|
||||
for (bindingName, binding) in bindings do
|
||||
match binding with
|
||||
| BindSubModel (ViewSubModel (initf, _, _, _, _)) ->
|
||||
console.log (sprintf "view: seting data context for %s" bindingName)
|
||||
let subModel = viewModel.[bindingName]
|
||||
initf subModel
|
||||
| _ -> ()
|
||||
|
||||
let internal setBindingContexts (bindings: ViewBindings<'model, 'msg>) (viewModel: ViewModel<'model, 'msg>) =
|
||||
for (bindingName, binding) in bindings do
|
||||
match binding with
|
||||
| BindSubModel (ViewSubModel (initf, _, _, _, _)) ->
|
||||
console.log (sprintf "view: seting data context for %s" bindingName)
|
||||
let subModel = viewModel.[bindingName]
|
||||
initf subModel
|
||||
| _ -> ()
|
||||
|
||||
let internal pageInit (page: Page) contentf bindings (viewModel: ViewModel<'model, 'msg>) model dispatch =
|
||||
setBindingContexts bindings viewModel
|
||||
page.BindingContext <- box viewModel
|
||||
match contentf with
|
||||
| None -> None
|
||||
| Some f ->
|
||||
let viewData: XamlElement = f model dispatch
|
||||
let contentPage = (page :?> ContentPage)
|
||||
contentPage.Content <- viewData.CreateAsView()
|
||||
Some viewData
|
||||
|
||||
let internal pageUpdate (page: Page) contentf model prevViewDataOpt dispatch =
|
||||
match contentf, prevViewDataOpt with
|
||||
| Some f, Some prevViewData ->
|
||||
let viewData: XamlElement = f model dispatch
|
||||
viewData.ApplyIncremental (prevViewData, (page :?> ContentPage).Content)
|
||||
Some viewData
|
||||
| _ ->
|
||||
prevViewDataOpt
|
||||
|
||||
let internal pageInitUntyped (page: ContentPage) (bindings: ViewBindings<'model, 'msg>) =
|
||||
fun (objViewModel: obj) ->
|
||||
match objViewModel with
|
||||
| :? ViewModel<obj, obj> as viewModel ->
|
||||
setBindingContextsUntyped bindings viewModel
|
||||
page.BindingContext <- objViewModel
|
||||
| _ -> failwithf "unexpected type in pageInitUntyped: %A" (objViewModel.GetType())
|
||||
|
||||
let internal genViewName = let mutable c = 0 in fun () -> c <- c + 1; "StaticView"+string c
|
||||
|
||||
let internal apply (view: StaticView<_, _, _>) =
|
||||
let page, bindings = view()
|
||||
let name = genViewName()
|
||||
name, page, bindings
|
||||
|
||||
let subPage (view1: StaticView<_, _, _>) =
|
||||
let nm1, page1, bindings1 = apply view1
|
||||
page1,
|
||||
[ nm1 |> Binding.subView (pageInitUntyped page1 bindings1) id id bindings1 ]
|
||||
|
||||
let combo2 (view1: StaticView<_, _, _>) (view2: StaticView<_, _, _>) =
|
||||
let nm1, page1, bindings1 = apply view1
|
||||
let nm2, page2, bindings2 = apply view2
|
||||
(page1, page2),
|
||||
[ nm1 |> Binding.subView (pageInitUntyped page1 bindings1) (fun (a,_) -> a) Choice1Of2 bindings1
|
||||
nm2 |> Binding.subView (pageInitUntyped page1 bindings2) (fun (_,a) -> a) Choice2Of2 bindings2 ]
|
||||
|
||||
let combo3 (view1: StaticView<_, _, _>) (view2: StaticView<_, _, _>) (view3: StaticView<_, _, _>) =
|
||||
let nm1, page1, bindings1 = apply view1
|
||||
let nm2, page2, bindings2 = apply view2
|
||||
let nm3, page3, bindings3 = apply view3
|
||||
(page1, page2, page3),
|
||||
[ nm1 |> Binding.subView (pageInitUntyped page1 bindings1) (fun (a,_,_) -> a) Choice1Of3 bindings1
|
||||
nm2 |> Binding.subView (pageInitUntyped page2 bindings2) (fun (_,a,_) -> a) Choice2Of3 bindings2
|
||||
nm3 |> Binding.subView (pageInitUntyped page3 bindings3) (fun (_,_,a) -> a) Choice3Of3 bindings3 ]
|
||||
|
||||
let combo4 (view1: StaticView<_, _, _>) (view2: StaticView<_, _, _>) (view3: StaticView<_, _, _>) (view4: StaticView<_, _, _>) =
|
||||
let nm1, page1, bindings1 = apply view1
|
||||
let nm2, page2, bindings2 = apply view2
|
||||
let nm3, page3, bindings3 = apply view3
|
||||
let nm4, page4, bindings4 = apply view4
|
||||
(page1, page2, page3, page4),
|
||||
[ nm1 |> Binding.subView (pageInitUntyped page1 bindings1) (fun (a,_,_,_) -> a) Choice1Of4 bindings1
|
||||
nm2 |> Binding.subView (pageInitUntyped page2 bindings2) (fun (_,a,_,_) -> a) Choice2Of4 bindings2
|
||||
nm3 |> Binding.subView (pageInitUntyped page3 bindings3) (fun (_,_,a,_) -> a) Choice3Of4 bindings3
|
||||
nm4 |> Binding.subView (pageInitUntyped page4 bindings4) (fun (_,_,_,a) -> a) Choice4Of4 bindings4 ]
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Program =
|
||||
|
||||
/// Start the program loop.
|
||||
/// arg: argument to pass to the init() function.
|
||||
/// program: program created with 'mkSimple' or 'mkProgram'.
|
||||
let runOnGuiThreadWith (arg: 'arg) (program: Program<'arg, 'model, 'msg, 'view>) =
|
||||
let (model,cmd) = program.init arg
|
||||
let mutable state = model
|
||||
let rec processMsg msg =
|
||||
try
|
||||
let (updatedModel,newCommands) = program.update msg state
|
||||
program.setState updatedModel dispatch
|
||||
newCommands |> List.iter (fun sub -> sub dispatch)
|
||||
state <- updatedModel
|
||||
with ex ->
|
||||
//System.Diagnostics.Debugger.Log(ex.Message)
|
||||
//System.Diagnostics.Debug.Fail(ex.Message)
|
||||
System.Diagnostics.Debugger.Break()
|
||||
program.onError ("Unable to process a message:", ex)
|
||||
|
||||
and dispatch msg = Device.BeginInvokeOnMainThread(fun () -> processMsg msg)
|
||||
program.setState model dispatch
|
||||
program.subscribe model
|
||||
@ cmd |> List.iter (fun sub -> sub dispatch)
|
||||
|
||||
/// Start the dispatch loop with `unit` for the init() function.
|
||||
let runOnGuiThread (program: Program<unit, 'model, 'msg, 'view>) = runOnGuiThreadWith () program
|
||||
|
||||
/// Starts the Elmish dispatch loop for the page with the given Elmish program
|
||||
let _runStaticView debug (program: Program<_, _, _, _>) =
|
||||
|
||||
// Compute the view mappings once, on startup.
|
||||
console.log "view: computing initial view with dummy model/dispatch"
|
||||
let page,bindings = program.view Unchecked.defaultof<_> (fun _ -> failwith "do not call disaptch in the view")
|
||||
|
||||
let mutable lastModel = None
|
||||
|
||||
let setState model dispatch =
|
||||
match lastModel with
|
||||
| None ->
|
||||
|
||||
// Construct the binding context for the view model
|
||||
let viewModel = ViewModel (model, dispatch, bindings, debug)
|
||||
|
||||
let viewData = StaticView.pageInit page None bindings viewModel model dispatch
|
||||
|
||||
lastModel <- Some (viewModel, viewData)
|
||||
console.log "view: set data context"
|
||||
|
||||
| Some (viewModel, prevViewData) ->
|
||||
let viewData = StaticView.pageUpdate page None model prevViewData dispatch
|
||||
lastModel <- Some (viewModel, viewData)
|
||||
viewModel.UpdateModel model
|
||||
|
||||
// Start Elmish dispatch loop
|
||||
{ program with setState = setState }
|
||||
|> runOnGuiThread
|
||||
|
||||
page
|
||||
|
||||
/// Starts the Elmish dispatch loop for the page with the given Elmish program
|
||||
let _runDynamicView debug (program: Program<_, _, _, _>) =
|
||||
|
||||
// Compute the view mappings once, on startup.
|
||||
console.log "view: computing initial page"
|
||||
let page, bindings, contentf = program.view Unchecked.defaultof<_> (fun _ -> failwith "do not call disaptch in the view")
|
||||
|
||||
let mutable lastModel = None
|
||||
|
||||
let setState model dispatch =
|
||||
match lastModel with
|
||||
| None ->
|
||||
|
||||
// Construct the binding context for the view model
|
||||
let viewModel = ViewModel (model, dispatch, bindings, debug)
|
||||
|
||||
let viewData = StaticView.pageInit page (Some contentf) bindings viewModel model dispatch
|
||||
|
||||
lastModel <- Some (viewModel, viewData)
|
||||
console.log "view: set data context"
|
||||
|
||||
| Some (viewModel, prevViewData) ->
|
||||
let viewData = StaticView.pageUpdate page (Some contentf) model prevViewData dispatch
|
||||
lastModel <- Some (viewModel, viewData)
|
||||
viewModel.UpdateModel model
|
||||
|
||||
// Start Elmish dispatch loop
|
||||
{ program with setState = setState }
|
||||
|> runOnGuiThread
|
||||
|
||||
page
|
||||
|
||||
|
||||
/// Creates the view model for the given page and starts the Elmish dispatch loop for the matching program
|
||||
let runDynamicView program = _runDynamicView false program
|
||||
|
||||
/// Creates the view model for the given page and starts the Elmish dispatch loop for the matching program
|
||||
let runStaticView program = _runStaticView false program
|
||||
|
||||
/// Creates the view model for the given page and starts the Elmish dispatch loop for the matching program
|
||||
let run program = runStaticView program
|
||||
|
||||
/// Creates the view model for the given page and starts the Elmish dispatch loop for the matching program
|
||||
let runDebugDynamicView program = _runDynamicView true program
|
||||
|
||||
/// Creates the view model for the given page and starts the Elmish dispatch loop for the matching program
|
||||
let runDebugStaticView program = _runStaticView true program
|
||||
|
||||
/// Creates the view model for the given page and starts the Elmish dispatch loop for the matching program
|
||||
let runDebug program = runDebugStaticView program
|
||||
|
||||
|
||||
let withNavigation (program: Program<_,_,_,_>) =
|
||||
{ init = program.init
|
||||
update = program.update
|
||||
subscribe = program.subscribe
|
||||
setState = program.setState
|
||||
onError = program.onError
|
||||
view = (fun m d ->
|
||||
let page, bindings, navMap = program.view m d
|
||||
console.log "view: setting global navigation map"
|
||||
// TODO: modify the Elmish framework we use to remove this global state and pass it into all commands??
|
||||
Nav.globalNavMap <- (navMap |> List.map (fun (tg, page) -> ((tg :> System.IComparable), page)) |> Map.ofList)
|
||||
page, bindings )}
|
||||
|
||||
namespace Elmish.XamarinForms.DynamicViews
|
||||
|
||||
open Xamarin.Forms
|
||||
|
||||
open Elmish
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.DynamicViews
|
||||
|
||||
(*
|
||||
[<AutoOpen>]
|
||||
module GridHelpers =
|
||||
|
||||
open System.Runtime.CompilerServices
|
||||
open System.Collections.Generic
|
||||
open System.Windows.Input
|
||||
|
||||
type XamlElement with
|
||||
|
||||
/// Adjust the RowSpacing property in the visual element
|
||||
member x.WithGridRowSpacing(value: double) = x.WithAttribute("GridRowSpacing", box (makeGridLength value))
|
||||
|
||||
/// Adjust the ColumnSpacing property in the visual element
|
||||
member x.WithGridColumnSpacing(value: double) = x.WithAttribute("GridColumnSpacing", box (makeGridLength value))
|
||||
|
||||
/// Adjust the RowDefinitions property in the visual element
|
||||
member x.WithGridRowDefinitions (value: IList<XamlElement>) = x.WithAttribute("GridRowDefinitions", box value)
|
||||
|
||||
/// Adjust the Column property in the visual element
|
||||
member x.WithGridColumnDefinitions (value: IList<XamlElement>) = x.WithAttribute("GridColumnDefinitions", box value)
|
||||
|
||||
/// Adjust the Grid Row property in the visual element
|
||||
member x.WithGridRow(value: int) = x.WithAttribute("GridRow", box value)
|
||||
|
||||
/// Adjust the Grid Column property in the visual element
|
||||
member x.WithGridColumn(value: int) = x.WithAttribute("GridColumn", box value)
|
||||
|
||||
/// Adjust the Grid RowSpan property in the visual element
|
||||
member x.WithGridRowSpan(value: int) = x.WithAttribute("GridRowSpan", box value)
|
||||
|
||||
/// Adjust the Grid ColumnSpan property in the visual element
|
||||
member x.WithGridColumnSpan(value: int) = x.WithAttribute("GridColumnSpan", box value)
|
||||
|
||||
/// Try to get the Grid Row property in the visual element
|
||||
member x.TryGridRow = match x.Attributes.TryFind("GridRow") with Some v -> Some(unbox<int>(v)) | None -> None
|
||||
|
||||
/// Try to get the Grid Column property in the visual element
|
||||
member x.TryGridColumn = match x.Attributes.TryFind("GridColumn") with Some v -> Some(unbox<int>(v)) | None -> None
|
||||
|
||||
/// Try to get the Grid RowSpan property in the visual element
|
||||
member x.TryGridRowSpan = match x.Attributes.TryFind("GridRowSpan") with Some v -> Some(unbox<int>(v)) | None -> None
|
||||
|
||||
/// Try to get the Grid ColumnSpan property in the visual element
|
||||
member x.TryGridColumnSpan = match x.Attributes.TryFind("GridColumnSpan") with Some v -> Some(unbox<int>(v)) | None -> None
|
||||
|
||||
/// Try to get the Grid RowSpacing property in the visual element
|
||||
member x.TryGridRowSpacing = match x.Attributes.TryFind("GridRowSpacing") with Some v -> Some(unbox<double>(v)) | None -> None
|
||||
|
||||
/// Try to get the Grid ColumnSpacing property in the visual element
|
||||
member x.TryGridColumnSpacing = match x.Attributes.TryFind("GridColumnSpacing") with Some v -> Some(unbox<double>(v)) | None -> None
|
||||
|
||||
/// Get the RowDefinitions property in the visual element
|
||||
member x.TryGridRowDefinitions = match x.Attributes.TryFind("GridRowDefinitions") with Some v -> Some(unbox<IList<XamlElement>>(v)) | None -> None
|
||||
|
||||
/// Get the ColumnDefinitions property in the visual element
|
||||
member x.TryGridColumnDefinitions = match x.Attributes.TryFind("GridColumnDefinitions") with Some v -> Some(unbox<IList<XamlElement>>(v)) | None -> None
|
||||
|
||||
/// Create as a ColumnDefinition element
|
||||
member x.CreateAsGridColumnDefinition () = x.Create() :?> ColumnDefinition
|
||||
|
||||
/// Create as a RowDefinition element
|
||||
member x.CreateAsGridRowDefinition () = x.Create() :?> RowDefinition
|
||||
|
||||
/// Represents a ListViewDescription in the view desription
|
||||
type Xaml with
|
||||
static member Grid(?rowdefs: IList<XamlElement>, ?coldefs: IList<XamlElement>, ?children: IList<XamlElement>, ?rowSpacing: obj, ?columnSpacing: obj, ?horizontalOptions: Xamarin.Forms.LayoutOptions, ?verticalOptions: Xamarin.Forms.LayoutOptions, ?margin: Xamarin.Forms.Thickness, ?backgroundColor: Xamarin.Forms.Color, ?isVisible: bool, ?opacity: double, ?widthRequest: double, ?heightRequest: double, ?isEnabled: bool) =
|
||||
|
||||
let create () =
|
||||
box (new Xamarin.Forms.Grid())
|
||||
|
||||
let apply (prevOpt: XamlElement option) (source: XamlElement) (targetObj:obj) =
|
||||
let target = (targetObj :?> Xamarin.Forms.Grid)
|
||||
|
||||
match source.TryGridRowDefinitions with
|
||||
| Some coll when coll <> null && coll.Count > 0 ->
|
||||
if (coll = null || coll.Count = 0) then
|
||||
match target.RowDefinitions with
|
||||
| null -> ()
|
||||
| coll -> coll.Clear()
|
||||
else
|
||||
// Remove the excess children
|
||||
while (target.RowDefinitions.Count > coll.Count) do
|
||||
target.RowDefinitions.RemoveAt(target.RowDefinitions.Count - 1)
|
||||
|
||||
// Count the existing children
|
||||
let n = target.RowDefinitions.Count
|
||||
|
||||
// Adjust the existing children and create new children
|
||||
for i in 0 .. n - 1 do
|
||||
let newChild = coll.[i]
|
||||
let prevChildOpt = match prevOpt with None -> None | Some prev -> match prev.TryChildren with None -> None | Some coll when i < coll.Count -> Some coll.[i] | _ -> None
|
||||
if (match prevChildOpt with None -> true | Some prevChild -> not (obj.ReferenceEquals(prevChild, newChild))) then
|
||||
let targetChild = target.Children.[i]
|
||||
if (match prevChildOpt with None -> true | Some prevChild -> newChild.TargetType <> prevChild.TargetType) then
|
||||
target.RowDefinitions.[i] <- newChild.CreateAsGridRowDefinition()
|
||||
else
|
||||
newChild.ApplyIncremental(prevChildOpt.Value, targetChild)
|
||||
|
||||
// Create the new children
|
||||
for i in n .. coll.Count-1 do
|
||||
target.RowDefinitions.Insert(i, coll.[i].CreateAsGridRowDefinition())
|
||||
| _ -> ()
|
||||
|
||||
match source.TryGridColumnDefinitions with
|
||||
| Some coll when coll <> null && coll.Count > 0 ->
|
||||
if (coll = null || coll.Count = 0) then
|
||||
match target.ColumnDefinitions with
|
||||
| null -> ()
|
||||
| coll -> coll.Clear()
|
||||
else
|
||||
// Remove the excess children
|
||||
while (target.ColumnDefinitions.Count > coll.Count) do
|
||||
target.ColumnDefinitions.RemoveAt(target.ColumnDefinitions.Count - 1)
|
||||
|
||||
// Count the existing children
|
||||
let n = target.ColumnDefinitions.Count
|
||||
|
||||
// Adjust the existing children
|
||||
for i in 0 .. n - 1 do
|
||||
let newChild = coll.[i]
|
||||
let prevChildOpt = match prevOpt with None -> None | Some prev -> match prev.TryChildren with None -> None | Some coll when i < coll.Count -> Some coll.[i] | _ -> None
|
||||
if (match prevChildOpt with None -> true | Some prevChild -> not (obj.ReferenceEquals(prevChild, newChild))) then
|
||||
let targetChild = target.Children.[i]
|
||||
if (match prevChildOpt with None -> true | Some prevChild -> newChild.TargetType <> prevChild.TargetType) then
|
||||
target.ColumnDefinitions.[i] <- newChild.CreateAsGridColumnDefinition()
|
||||
else
|
||||
newChild.ApplyIncremental(prevChildOpt.Value, targetChild)
|
||||
|
||||
// Create the new children
|
||||
for i in n .. coll.Count-1 do
|
||||
target.ColumnDefinitions.Insert(i, coll.[i].CreateAsGridColumnDefinition())
|
||||
| _ -> ()
|
||||
|
||||
match source.TryChildren with
|
||||
| Some coll when coll <> null && coll.Count > 0 ->
|
||||
if (coll = null || coll.Count = 0) then
|
||||
match target.Children with
|
||||
| null -> ()
|
||||
| children -> children.Clear()
|
||||
else
|
||||
// Remove the excess children
|
||||
while (target.Children.Count > coll.Count) do
|
||||
target.Children.RemoveAt(target.Children.Count - 1)
|
||||
|
||||
// Count the existing children
|
||||
let n = target.Children.Count
|
||||
|
||||
// Adjust the existing children and create new children
|
||||
for i in 0 .. coll.Count-1 do
|
||||
let newChild = coll.[i]
|
||||
let prevChildOpt = match prevOpt with None -> None | Some prev -> match prev.TryChildren with Some coll when i < coll.Count && i < n -> Some coll.[i] | _ -> None
|
||||
let prevChildOpt, targetChild =
|
||||
if (match prevChildOpt with None -> true | Some prevChild -> not (obj.ReferenceEquals(prevChild, newChild))) then
|
||||
let mustCreate = (i >= n || match prevChildOpt with None -> true | Some prevChild -> newChild.TargetType <> prevChild.TargetType)
|
||||
if mustCreate then
|
||||
let targetChild = newChild.CreateAsView()
|
||||
|
||||
if i >= n then
|
||||
target.Children.Insert(i, targetChild)
|
||||
else
|
||||
target.Children.[i] <- targetChild
|
||||
None, targetChild
|
||||
else
|
||||
let targetChild = target.Children.[i]
|
||||
newChild.ApplyIncremental(prevChildOpt.Value, targetChild)
|
||||
prevChildOpt, targetChild
|
||||
else
|
||||
prevChildOpt, target.Children.[i]
|
||||
|
||||
// Adjust the attached properties
|
||||
match (match prevChildOpt with None -> None | Some prevChild -> prevChild.TryGridRow), newChild.TryGridRow with
|
||||
| Some prev, Some v when prev = v -> ()
|
||||
| _, Some v -> Grid.SetRow(targetChild, v)
|
||||
| _ -> ()
|
||||
match (match prevChildOpt with None -> None | Some prevChild -> prevChild.TryGridColumn), newChild.TryGridColumn with
|
||||
| Some prev, Some v when prev = v -> ()
|
||||
| _, Some v -> Grid.SetColumn(targetChild, v)
|
||||
| _ -> ()
|
||||
match (match prevChildOpt with None -> None | Some prevChild -> prevChild.TryGridRowSpan), newChild.TryGridRowSpan with
|
||||
| Some prev, Some v when prev = v -> ()
|
||||
| _, Some v -> Grid.SetRowSpan(targetChild, v)
|
||||
| _ -> ()
|
||||
match (match prevChildOpt with None -> None | Some prevChild -> prevChild.TryGridColumnSpan), newChild.TryGridColumnSpan with
|
||||
| Some prev, Some v when prev = v -> ()
|
||||
| _, Some v -> Grid.SetColumnSpan(targetChild, v)
|
||||
| _ -> ()
|
||||
| _ -> ()
|
||||
|
||||
let prevRowSpacing = match prevOpt with Some prev -> prev.TryGridRowSpacing | _ -> None
|
||||
match prevRowSpacing, source.TryGridRowSpacing with
|
||||
| Some prev, Some v when prev = v -> ()
|
||||
| _, Some v -> target.RowSpacing <- v
|
||||
| _, None -> ()
|
||||
|
||||
let prevColumnSpacing = match prevOpt with Some prev -> prev.TryGridColumnSpacing | _ -> None
|
||||
match prevColumnSpacing, source.TryGridColumnSpacing with
|
||||
| Some prev, Some v when prev = v -> ()
|
||||
| _, Some v -> target.ColumnSpacing <- v
|
||||
| _, None -> ()
|
||||
|
||||
let attribs = [|
|
||||
match children with None -> () | Some v -> yield ("Children", box v)
|
||||
match rowSpacing with None -> () | Some v -> yield ("GridRowSpacing", box (makeGridLength v))
|
||||
match columnSpacing with None -> () | Some v -> yield ("GridColumnSpacing", box (makeGridLength v))
|
||||
match rowdefs with None -> () | Some v -> yield ("GridRowDefinitions", box v)
|
||||
match coldefs with None -> () | Some v -> yield ("GridColumnDefinitions", box v)
|
||||
|]
|
||||
Xaml.Layout().Inherit(typeof<Xamarin.Forms.Grid>, create, apply, attribs)
|
||||
|
||||
let withRowSpacing m (x: XamlElement) = x.WithGridRowSpacing m
|
||||
let rowSpacing m x = withRowSpacing m x
|
||||
|
||||
let withColumnSpacing m (x: XamlElement) = x.WithGridColumnSpacing m
|
||||
let columnSpacing m (x: XamlElement) = withColumnSpacing m x
|
||||
|
||||
let withGridRow row (el: XamlElement) = el.WithGridRow(row)
|
||||
let gridRow row (el: XamlElement) = el.WithGridRow(row)
|
||||
let withGridColumn col (el: XamlElement) = el.WithGridColumn(col)
|
||||
let gridColumn col (el: XamlElement) = el.WithGridColumn(col)
|
||||
let withGridRowSpan rowspan (el: XamlElement) = el.WithGridRowSpan(rowspan)
|
||||
let gridRowSpan rowspan (el: XamlElement) = el.WithGridRowSpan(rowspan)
|
||||
let withGridColumnSpan colspan (el: XamlElement) = el.WithGridColumnSpan(colspan)
|
||||
let gridColumnSpan colspan (el: XamlElement) = el.WithGridColumnSpan(colspan)
|
||||
*)
|
||||
[<AutoOpen>]
|
||||
module SimplerHelpers =
|
||||
open System.Runtime.CompilerServices
|
||||
open System.Collections.Generic
|
||||
open System.Windows.Input
|
||||
|
||||
|
||||
let rowdef h = Xaml.RowDefinition(height=makeGridLength h)
|
||||
let coldef h = Xaml.ColumnDefinition(width=makeGridLength h)
|
||||
|
||||
let rows rds (els: XamlElement list) =
|
||||
let children = els |> List.mapi (fun i x -> x.GridRow i)
|
||||
Xaml.Grid(rowdefs=rds, children=children)
|
||||
|
||||
let cols cds (els: XamlElement list) =
|
||||
let children = els |> List.mapi (fun i x -> x.GridColumn i)
|
||||
Xaml.Grid(coldefs=cds, children=children)
|
||||
|
||||
|
||||
// Helper page for the TicTacToe sample
|
||||
// Need to generlize the HeightRequest phase of the XF content digestion process...
|
||||
type HelperPage(?viewAllocatedSizeFixup) as self =
|
||||
inherit ContentPage()
|
||||
do Xamarin.Forms.PlatformConfiguration.iOSSpecific.Page.SetUseSafeArea(self, true)
|
||||
|
||||
// painful.... It is unfortunately not possible to simpy recreate the whole
|
||||
// view here, you have to mutate the content in-place.
|
||||
override this.OnSizeAllocated(width, height) =
|
||||
base.OnSizeAllocated(width, height)
|
||||
match viewAllocatedSizeFixup with
|
||||
| Some f -> f self.Content (width, height)
|
||||
| None -> ()
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2018 Elmish.XamarinForms contributors. See LICENSE.md for license.
|
||||
namespace Elmish.XamarinForms.DynamicViews
|
||||
|
||||
open Xamarin.Forms
|
||||
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.DynamicViews
|
||||
[<AutoOpen>]
|
||||
module SimplerHelpers =
|
||||
open System.Runtime.CompilerServices
|
||||
open System.Collections.Generic
|
||||
open System.Windows.Input
|
||||
|
||||
|
||||
let rowdef h = Xaml.RowDefinition(height=makeGridLength h)
|
||||
let coldef h = Xaml.ColumnDefinition(width=makeGridLength h)
|
||||
|
||||
let rows rds (els: XamlElement list) =
|
||||
let children = els |> List.mapi (fun i x -> x.GridRow i)
|
||||
Xaml.Grid(rowdefs=rds, children=children)
|
||||
|
||||
let cols cds (els: XamlElement list) =
|
||||
let children = els |> List.mapi (fun i x -> x.GridColumn i)
|
||||
Xaml.Grid(coldefs=cds, children=children)
|
||||
|
||||
|
||||
// Helper page for the TicTacToe sample
|
||||
// Need to generlize the HeightRequest phase of the XF content digestion process...
|
||||
type HelperPage(?viewAllocatedSizeFixup) as self =
|
||||
inherit ContentPage()
|
||||
do Xamarin.Forms.PlatformConfiguration.iOSSpecific.Page.SetUseSafeArea(self, true)
|
||||
|
||||
// painful.... It is unfortunately not possible to simpy recreate the whole
|
||||
// view here, you have to mutate the content in-place.
|
||||
override this.OnSizeAllocated(width, height) =
|
||||
base.OnSizeAllocated(width, height)
|
||||
match viewAllocatedSizeFixup with
|
||||
| Some f -> f self.Content (width, height)
|
||||
| None -> ()
|
||||
|
|
@ -6,19 +6,11 @@
|
|||
<Compile Include="AssemblyInfo.fs" />
|
||||
<Compile Include="DynamicXamlConverters.fs" />
|
||||
<Compile Include="DynamicXaml.fs" />
|
||||
<Compile Include="FableStub.fs" />
|
||||
<Compile Include="..\paket-files\neutral\fable-elmish\elmish\src\cmd.fs">
|
||||
<Paket>True</Paket>
|
||||
<Link>elmish/src/cmd.fs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\paket-files\neutral\fable-elmish\elmish\src\program.fs">
|
||||
<Paket>True</Paket>
|
||||
<Link>elmish/src/program.fs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Cmd.fs" />
|
||||
<Compile Include="Binding.fs" />
|
||||
<Compile Include="ViewModel.fs" />
|
||||
<Compile Include="Combinators.fs" />
|
||||
<Compile Include="ElmishCmd.fs" />
|
||||
<Compile Include="StaticXamlBinding.fs" />
|
||||
<Compile Include="StaticXamlViewModel.fs" />
|
||||
<Compile Include="ElmishProgram.fs" />
|
||||
<Compile Include="DynamicViewHelpers.fs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FSharp.Core" Version="4.3.3" />
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2018 Elmish and Elmish.XamarinForms contributors. See LICENSE.md for license.
|
||||
namespace Elmish.XamarinForms
|
||||
|
||||
open System
|
||||
open System.Diagnostics
|
||||
open FSharp.Control
|
||||
|
||||
/// Dispatch - feed new message into the processing loop
|
||||
type Dispatch<'msg> = 'msg -> unit
|
||||
|
||||
/// Subscription - return immediately, but may schedule dispatch of a message at any time
|
||||
type Sub<'msg> = Dispatch<'msg> -> unit
|
||||
|
||||
/// Cmd - container for subscriptions that may produce messages
|
||||
type Cmd<'msg> = Sub<'msg> list
|
||||
|
||||
/// Cmd module for creating and manipulating commands
|
||||
[<RequireQualifiedAccess>]
|
||||
module Cmd =
|
||||
/// None - no commands, also known as `[]`
|
||||
let none : Cmd<'msg> =
|
||||
[]
|
||||
|
||||
/// Command to issue a specific message
|
||||
let ofMsg (msg:'msg) : Cmd<'msg> =
|
||||
[fun dispatch -> dispatch msg]
|
||||
|
||||
/// When emitting the message, map to another type
|
||||
let map (f: 'a -> 'msg) (cmd: Cmd<'a>) : Cmd<'msg> =
|
||||
cmd |> List.map (fun g -> (fun dispatch -> f >> dispatch) >> g)
|
||||
|
||||
/// Aggregate multiple commands
|
||||
let batch (cmds: #seq<Cmd<'msg>>) : Cmd<'msg> =
|
||||
cmds |> List.concat
|
||||
|
||||
/// Command to call the subscriber
|
||||
let ofSub (sub: Sub<'msg>) : Cmd<'msg> =
|
||||
[sub]
|
||||
|
||||
let dispatch d (cmd: Cmd<_>) = for sub in cmd do sub d
|
||||
|
||||
let ofAsyncMsg (p: Async<'msg>) : Cmd<_> =
|
||||
[ fun dispatch -> async { let! msg = p in dispatch p } |> Async.StartImmediate ]
|
||||
|
||||
let ofAsyncMsgs p : Cmd<_> =
|
||||
[ fun dispatch -> p |> AsyncSeq.iter dispatch |> Async.StartImmediate ]
|
||||
|
||||
type CmdBuilder() =
|
||||
inherit AsyncSeq.AsyncSeqBuilder()
|
||||
member x.Run(p: AsyncSeq<_>) = ofAsyncMsgs p
|
||||
|
||||
[<AutoOpen>]
|
||||
module CommandBuilder =
|
||||
let cmd = Cmd.CmdBuilder()
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
// Copyright 2018 Elmish.XamarinForms contributors. See LICENSE.md for license.
|
||||
namespace Elmish.XamarinForms
|
||||
|
||||
open System
|
||||
open System.Diagnostics
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.StaticViews
|
||||
open Elmish.XamarinForms.DynamicViews
|
||||
open Xamarin.Forms
|
||||
|
||||
/// Represents a model when you don't have a model. A Clayton's model.
|
||||
type NoModel =
|
||||
| NoModel
|
||||
|
||||
type Update<'model, 'msg> = 'msg -> 'model -> 'model * Cmd<'msg>
|
||||
type Update<'model, 'msg, 'extmsg> = 'msg -> 'model -> 'model * Cmd<'msg> * 'extmsg
|
||||
type StaticView<'model, 'msg, 'page> = unit -> 'page * ViewBindings<'model, 'msg>
|
||||
type DynamicView<'model, 'msg, 'page> = unit -> 'page * ('model -> 'msg -> Xamarin.Forms.View)
|
||||
|
||||
[<AutoOpen>]
|
||||
module Values =
|
||||
let NoCmd<'a> : Cmd<'a> = Cmd.none
|
||||
let mutable currentPage : Page = null
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Nav =
|
||||
|
||||
// TODO: modify the Elmish framework we use to remove this global state and pass it into all commands??
|
||||
let mutable globalNavMap : Map<System.IComparable, Page> = Map.empty
|
||||
|
||||
/// Push new location into history and navigate there
|
||||
let push (fromPageTag: ('navTarget :> System.IComparable)) (toPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let toPage = globalNavMap.[toPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PushAsync toPage |> ignore ]
|
||||
|
||||
/// Push new location into history and navigate there
|
||||
let pushModal (fromPageTag: ('navTarget :> System.IComparable)) (toPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let toPage = globalNavMap.[toPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PushModalAsync toPage |> ignore ]
|
||||
|
||||
let popToRoot (fromPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PopToRootAsync() |> ignore ]
|
||||
|
||||
let popModal (fromPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PopModalAsync() |> ignore ]
|
||||
|
||||
let pop (fromPageTag: ('navTarget :> System.IComparable)) : Cmd<_> =
|
||||
[ fun _ ->
|
||||
let fromPage = globalNavMap.[fromPageTag]
|
||||
let nav = fromPage.Navigation
|
||||
nav.PopAsync() |> ignore ]
|
||||
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Init =
|
||||
let combo2 init1 init2 () =
|
||||
let model1 = init1()
|
||||
let model2 = init2()
|
||||
(model1, model2)
|
||||
|
||||
let combo3 init1 init2 init3 () =
|
||||
let model1 = init1()
|
||||
let model2 = init2()
|
||||
let model3 = init3()
|
||||
(model1, model2, model3)
|
||||
|
||||
let combo4 init1 init2 init3 init4 () =
|
||||
let model1 = init1()
|
||||
let model2 = init2()
|
||||
let model3 = init3()
|
||||
let model4 = init4()
|
||||
(model1, model2, model3, model4)
|
||||
|
||||
let combo5 init1 init2 init3 init4 init5 () =
|
||||
let model1 = init1()
|
||||
let model2 = init2()
|
||||
let model3 = init3()
|
||||
let model4 = init4()
|
||||
let model5 = init5()
|
||||
(model1, model2, model3, model4, model5)
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module InitCmd =
|
||||
let combo2 init1 init2 () =
|
||||
let model1, cmd1 = init1()
|
||||
let model2, cmd2 = init2()
|
||||
(model1, model2), Cmd.batch [cmd1; cmd2]
|
||||
|
||||
let combo3 init1 init2 init3 () =
|
||||
let model1, cmd1 = init1()
|
||||
let model2, cmd2 = init2()
|
||||
let model3, cmd3 = init3()
|
||||
(model1, model2, model3), Cmd.batch [cmd1; cmd2; cmd3]
|
||||
|
||||
let combo4 init1 init2 init3 init4 () =
|
||||
let model1, cmd1 = init1()
|
||||
let model2, cmd2 = init2()
|
||||
let model3, cmd3 = init3()
|
||||
let model4, cmd4 = init4()
|
||||
(model1, model2, model3, model4), Cmd.batch [cmd1; cmd2; cmd3; cmd4]
|
||||
|
||||
let combo5 init1 init2 init3 init4 init5 () =
|
||||
let model1, cmd1 = init1()
|
||||
let model2, cmd2 = init2()
|
||||
let model3, cmd3 = init3()
|
||||
let model4, cmd4 = init4()
|
||||
let model5, cmd5 = init5()
|
||||
(model1, model2, model3, model4, model5), Cmd.batch [cmd1; cmd2; cmd3; cmd4; cmd5]
|
||||
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Update =
|
||||
|
||||
let combo2 (update1: Update<_, _, _>) (update2: Update<_, _, _>) : Update<_,_,_> = fun msg (model1, model2) ->
|
||||
match msg with
|
||||
| Choice1Of2 msg1 -> let newModel, cmds, extmsg = update1 msg1 model1 in (newModel, model2), Cmd.map Choice1Of2 cmds, extmsg
|
||||
| Choice2Of2 msg2 -> let newModel, cmds, extmsg = update2 msg2 model2 in (model1, newModel), Cmd.map Choice2Of2 cmds, extmsg
|
||||
|
||||
let combo3 (update1: Update<_, _, _>) (update2: Update<_, _, _>) (update3: Update<_, _, _>) : Update<_,_,_> = fun msg (model1, model2, model3) ->
|
||||
match msg with
|
||||
| Choice1Of3 msg1 -> let newModel, cmds, extmsg = update1 msg1 model1 in (newModel, model2, model3), Cmd.map Choice1Of3 cmds, extmsg
|
||||
| Choice2Of3 msg2 -> let newModel, cmds, extmsg = update2 msg2 model2 in (model1, newModel, model3), Cmd.map Choice2Of3 cmds, extmsg
|
||||
| Choice3Of3 msg3 -> let newModel, cmds, extmsg = update3 msg3 model3 in (model1, model2, newModel), Cmd.map Choice3Of3 cmds, extmsg
|
||||
|
||||
let combo4 (update1: Update<_, _, _>) (update2: Update<_, _, _>) (update3: Update<_, _, _>) (update4: Update<_, _, _>) : Update<_,_,_> = fun msg (model1, model2, model3, model4) ->
|
||||
match msg with
|
||||
| Choice1Of4 msg1 -> let newModel, cmds, extmsg = update1 msg1 model1 in (newModel, model2, model3, model4), Cmd.map Choice1Of4 cmds, extmsg
|
||||
| Choice2Of4 msg2 -> let newModel, cmds, extmsg = update2 msg2 model2 in (model1, newModel, model3, model4), Cmd.map Choice2Of4 cmds, extmsg
|
||||
| Choice3Of4 msg3 -> let newModel, cmds, extmsg = update3 msg3 model3 in (model1, model2, newModel, model4), Cmd.map Choice3Of4 cmds, extmsg
|
||||
| Choice4Of4 msg4 -> let newModel, cmds, extmsg = update4 msg4 model4 in (model1, model2, model3, newModel), Cmd.map Choice4Of4 cmds, extmsg
|
||||
|
||||
let combo5 (update1: Update<_, _, _>) (update2: Update<_, _, _>) (update3: Update<_, _, _>) (update4: Update<_, _, _>) (update5: Update<_, _, _>) : Update<_,_,_> = fun msg (model1, model2, model3, model4, model5) ->
|
||||
match msg with
|
||||
| Choice1Of5 msg1 -> let newModel, cmds, extmsg = update1 msg1 model1 in (newModel, model2, model3, model4, model5), Cmd.map Choice1Of5 cmds, extmsg
|
||||
| Choice2Of5 msg2 -> let newModel, cmds, extmsg = update2 msg2 model2 in (model1, newModel, model3, model4, model5), Cmd.map Choice2Of5 cmds, extmsg
|
||||
| Choice3Of5 msg3 -> let newModel, cmds, extmsg = update3 msg3 model3 in (model1, model2, newModel, model4, model5), Cmd.map Choice3Of5 cmds, extmsg
|
||||
| Choice4Of5 msg4 -> let newModel, cmds, extmsg = update4 msg4 model4 in (model1, model2, model3, newModel, model5), Cmd.map Choice4Of5 cmds, extmsg
|
||||
| Choice5Of5 msg5 -> let newModel, cmds, extmsg = update5 msg5 model5 in (model1, model2, model3, model4, newModel), Cmd.map Choice5Of5 cmds, extmsg
|
||||
|
||||
/// Reconcile external messages by turning them into changes in the composed model and/or commands
|
||||
let reconcile f (update: Update<'model,'msg,'extmsg>) : Update<'model,'msg> = fun msg model ->
|
||||
let newModel, cmds, extmsg = update msg model
|
||||
let newModel2, cmds2 = f extmsg newModel
|
||||
newModel2, Cmd.batch [cmds; cmds2]
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module StaticView =
|
||||
|
||||
let internal setBindingContextsUntyped (bindings: ViewBindings<'model, 'msg>) (viewModel: StaticViewModel<obj, obj>) =
|
||||
for (bindingName, binding) in bindings do
|
||||
match binding with
|
||||
| BindSubModel (ViewSubModel (initf, _, _, _, _)) ->
|
||||
Trace.WriteLine (sprintf "view: seting data context for %s" bindingName)
|
||||
let subModel = viewModel.[bindingName]
|
||||
initf subModel
|
||||
| _ -> ()
|
||||
|
||||
let internal setBindingContexts (bindings: ViewBindings<'model, 'msg>) (viewModel: StaticViewModel<'model, 'msg>) =
|
||||
for (bindingName, binding) in bindings do
|
||||
match binding with
|
||||
| BindSubModel (ViewSubModel (initf, _, _, _, _)) ->
|
||||
Trace.WriteLine (sprintf "view: seting data context for %s" bindingName)
|
||||
let subModel = viewModel.[bindingName]
|
||||
initf subModel
|
||||
| _ -> ()
|
||||
|
||||
let internal staticPageInitUntyped (page: ContentPage) (bindings: ViewBindings<'model, 'msg>) =
|
||||
fun (objViewModel: obj) ->
|
||||
match objViewModel with
|
||||
| :? StaticViewModel<obj, obj> as viewModel ->
|
||||
setBindingContextsUntyped bindings viewModel
|
||||
page.BindingContext <- objViewModel
|
||||
| _ -> failwithf "unexpected type in staticPageInitUntyped: %A" (objViewModel.GetType())
|
||||
|
||||
let internal genViewName = let mutable c = 0 in fun () -> c <- c + 1; "View"+string c
|
||||
|
||||
let internal apply (view: StaticView<_, _, _>) =
|
||||
let page, bindings = view()
|
||||
let name = genViewName()
|
||||
name, page, bindings
|
||||
|
||||
let subPage (view1: StaticView<_, _, _>) =
|
||||
let nm1, page1, bindings1 = apply view1
|
||||
page1,
|
||||
[ nm1 |> Binding.subView (staticPageInitUntyped page1 bindings1) id id bindings1 ]
|
||||
|
||||
let combo2 (view1: StaticView<_, _, _>) (view2: StaticView<_, _, _>) =
|
||||
let nm1, page1, bindings1 = apply view1
|
||||
let nm2, page2, bindings2 = apply view2
|
||||
(page1, page2),
|
||||
[ nm1 |> Binding.subView (staticPageInitUntyped page1 bindings1) (fun (a,_) -> a) Choice1Of2 bindings1
|
||||
nm2 |> Binding.subView (staticPageInitUntyped page1 bindings2) (fun (_,a) -> a) Choice2Of2 bindings2 ]
|
||||
|
||||
let combo3 (view1: StaticView<_, _, _>) (view2: StaticView<_, _, _>) (view3: StaticView<_, _, _>) =
|
||||
let nm1, page1, bindings1 = apply view1
|
||||
let nm2, page2, bindings2 = apply view2
|
||||
let nm3, page3, bindings3 = apply view3
|
||||
(page1, page2, page3),
|
||||
[ nm1 |> Binding.subView (staticPageInitUntyped page1 bindings1) (fun (a,_,_) -> a) Choice1Of3 bindings1
|
||||
nm2 |> Binding.subView (staticPageInitUntyped page2 bindings2) (fun (_,a,_) -> a) Choice2Of3 bindings2
|
||||
nm3 |> Binding.subView (staticPageInitUntyped page3 bindings3) (fun (_,_,a) -> a) Choice3Of3 bindings3 ]
|
||||
|
||||
let combo4 (view1: StaticView<_, _, _>) (view2: StaticView<_, _, _>) (view3: StaticView<_, _, _>) (view4: StaticView<_, _, _>) =
|
||||
let nm1, page1, bindings1 = apply view1
|
||||
let nm2, page2, bindings2 = apply view2
|
||||
let nm3, page3, bindings3 = apply view3
|
||||
let nm4, page4, bindings4 = apply view4
|
||||
(page1, page2, page3, page4),
|
||||
[ nm1 |> Binding.subView (staticPageInitUntyped page1 bindings1) (fun (a,_,_,_) -> a) Choice1Of4 bindings1
|
||||
nm2 |> Binding.subView (staticPageInitUntyped page2 bindings2) (fun (_,a,_,_) -> a) Choice2Of4 bindings2
|
||||
nm3 |> Binding.subView (staticPageInitUntyped page3 bindings3) (fun (_,_,a,_) -> a) Choice3Of4 bindings3
|
||||
nm4 |> Binding.subView (staticPageInitUntyped page4 bindings4) (fun (_,_,_,a) -> a) Choice4Of4 bindings4 ]
|
||||
|
||||
/// Program type captures various aspects of program behavior
|
||||
type Program<'model, 'msg, 'view> =
|
||||
{ init : unit -> 'model * Cmd<'msg>
|
||||
update : 'msg -> 'model -> 'model * Cmd<'msg>
|
||||
subscribe : 'model -> Cmd<'msg>
|
||||
view : 'view
|
||||
onError : (string*exn) -> unit }
|
||||
|
||||
/// Program module - functions to manipulate program instances
|
||||
[<RequireQualifiedAccess>]
|
||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||
module Program =
|
||||
let internal onError (text: string, ex: exn) =
|
||||
Trace.WriteLine (sprintf "%s: %A" text ex)
|
||||
|
||||
/// Typical program, new commands are produced by `init` and `update` along with the new state.
|
||||
let mkProgram init update view =
|
||||
{ init = init
|
||||
update = update
|
||||
view = view
|
||||
subscribe = fun _ -> Cmd.none
|
||||
onError = onError }
|
||||
|
||||
/// Simple program that produces only new state with `init` and `update`.
|
||||
let mkSimple init update view = mkProgram (fun arg -> init arg, Cmd.none) (fun msg model -> update msg model, Cmd.none) view
|
||||
|
||||
/// Subscribe to external source of events.
|
||||
/// The subscription is called once - with the initial model, but can dispatch new messages at any time.
|
||||
let withSubscription (subscribe : 'model -> Cmd<'msg>) (program: Program<'model, 'msg, 'view>) =
|
||||
let sub model =
|
||||
Cmd.batch [ program.subscribe model
|
||||
subscribe model ]
|
||||
{ program with subscribe = sub }
|
||||
|
||||
/// Trace all the updates to the console
|
||||
let withConsoleTrace (program: Program<'model, 'msg, 'view>) =
|
||||
let traceInit () =
|
||||
let initModel,cmd = program.init ()
|
||||
Console.WriteLine (sprintf "Initial state: %A" initModel)
|
||||
initModel,cmd
|
||||
|
||||
let traceUpdate msg model =
|
||||
Console.WriteLine (sprintf "New message: %A" msg)
|
||||
let newModel,cmd = program.update msg model
|
||||
Console.WriteLine (sprintf "Updated state: %A" newModel)
|
||||
newModel,cmd
|
||||
|
||||
{ program with
|
||||
init = traceInit
|
||||
update = traceUpdate }
|
||||
|
||||
/// Trace all the messages as they update the model
|
||||
let withTrace trace (program: Program<'model, 'msg, 'view>) =
|
||||
{ program
|
||||
with update = fun msg model -> trace msg model; program.update msg model}
|
||||
|
||||
/// Handle dispatch loop exceptions
|
||||
let withErrorHandler onError (program: Program<'model, 'msg, 'view>) =
|
||||
{ program
|
||||
with onError = onError }
|
||||
|
||||
|
||||
/// Starts the Elmish dispatch loop for the page with the given Elmish program
|
||||
let _run debug (program: Program<_, _, _>) =
|
||||
|
||||
Trace.WriteLine "run: computing initial model"
|
||||
|
||||
// Get the initial model
|
||||
let (model,cmd) = program.init ()
|
||||
|
||||
let mutable lastModel = model
|
||||
let mutable lastViewData = None
|
||||
let initialMessages = ResizeArray<_>()
|
||||
let mutable dispatchImpl = (fun msg -> initialMessages.Add(msg))
|
||||
let dispatch msg = dispatchImpl msg
|
||||
|
||||
Trace.WriteLine "run: computing static components of view"
|
||||
|
||||
// Extract the static content from the view
|
||||
let viewInfo = program.view ()
|
||||
|
||||
// If the view is dynamic, create the initial page
|
||||
let viewInfo =
|
||||
match viewInfo with
|
||||
| Choice1Of2 (mainPage, bindings) -> Choice1Of2 (mainPage, bindings)
|
||||
| Choice2Of2 (contentf: _ -> _ -> XamlElement) ->
|
||||
let pageDescription = contentf model dispatch
|
||||
let mainPage = pageDescription.CreateAsPage()
|
||||
Choice2Of2 (pageDescription, mainPage, contentf)
|
||||
|
||||
// We need a mainPage to return
|
||||
let mainPage =
|
||||
match viewInfo with
|
||||
| Choice1Of2 (mainPage, bindings) -> mainPage
|
||||
| Choice2Of2 (_, mainPage, _) -> mainPage
|
||||
|
||||
// Start Elmish dispatch loop
|
||||
let rec processMsg msg =
|
||||
try
|
||||
let (updatedModel,newCommands) = program.update msg lastModel
|
||||
updateView updatedModel
|
||||
newCommands |> List.iter (fun sub -> sub dispatch)
|
||||
lastModel <- updatedModel
|
||||
with ex ->
|
||||
//System.Diagnostics.Debugger.Log(ex.Message)
|
||||
//System.Diagnostics.Debug.Fail(ex.Message)
|
||||
System.Diagnostics.Debugger.Break()
|
||||
program.onError ("Unable to process a message:", ex)
|
||||
|
||||
and updateView model =
|
||||
match lastViewData with
|
||||
| None ->
|
||||
|
||||
// Construct the binding context for the view model
|
||||
|
||||
let viewData =
|
||||
match viewInfo with
|
||||
| Choice1Of2 (mainPage, bindings) ->
|
||||
let viewModel = StaticViewModel (model, dispatch, bindings, debug)
|
||||
StaticView.setBindingContexts bindings viewModel
|
||||
mainPage.BindingContext <- box viewModel
|
||||
Choice1Of2 (mainPage, bindings, viewModel)
|
||||
| Choice2Of2 (pageDescription, mainPage, contentf) ->
|
||||
Choice2Of2 (pageDescription, mainPage, contentf)
|
||||
|
||||
lastViewData <- Some viewData
|
||||
Trace.WriteLine "view: set data context"
|
||||
|
||||
| Some prevViewData ->
|
||||
let viewData =
|
||||
match prevViewData with
|
||||
| Choice1Of2 (page, bindings, viewModel) ->
|
||||
viewModel.UpdateModel model
|
||||
Choice1Of2 (page, bindings, viewModel)
|
||||
| Choice2Of2 (prevPageDescription, page, contentf) ->
|
||||
let newPageDescription: XamlElement = contentf model dispatch
|
||||
newPageDescription.ApplyIncremental (prevPageDescription, page)
|
||||
Choice2Of2 (newPageDescription, page, contentf)
|
||||
| _ -> failwith "unreachable"
|
||||
lastViewData <- Some viewData
|
||||
|
||||
let initialMsgs = initialMessages.ToArray()
|
||||
initialMessages.Clear()
|
||||
dispatchImpl <- (fun msg -> Device.BeginInvokeOnMainThread(fun () -> processMsg msg))
|
||||
|
||||
Trace.WriteLine "updating the initial view"
|
||||
|
||||
updateView model
|
||||
|
||||
Trace.WriteLine "dispatching initial commands"
|
||||
for sub in (program.subscribe model @ cmd) do
|
||||
sub dispatch
|
||||
for initialMsg in initialMsgs do
|
||||
dispatch initialMsg
|
||||
|
||||
mainPage
|
||||
|
||||
|
||||
/// Creates the view model for the given page and starts the Elmish dispatch loop for the matching program
|
||||
let run program = _run false program
|
||||
|
||||
/// Creates the view model for the given page and starts the Elmish dispatch loop for the matching program
|
||||
let runDebug program = _run true program
|
||||
|
||||
let withNavigation (program: Program<_,_,_>) =
|
||||
{ init = program.init
|
||||
update = program.update
|
||||
subscribe = program.subscribe
|
||||
onError = program.onError
|
||||
view = (fun () ->
|
||||
let page, contents, navMap = program.view ()
|
||||
Trace.WriteLine "view: setting global navigation map"
|
||||
// TODO: modify the Elmish framework we use to remove this global state and pass it into all commands??
|
||||
Nav.globalNavMap <- (navMap |> List.map (fun (tg, page) -> ((tg :> System.IComparable), page)) |> Map.ofList)
|
||||
page, contents )}
|
||||
|
||||
let withStaticView (program: Program<_,_,_>) =
|
||||
{ init = program.init
|
||||
update = program.update
|
||||
subscribe = program.subscribe
|
||||
onError = program.onError
|
||||
view = (fun () ->
|
||||
let page, bindings = program.view ()
|
||||
Choice1Of2 ((page :> Page), bindings)) }
|
||||
|
||||
let withDynamicView (program: Program<_,_,_>) =
|
||||
{ init = program.init
|
||||
update = program.update
|
||||
subscribe = program.subscribe
|
||||
onError = program.onError
|
||||
view = (fun () -> Choice2Of2 program.view) }
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright 2018 Elmish.XamarinForms contributors. See LICENSE.md for license.
|
||||
[<AutoOpen>]
|
||||
module Fable
|
||||
[<AutoOpen>]
|
||||
module PowerPack = ()
|
||||
[<AutoOpen>]
|
||||
module Core =
|
||||
[<AutoOpen>]
|
||||
module JsInterop = ()
|
||||
[<AutoOpen>]
|
||||
module Import =
|
||||
[<AutoOpen>]
|
||||
module Browser =
|
||||
[<AutoOpen>]
|
||||
type console =
|
||||
|
||||
[<System.Diagnostics.Conditional("DEBUG")>]
|
||||
static member error (str, ex) = sprintf "%s: %O" str ex |> System.Console.WriteLine
|
||||
|
||||
[<System.Diagnostics.Conditional("DEBUG")>]
|
||||
static member log (o: string) = sprintf "%s -- %A" (System.DateTime.Now.ToString("o")) o |> System.Console.WriteLine
|
||||
|
||||
[<System.Diagnostics.Conditional("DEBUG")>]
|
||||
static member log (o: string, v:obj) = sprintf "%s -- %s %A" (System.DateTime.Now.ToString("o")) o v |> System.Console.WriteLine
|
||||
|
||||
let toJson o = o
|
||||
|
||||
[<AutoOpen>]
|
||||
module JS =
|
||||
[<AutoOpen>]
|
||||
|
||||
module JSON =
|
||||
let parse str = str
|
||||
type Promise() = class end
|
||||
type Promise<'T>() =
|
||||
inherit Promise()
|
||||
with
|
||||
static member map _ = failwith "Promise not supported"
|
||||
static member catch _ = failwith "Promise not supported"
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright 2018 Elmish.XamarinForms contributors. See LICENSE.md for license.
|
||||
namespace Elmish.XamarinForms
|
||||
namespace Elmish.XamarinForms.StaticViews
|
||||
|
||||
open System
|
||||
open Xamarin.Forms
|
||||
open Elmish
|
||||
open Elmish.XamarinForms
|
||||
|
||||
type internal Getter<'model> =
|
||||
'model -> obj
|
|
@ -1,24 +1,25 @@
|
|||
// Copyright 2018 Elmish.XamarinForms contributors. See LICENSE.md for license.
|
||||
namespace Elmish.XamarinForms
|
||||
namespace Elmish.XamarinForms.StaticViews
|
||||
|
||||
open System
|
||||
open System.Collections.Generic
|
||||
open System.ComponentModel
|
||||
open System.Diagnostics
|
||||
open Xamarin.Forms
|
||||
open Elmish
|
||||
open Elmish.XamarinForms
|
||||
|
||||
/// The internal representation of a binding in the ViewModel
|
||||
/// The internal representation of a binding in the ViewModel for static Xaml
|
||||
type internal PropertyBinding<'model, 'msg> =
|
||||
| Get of Getter<'model>
|
||||
| Set of Setter<'model, 'msg>
|
||||
| GetSet of Getter<'model> * Setter<'model, 'msg>
|
||||
| GetSetValidate of Getter<'model> * ValidSetter<'model, 'msg>
|
||||
| Cmd of Xamarin.Forms.Command
|
||||
| SubModel of ('model -> obj) * (obj -> 'msg) * ViewModel<obj, obj>
|
||||
| SubModel of ('model -> obj) * (obj -> 'msg) * StaticViewModel<obj, obj>
|
||||
| Map of Getter<'model> * (obj -> obj)
|
||||
|
||||
and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBindings<'model, 'msg>, debug: bool) as self =
|
||||
and StaticViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBindings<'model, 'msg>, debug: bool) as self =
|
||||
inherit System.Dynamic.DynamicObject()
|
||||
|
||||
let props = new Dictionary<string, PropertyBinding<'model, 'msg>>()
|
||||
|
@ -33,7 +34,7 @@ and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBind
|
|||
// For INotifyPropertyChanged
|
||||
let propertyChanged = Event<PropertyChangedEventHandler, PropertyChangedEventArgs> ()
|
||||
let notifyPropertyChanged name =
|
||||
if debug then console.log (sprintf "notifyPropertyChanged %s" name)
|
||||
if debug then Trace.WriteLine (sprintf "notifyPropertyChanged %s" name)
|
||||
let key = "Item[" + name + "]"
|
||||
propertyChanged.Trigger(self, PropertyChangedEventArgs key)
|
||||
|
||||
|
@ -50,17 +51,17 @@ and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBind
|
|||
let toCommand name (exec, canExec) =
|
||||
let execute =
|
||||
Action<obj> (fun cmdParameter ->
|
||||
if debug then console.log (sprintf "view: execute cmd %s" name)
|
||||
if debug then Trace.WriteLine (sprintf "view: execute cmd %s" name)
|
||||
let msg =
|
||||
try exec cmdParameter model
|
||||
with exn ->
|
||||
if debug then console.log (sprintf "view: execute cmd %s raised exception:\n%s" name (exn.ToString()))
|
||||
if debug then Trace.WriteLine (sprintf "view: execute cmd %s raised exception:\n%s" name (exn.ToString()))
|
||||
reraise()
|
||||
dispatch msg)
|
||||
|
||||
let canExecute =
|
||||
Func<obj, bool>(fun cmdParameter ->
|
||||
if debug then console.log (sprintf "view: checking if cmd %s can execute" name)
|
||||
if debug then Trace.WriteLine (sprintf "view: checking if cmd %s can execute" name)
|
||||
canExec cmdParameter model)
|
||||
Xamarin.Forms.Command (execute, canExecute)
|
||||
|
||||
|
@ -73,7 +74,7 @@ and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBind
|
|||
| BindTwoWay (getter, setter) -> name, GetSet (getter, setter)
|
||||
| BindTwoWayValidation (getter, setter) -> name, GetSetValidate (getter, setter)
|
||||
| BindCmd (exec, canExec) -> name, Cmd (toCommand name (exec, canExec))
|
||||
| BindSubModel (ViewSubModel (_, subName, getter, toMsg, propMap)) -> name, SubModel (getter, toMsg, ViewModel<obj, obj>(getter model, toMsg >> dispatch, propMap, debug))
|
||||
| BindSubModel (ViewSubModel (_, subName, getter, toMsg, propMap)) -> name, SubModel (getter, toMsg, StaticViewModel<obj, obj>(getter model, toMsg >> dispatch, propMap, debug))
|
||||
| BindMap (getter, mapper) -> name, Map (getter, mapper)
|
||||
|
||||
do propMap |> List.map convert |> List.iter props.Add
|
||||
|
@ -89,7 +90,7 @@ and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBind
|
|||
member __.ErrorsChanged = errorsChanged.Publish
|
||||
member __.HasErrors = errors.Count > 0
|
||||
member __.GetErrors propName =
|
||||
if debug then console.log (sprintf "Getting errors for %s" propName)
|
||||
if debug then Trace.WriteLine (sprintf "Getting errors for %s" propName)
|
||||
let results =
|
||||
match errors.TryGetValue propName with
|
||||
| true, errs -> errs
|
||||
|
@ -99,8 +100,8 @@ and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBind
|
|||
/// Used internally to update the model. Only properties that have changed are updated.
|
||||
member __.UpdateModel (other: 'model) : unit =
|
||||
if Object.ReferenceEquals (model, other) then
|
||||
if debug then console.log (sprintf "...Skipping update because model is reference-identical")
|
||||
//if debug then console.log (sprintf "UpdateModel %+A" (props.Keys |> Seq.toArray))
|
||||
if debug then Trace.WriteLine (sprintf "...Skipping update because model is reference-identical")
|
||||
//if debug then Trace.WriteLine (sprintf "UpdateModel %+A" (props.Keys |> Seq.toArray))
|
||||
let propDiff name prop =
|
||||
match prop with
|
||||
| Get getter
|
||||
|
@ -143,15 +144,15 @@ and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBind
|
|||
| Map (getter, mapper) -> getter model |> mapper
|
||||
| Set setter -> invalidOp (sprintf "Prop Binding Not Settable: %s" name)
|
||||
|
||||
if debug then console.log (sprintf "view: got %s = %+A" name value)
|
||||
if debug then Trace.WriteLine (sprintf "view: got %s = %+A" name value)
|
||||
value
|
||||
|
||||
else
|
||||
if debug then console.log (sprintf "view: failed to get property %s" name)
|
||||
if debug then Trace.WriteLine (sprintf "view: failed to get property %s" name)
|
||||
invalidOp (sprintf "Prop Binding Not Set: %s" name)
|
||||
|
||||
and set (name : string) (value : obj) : unit =
|
||||
if debug then console.log (sprintf "view: set %s to %+A" name value)
|
||||
if debug then Trace.WriteLine (sprintf "view: set %s to %+A" name value)
|
||||
|
||||
if props.ContainsKey name then
|
||||
|
||||
|
@ -177,16 +178,16 @@ and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBind
|
|||
| false, _ -> errors.Add(name, [err])
|
||||
errorsChanged()
|
||||
| _ ->
|
||||
if debug then console.log (sprintf "view: failed to set read-only property %s" name)
|
||||
if debug then Trace.WriteLine (sprintf "view: failed to set read-only property %s" name)
|
||||
invalidOp "Unable to set read-only member"
|
||||
else
|
||||
if debug then console.log (sprintf "view: failed to set unbound property %s" name)
|
||||
if debug then Trace.WriteLine (sprintf "view: failed to set unbound property %s" name)
|
||||
invalidOp (sprintf "Prop Binding Not Set: %s" name)
|
||||
|
||||
|
||||
/// Used by the view to try to get values
|
||||
override this.TryGetMember (binder, result) =
|
||||
if debug then console.log (sprintf "view: TryGetMember %s" binder.Name)
|
||||
if debug then Trace.WriteLine (sprintf "view: TryGetMember %s" binder.Name)
|
||||
if props.ContainsKey binder.Name then
|
||||
let v = this.[binder.Name]
|
||||
result <- v
|
||||
|
@ -195,7 +196,7 @@ and ViewModel<'model, 'msg>(m: 'model, dispatch: 'msg -> unit, propMap: ViewBind
|
|||
|
||||
/// Used by the view to try to set values
|
||||
override this.TrySetMember (binder, value) =
|
||||
if debug then console.log (sprintf "view: TrySetMember %s" binder.Name)
|
||||
if debug then Trace.WriteLine (sprintf "view: TrySetMember %s" binder.Name)
|
||||
if props.ContainsKey binder.Name then
|
||||
this.[binder.Name] <- value
|
||||
false
|
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Cecil" Version="0.10.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,578 @@
|
|||
// dotnet build Generator\Generator.csproj && dotnet Generator\bin\Debug\netcoreapp2.0\Generator.dll Generator\bindings.json Elmish.XamarinForms\DynamicXaml.fs && fsc -a -r:packages\androidapp\Xamarin.Forms\lib\netstandard1.0\Xamarin.Forms.Core.dll Elmish.XamarinForms\DynamicXamlConverters.fs Elmish.XamarinForms\DynamicXaml.fs
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
class Bindings
|
||||
{
|
||||
// Input
|
||||
public List<string> Assemblies { get; set; }
|
||||
public List<TypeBinding> Types { get; set; }
|
||||
public string OutputNamespace { get; set; }
|
||||
|
||||
// Output
|
||||
public List<AssemblyDefinition> AssemblyDefinitions { get; set; }
|
||||
|
||||
public TypeDefinition GetTypeDefinition(string name) =>
|
||||
(from a in AssemblyDefinitions
|
||||
from m in a.Modules
|
||||
from tdef in m.Types
|
||||
where tdef.FullName == name
|
||||
select tdef).First();
|
||||
|
||||
public TypeBinding FindType (string name) => Types.FirstOrDefault (x => x.Name == name);
|
||||
}
|
||||
|
||||
class TypeBinding
|
||||
{
|
||||
// Input
|
||||
public string Name { get; set; }
|
||||
public List<MemberBinding> Members { get; set; }
|
||||
|
||||
// Output
|
||||
public string BoundCode { get; set; }
|
||||
public TypeDefinition Definition { get; set; }
|
||||
|
||||
public string BoundName => "XamlElement"; // Definition.Name + "Description";
|
||||
}
|
||||
|
||||
class MemberBinding
|
||||
{
|
||||
// Input
|
||||
public string Name { get; set; }
|
||||
public string UniqueName { get; set; }
|
||||
public string ShortName { get; set; }
|
||||
public string Default { get; set; }
|
||||
public string Equality { get; set; }
|
||||
public string Conv { get; set; }
|
||||
public string ModelType { get; set; }
|
||||
public string ElementType { get; set; }
|
||||
public string InputType { get; set; }
|
||||
public List<MemberBinding> Attached { get; set; }
|
||||
|
||||
// Output
|
||||
public MemberReference Definition { get; set; }
|
||||
|
||||
public TypeReference BoundType =>
|
||||
(Definition is PropertyDefinition p)
|
||||
? p.PropertyType
|
||||
: ((EventDefinition)Definition).EventType;
|
||||
|
||||
public string BoundUniqueName => string.IsNullOrEmpty(UniqueName) ? Name : UniqueName;
|
||||
public string LowerBoundUniqueName => char.ToLowerInvariant (BoundUniqueName[0]) + BoundUniqueName.Substring (1);
|
||||
public string BoundShortName => string.IsNullOrEmpty(ShortName) ? Name : ShortName;
|
||||
public string LowerBoundShortName => char.ToLowerInvariant(BoundShortName[0]) + BoundShortName.Substring(1);
|
||||
public string GetInputType(Bindings bindings, IEnumerable<Tuple<TypeReference, TypeDefinition>> hierarchy)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(InputType))
|
||||
{
|
||||
return InputType;
|
||||
}
|
||||
return this.GetModelType(bindings, hierarchy);
|
||||
}
|
||||
public string GetModelType(Bindings bindings, IEnumerable<Tuple<TypeReference, TypeDefinition>> hierarchy)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ModelType))
|
||||
{
|
||||
return ModelType;
|
||||
}
|
||||
return GetModelTypeInner(bindings, this.BoundType, hierarchy);
|
||||
}
|
||||
public static string GetModelTypeInner(Bindings bindings, TypeReference tref, IEnumerable<Tuple<TypeReference, TypeDefinition>> hierarchy)
|
||||
{
|
||||
if (tref.IsGenericParameter)
|
||||
{
|
||||
if (hierarchy != null)
|
||||
{
|
||||
var r = Program.ResolveGenericParameter(tref, hierarchy);
|
||||
return GetModelTypeInner(bindings, r, hierarchy);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "XamlElement";
|
||||
}
|
||||
}
|
||||
if (tref.IsGenericInstance)
|
||||
{
|
||||
var n = tref.Name.Substring(0, tref.Name.IndexOf('`'));
|
||||
var ns = tref.Namespace;
|
||||
if (tref.IsNested)
|
||||
{
|
||||
n = tref.DeclaringType.Name + "." + n;
|
||||
ns = tref.DeclaringType.Namespace;
|
||||
}
|
||||
var args = string.Join(", ", ((GenericInstanceType)tref).GenericArguments.Select(s => GetModelTypeInner(bindings, s, hierarchy)));
|
||||
return $"{ns}.{n}<{args}>";
|
||||
}
|
||||
switch (tref.FullName)
|
||||
{
|
||||
case "System.String": return "string";
|
||||
case "System.Boolean": return "bool";
|
||||
case "System.Int32": return "int";
|
||||
case "System.Double": return "double";
|
||||
case "System.Single": return "single";
|
||||
default:
|
||||
if (bindings.Types.FirstOrDefault(x => x.Name == tref.FullName) is TypeBinding tb)
|
||||
return tb.BoundName;
|
||||
return tref.FullName.Replace('/', '.');
|
||||
}
|
||||
}
|
||||
public string GetElementType(IEnumerable<Tuple<TypeReference, TypeDefinition>> hierarchy)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ElementType))
|
||||
{
|
||||
return ElementType;
|
||||
}
|
||||
return GetElementTypeInner(this.BoundType, hierarchy);
|
||||
}
|
||||
static string GetElementTypeInner(TypeReference tref, IEnumerable<Tuple<TypeReference, TypeDefinition>> hierarchy)
|
||||
{
|
||||
var r = Program.ResolveGenericParameter(tref, hierarchy);
|
||||
if (r == null)
|
||||
return null;
|
||||
if (r.FullName == "System.String")
|
||||
return null;
|
||||
if (r.Name == "IList`1" && r.IsGenericInstance)
|
||||
{
|
||||
var args = ((GenericInstanceType)r).GenericArguments;
|
||||
var elementType = Program.ResolveGenericParameter(args[0], hierarchy);
|
||||
return elementType.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
var bs = r.Resolve().Interfaces;
|
||||
return bs.Select(b => GetElementTypeInner(b.InterfaceType, hierarchy)).FirstOrDefault(b => b != null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
try {
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.Error.WriteLine("usage: generator <outputPath>");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
var bindingsPath = args[0];
|
||||
var outputPath = args[1];
|
||||
|
||||
var bindings = JsonConvert.DeserializeObject<Bindings> (File.ReadAllText (bindingsPath));
|
||||
|
||||
bindings.AssemblyDefinitions = bindings.Assemblies.Select(LoadAssembly).ToList();
|
||||
|
||||
foreach (var x in bindings.Types)
|
||||
x.Definition = bindings.GetTypeDefinition (x.Name);
|
||||
foreach (var x in bindings.Types) {
|
||||
foreach (var m in x.Members) {
|
||||
if (FindProperty (m.Name, x.Definition) is PropertyDefinition p) {
|
||||
m.Definition = p;
|
||||
}
|
||||
else if (FindEvent (m.Name, x.Definition) is EventDefinition e) {
|
||||
m.Definition = e;
|
||||
}
|
||||
else {
|
||||
throw new Exception ($"Could not find member `{m.Name}`");
|
||||
}
|
||||
}
|
||||
}
|
||||
var code = BindTypes (bindings);
|
||||
|
||||
File.WriteAllText (outputPath, code);
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
System.Console.WriteLine(ex);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static string BindTypes (Bindings bindings)
|
||||
{
|
||||
var w = new StringWriter();
|
||||
var head = "";
|
||||
|
||||
w.WriteLine("namespace rec " + bindings.OutputNamespace);
|
||||
w.WriteLine();
|
||||
w.WriteLine("#nowarn \"67\" // cast always holds");
|
||||
w.WriteLine();
|
||||
w.WriteLine("open System");
|
||||
w.WriteLine("open System.Diagnostics");
|
||||
w.WriteLine();
|
||||
w.WriteLine($"/// A description of a visual element");
|
||||
w.WriteLine($"[<AllowNullLiteral>]");
|
||||
w.WriteLine($"type XamlElement(targetType: Type, create: (unit -> obj), apply: (XamlElement option -> XamlElement -> obj -> unit), attribs: Map<string, obj>) = ");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Get the type created by the visual element");
|
||||
w.WriteLine($" member x.TargetType = targetType");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Get the attributes of the visual element");
|
||||
w.WriteLine($" [<DebuggerBrowsable(DebuggerBrowsableState.RootHidden)>]");
|
||||
w.WriteLine($" member x.Attributes = attribs");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Apply the description to a visual element");
|
||||
w.WriteLine($" member x.Apply (target: obj) = apply None x target");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Apply a different description to a similar visual element");
|
||||
w.WriteLine($" [<DebuggerBrowsable(DebuggerBrowsableState.Never)>]");
|
||||
w.WriteLine($" member x.ApplyMethod = apply");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Incrementally apply a description to a visual element");
|
||||
w.WriteLine($" member x.ApplyIncremental(prev: XamlElement, target: obj) = apply (Some prev) x target");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Apply a different description to a similar visual element");
|
||||
w.WriteLine($" [<DebuggerBrowsable(DebuggerBrowsableState.Never)>]");
|
||||
w.WriteLine($" member x.CreateMethod = create");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Produce a new visual element with an adjusted attribute");
|
||||
w.WriteLine($" member x.WithAttribute(name: string, value: obj) = XamlElement(targetType, create, apply, x.Attributes.Add(name, value))");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Produce a visual element from a visual element for a different type");
|
||||
w.WriteLine($" member x.Inherit(newTargetType, newCreate, newApply, newAttribs) = ");
|
||||
w.WriteLine($" let combinedAttribs = Map.ofArray(Array.append(Map.toArray attribs) newAttribs)");
|
||||
w.WriteLine($" XamlElement(newTargetType, newCreate, (fun prevOpt source target -> apply prevOpt source target; newApply prevOpt source target), combinedAttribs)");
|
||||
w.WriteLine();
|
||||
|
||||
w.WriteLine($" /// Produce a new visual element with an adjusted attribute");
|
||||
w.WriteLine($"[<AutoOpen>]");
|
||||
w.WriteLine($"module XamlElementExtensions = ");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" type XamlElement with");
|
||||
w.WriteLine($" /// Create the UI element from the view description");
|
||||
w.WriteLine($" member x.Create() : obj =");
|
||||
w.WriteLine($" let target = x.CreateMethod()");
|
||||
w.WriteLine($" x.Apply(target)");
|
||||
w.WriteLine($" target");
|
||||
foreach (var type in bindings.Types)
|
||||
{
|
||||
var tdef = type.Definition;
|
||||
var hierarchy = GetHierarchy(type.Definition).ToList();
|
||||
var ctor = tdef.Methods
|
||||
.Where(x => x.IsConstructor && x.IsPublic)
|
||||
.OrderBy(x => x.Parameters.Count)
|
||||
.FirstOrDefault();
|
||||
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Create a {tdef.FullName} from the view description");
|
||||
w.WriteLine($" member x.CreateAs{tdef.Name}() : {tdef.FullName} = (x.Create() :?> {tdef.FullName})");
|
||||
}
|
||||
var allMembersInAllTypes = new List<MemberBinding>();
|
||||
foreach (var type in bindings.Types)
|
||||
{
|
||||
if (type.Members != null)
|
||||
{
|
||||
foreach (var y in type.Members)
|
||||
{
|
||||
allMembersInAllTypes.Add(y);
|
||||
if (y.Attached != null)
|
||||
{
|
||||
foreach (var ap in y.Attached)
|
||||
allMembersInAllTypes.Add(ap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var allMembersInAllTypesGroupedByName = allMembersInAllTypes.GroupBy(y => y.BoundUniqueName);
|
||||
/* foreach (var ms in allMembersInAllTypesGroupedByName)
|
||||
{
|
||||
var m = ms.First();
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Get the {m.BoundUniqueName} property in the visual element");
|
||||
w.WriteLine(" member x." + m.BoundUniqueName + " = match x.Attributes.TryFind(\"" + m.BoundUniqueName + "\") with Some v -> unbox<" + GetModelType(bindings, m.BoundType, null) + ">(v) | None -> " + m.Default);
|
||||
}
|
||||
*/
|
||||
foreach (var ms in allMembersInAllTypesGroupedByName)
|
||||
{
|
||||
var m = ms.First();
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Try to get the {m.BoundUniqueName} property in the visual element");
|
||||
var modelType = m.GetModelType(bindings, null);
|
||||
w.WriteLine(" member x.Try" + m.BoundUniqueName + " = match x.Attributes.TryFind(\"" + m.BoundUniqueName + "\") with Some v -> Some(unbox<" + modelType + ">(v)) | None -> None");
|
||||
}
|
||||
foreach (var ms in allMembersInAllTypesGroupedByName)
|
||||
{
|
||||
var m = ms.First();
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Adjusts the {m.BoundUniqueName} property in the visual element");
|
||||
var conv = string.IsNullOrWhiteSpace(m.Conv) ? "" : m.Conv;
|
||||
var inputType = m.GetInputType(bindings, null);
|
||||
w.WriteLine(" member x." + m.BoundUniqueName + "(value: " + inputType + ") = XamlElement(x.TargetType, x.CreateMethod, x.ApplyMethod, x.Attributes.Add(\"" + m.BoundUniqueName + "\", box (" + conv + "(value))))");
|
||||
}
|
||||
w.WriteLine();
|
||||
foreach (var ms in allMembersInAllTypesGroupedByName)
|
||||
{
|
||||
var m = ms.First();
|
||||
var inputType = m.GetInputType(bindings, null);
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Adjusts the {m.BoundUniqueName} property in the visual element");
|
||||
w.WriteLine(" let with" + m.BoundUniqueName + " (value: " + inputType + ") (x: XamlElement) = x." + m.BoundUniqueName + "(value)");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Adjusts the {m.BoundUniqueName} property in the visual element");
|
||||
w.WriteLine(" let " + m.LowerBoundUniqueName + " (value: " + inputType + ") (x: XamlElement) = x." + m.BoundUniqueName + "(value)");
|
||||
}
|
||||
w.WriteLine();
|
||||
w.WriteLine("type Xaml() =");
|
||||
foreach (var type in bindings.Types)
|
||||
{
|
||||
var tdef = type.Definition;
|
||||
var hierarchy = GetHierarchy(type.Definition).ToList();
|
||||
var boundHierarchy =
|
||||
hierarchy.Select(x => bindings.Types.FirstOrDefault(y => y.Name == x.Item2.FullName))
|
||||
.Where(x => x != null)
|
||||
.ToList();
|
||||
|
||||
|
||||
var baseType = boundHierarchy.Count > 1 ? boundHierarchy[1] : null;
|
||||
|
||||
// All properties and events apart from the attached ones
|
||||
var allDirectMembers = (from x in boundHierarchy from y in x.Members select y).ToList();
|
||||
|
||||
// Emit the constructor
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Describes a {tdef.Name} in the view");
|
||||
w.Write($" static member {tdef.Name}(");
|
||||
head = "";
|
||||
foreach (var m in allDirectMembers)
|
||||
{
|
||||
var inputType = m.GetInputType(bindings, null);
|
||||
|
||||
w.Write($"{head}?{m.LowerBoundShortName}: {inputType}");
|
||||
head = ", ";
|
||||
}
|
||||
w.WriteLine($") = ");
|
||||
w.WriteLine($" let attribs = [| ");
|
||||
foreach (var m in allDirectMembers)
|
||||
{
|
||||
var conv = string.IsNullOrWhiteSpace(m.Conv) ? "" : m.Conv;
|
||||
w.WriteLine(" match " + m.LowerBoundShortName + " with None -> () | Some v -> yield (\"" + m.BoundUniqueName + "\"" + $", box (" + conv + "(v))) ");
|
||||
}
|
||||
w.WriteLine($" |]");
|
||||
|
||||
var ctor = tdef.Methods
|
||||
.Where(x => x.IsConstructor && x.IsPublic)
|
||||
.OrderBy(x => x.Parameters.Count)
|
||||
.FirstOrDefault();
|
||||
|
||||
w.WriteLine();
|
||||
w.WriteLine($" let create () =");
|
||||
if (!tdef.IsAbstract && ctor != null && ctor.Parameters.Count == 0)
|
||||
{
|
||||
w.WriteLine($" box (new {tdef.FullName}())");
|
||||
}
|
||||
else
|
||||
{
|
||||
w.WriteLine($" failwith \"can'tdef create {tdef.FullName}\"");
|
||||
}
|
||||
w.WriteLine();
|
||||
w.WriteLine($" let apply (prevOpt: XamlElement option) (source: XamlElement) (target:obj) = ");
|
||||
|
||||
if (baseType == null && type.Members.Count() == 0)
|
||||
{
|
||||
w.WriteLine($" ()");
|
||||
}
|
||||
else
|
||||
{
|
||||
w.WriteLine($" let target = (target :?> {tdef.FullName})");
|
||||
foreach (var m in allDirectMembers)
|
||||
{
|
||||
var bt = ResolveGenericParameter(m.BoundType, hierarchy);
|
||||
string elementType = m.GetElementType(hierarchy);
|
||||
if (elementType != null && elementType != "obj")
|
||||
{
|
||||
w.WriteLine($" match source.Try{m.BoundUniqueName} with");
|
||||
w.WriteLine($" | Some coll when coll <> null && coll.Length > 0 ->");
|
||||
w.WriteLine($" if (coll = null || coll.Length = 0) then");
|
||||
w.WriteLine($" match target.{m.Name} with");
|
||||
w.WriteLine($" | null -> ()");
|
||||
w.WriteLine($" | targetColl -> targetColl.Clear() ");
|
||||
w.WriteLine($" else");
|
||||
w.WriteLine($" // Remove the excess children");
|
||||
w.WriteLine($" while (target.{m.Name}.Count > coll.Length) do");
|
||||
w.WriteLine($" target.{m.Name}.RemoveAt(target.{m.Name}.Count - 1)");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" // Count the existing children");
|
||||
w.WriteLine($" let n = target.{m.Name}.Count;");
|
||||
w.WriteLine();
|
||||
w.WriteLine($" // Adjust the existing children and create the new children");
|
||||
w.WriteLine($" for i in 0 .. coll.Length-1 do");
|
||||
w.WriteLine($" let newChild = coll.[i]");
|
||||
w.WriteLine($" let prevChildOpt = match prevOpt with None -> None | Some prev -> match prev.Try{m.BoundUniqueName} with None -> None | Some coll when i < coll.Length && i < n -> Some coll.[i] | _ -> None");
|
||||
w.WriteLine($" let prevChildOpt, targetChild = ");
|
||||
w.WriteLine($" if (match prevChildOpt with None -> true | Some prevChild -> not (obj.ReferenceEquals(prevChild, newChild))) then");
|
||||
w.WriteLine($" let mustCreate = (i >= n || match prevChildOpt with None -> true | Some prevChild -> newChild.TargetType <> prevChild.TargetType)");
|
||||
w.WriteLine($" if mustCreate then");
|
||||
w.WriteLine($" let targetChild = newChild.CreateAs{elementType}()");
|
||||
w.WriteLine($" if i >= n then");
|
||||
w.WriteLine($" target.{m.Name}.Insert(i, targetChild)");
|
||||
w.WriteLine($" else");
|
||||
w.WriteLine($" target.{m.Name}.[i] <- targetChild");
|
||||
w.WriteLine($" None, targetChild");
|
||||
w.WriteLine($" else");
|
||||
w.WriteLine($" let targetChild = target.{m.Name}.[i]");
|
||||
w.WriteLine($" newChild.ApplyIncremental(prevChildOpt.Value, targetChild)");
|
||||
w.WriteLine($" prevChildOpt, targetChild");
|
||||
w.WriteLine($" else");
|
||||
w.WriteLine($" prevChildOpt, target.{m.Name}.[i]");
|
||||
if (m.Attached != null)
|
||||
{
|
||||
foreach (var ap in m.Attached)
|
||||
{
|
||||
w.WriteLine($" // Adjust the attached properties");
|
||||
w.WriteLine($" match (match prevChildOpt with None -> None | Some prevChild -> prevChild.Try{ap.BoundUniqueName}), newChild.Try{ap.BoundUniqueName} with");
|
||||
w.WriteLine($" | Some prev, Some v when prev = v -> ()");
|
||||
w.WriteLine($" | _, Some v -> {tdef.FullName}.Set{ap.Name}(targetChild, v)");
|
||||
w.WriteLine($" | Some _, None -> {tdef.FullName}.Set{ap.Name}(targetChild, {ap.Default}) // TODO: not always perfect, should set back to original default?");
|
||||
w.WriteLine($" | _ -> ()");
|
||||
}
|
||||
}
|
||||
w.WriteLine($" ()");
|
||||
w.WriteLine($" | _ -> ()");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bindings.FindType(bt.FullName) is TypeBinding b)
|
||||
{
|
||||
if (bt.IsValueType)
|
||||
{
|
||||
w.WriteLine($" let prevChildOpt = match prevOpt with None -> None | Some prev -> prev.Try{m.BoundUniqueName}");
|
||||
w.WriteLine($" match prevChildOpt, source.Try{m.BoundUniqueName} with");
|
||||
w.WriteLine($" // For structured objects, the only caching is based on reference equality");
|
||||
w.WriteLine($" | Some prevChild, Some newChild when obj.ReferenceEquals(prevChild, newChild) -> ()");
|
||||
w.WriteLine($" | _, Some newChild ->");
|
||||
w.WriteLine($" target.{m.Name} <- newChild.CreateAs{bt.Name}()");
|
||||
w.WriteLine($" | _, None ->");
|
||||
w.WriteLine($" target.{m.Name} <- Unchecked.defaultof<_>");
|
||||
}
|
||||
else
|
||||
{
|
||||
w.WriteLine($" let prevChildOpt = match prevOpt with None -> None | Some prev -> prev.Try{m.BoundUniqueName}");
|
||||
w.WriteLine($" match prevChildOpt, source.Try{m.BoundUniqueName} with");
|
||||
w.WriteLine($" // For structured objects, the only caching is based on reference equality");
|
||||
w.WriteLine($" | Some prevChild, Some newChild when obj.ReferenceEquals(prevChild, newChild) -> ()");
|
||||
w.WriteLine($" | Some prevChild, Some newChild ->");
|
||||
w.WriteLine($" newChild.ApplyIncremental(prevChild, target.{m.Name})");
|
||||
w.WriteLine($" | None, Some newChild ->");
|
||||
w.WriteLine($" target.{m.Name} <- newChild.CreateAs{bt.Name}()");
|
||||
w.WriteLine($" | _, None ->");
|
||||
w.WriteLine($" target.{m.Name} <- null;");
|
||||
}
|
||||
}
|
||||
else if (bt.Name.EndsWith("Handler") || bt.Name.EndsWith("Handler`1") || bt.Name.EndsWith("Handler`2"))
|
||||
{
|
||||
w.WriteLine($" let prevValueOpt = match prevOpt with None -> None | Some prev -> prev.Try{m.BoundUniqueName}");
|
||||
w.WriteLine($" match prevValueOpt, source.Try{m.BoundUniqueName} with");
|
||||
w.WriteLine($" | Some prevValue, Some value when prevValue = value -> ()");
|
||||
w.WriteLine($" | Some prevValue, Some value -> target.{m.Name}.RemoveHandler(prevValue); target.{m.Name}.AddHandler(value)");
|
||||
w.WriteLine($" | None, Some value -> target.{m.Name}.AddHandler(value)");
|
||||
w.WriteLine($" | Some prevValue, None -> target.{m.Name}.RemoveHandler(prevValue)");
|
||||
w.WriteLine($" | None, None -> ()");
|
||||
}
|
||||
else
|
||||
{
|
||||
w.WriteLine($" let prevValueOpt = match prevOpt with None -> None | Some prev -> prev.Try{m.BoundUniqueName}");
|
||||
w.WriteLine($" match prevValueOpt, source.Try{m.BoundUniqueName} with");
|
||||
w.WriteLine($" | Some prevValue, Some value when prevValue = value -> ()");
|
||||
w.WriteLine($" | _, Some value -> target.{m.Name} <- value");
|
||||
w.WriteLine($" | Some _, None -> target.{m.Name} <- {m.Default} // TODO: not always perfect, should set back to original default?");
|
||||
w.WriteLine($" | None, None -> ()");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteLine($" new XamlElement(typeof<{tdef.FullName}>, create, apply, Map.ofArray attribs)");
|
||||
|
||||
}
|
||||
w.WriteLine($"[<AutoOpen>]");
|
||||
w.WriteLine($"module XamlCreateExtensions = ");
|
||||
foreach (var type in bindings.Types)
|
||||
{
|
||||
var tdef = type.Definition;
|
||||
var hierarchy = GetHierarchy(type.Definition).ToList();
|
||||
var boundHierarchy = hierarchy.Select(x => bindings.Types.FirstOrDefault(y => y.Name == x.Item2.FullName))
|
||||
.Where(x => x != null)
|
||||
.ToList();
|
||||
|
||||
var ctor = tdef.Methods
|
||||
.Where(x => x.IsConstructor && x.IsPublic)
|
||||
.OrderBy(x => x.Parameters.Count)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (!tdef.IsAbstract && ctor != null && ctor.Parameters.Count == 0)
|
||||
{
|
||||
w.WriteLine();
|
||||
w.WriteLine($" /// Specifies a {tdef.Name} in the view description, initially with default attributes");
|
||||
w.WriteLine($" let {Char.ToLower(tdef.Name[0])}{tdef.Name.Substring(1)} = Xaml.{tdef.Name}()");
|
||||
}
|
||||
}
|
||||
return w.ToString ();
|
||||
}
|
||||
|
||||
static public TypeReference ResolveGenericParameter (TypeReference tref, IEnumerable<Tuple<TypeReference, TypeDefinition>> hierarchy)
|
||||
{
|
||||
if (tref == null)
|
||||
return null;
|
||||
if (!tref.IsGenericParameter)
|
||||
return tref;
|
||||
var q =
|
||||
from b in hierarchy where b.Item1.IsGenericInstance
|
||||
let ps = b.Item2.GenericParameters
|
||||
let p = ps.FirstOrDefault(x => x.Name == tref.Name)
|
||||
where p != null
|
||||
let pi = ps.IndexOf(p)
|
||||
let args = ((GenericInstanceType)b.Item1).GenericArguments
|
||||
select ResolveGenericParameter (args[pi], hierarchy);
|
||||
return q.First ();
|
||||
}
|
||||
|
||||
|
||||
static PropertyDefinition FindProperty(string name, TypeDefinition type)
|
||||
{
|
||||
var q =
|
||||
from tdef in GetHierarchy(type)
|
||||
from p in tdef.Item2.Properties
|
||||
where p.Name == name
|
||||
select p;
|
||||
return q.FirstOrDefault ();
|
||||
}
|
||||
|
||||
static EventDefinition FindEvent(string name, TypeDefinition type)
|
||||
{
|
||||
var q =
|
||||
from tdef in GetHierarchy(type)
|
||||
from p in tdef.Item2.Events
|
||||
where p.Name == name
|
||||
select p;
|
||||
return q.FirstOrDefault ();
|
||||
}
|
||||
|
||||
static IEnumerable<Tuple<TypeReference, TypeDefinition>> GetHierarchy (TypeDefinition type)
|
||||
{
|
||||
var d = type;
|
||||
yield return Tuple.Create ((TypeReference)d, d);
|
||||
while (d.BaseType != null) {
|
||||
var r = d.BaseType;
|
||||
d = r.Resolve();
|
||||
yield return Tuple.Create (r, d);
|
||||
}
|
||||
}
|
||||
|
||||
static AssemblyDefinition LoadAssembly (string path)
|
||||
{
|
||||
if (path.StartsWith("packages")) {
|
||||
var user = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
path = Path.Combine (user, ".nuget", path);
|
||||
}
|
||||
return AssemblyDefinition.ReadAssembly(path);
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -55,6 +55,7 @@ type AllControls () =
|
|||
( "White", Color.White ); ( "Yellow", Color.Yellow ) ]
|
||||
|
||||
let view (model: Model) dispatch =
|
||||
Xaml.ContentPage(
|
||||
Xaml.ScrollView(
|
||||
Xaml.StackLayout(padding=20.0,
|
||||
children=[
|
||||
|
@ -127,7 +128,6 @@ type AllControls () =
|
|||
// Xaml.BoxView: TODO
|
||||
// Xaml.Cell, EntryCell: TODO
|
||||
// Xaml.CarouselPage: TODO
|
||||
// Xaml.ContentPage: TODO
|
||||
// Xaml.MasterDetailPage: TODO
|
||||
// Xaml.Menu: TODO
|
||||
// Xaml.MenuItem: TODO
|
||||
|
@ -149,13 +149,13 @@ type AllControls () =
|
|||
Xaml.ListView(itemsSource= ["Ionide"; "Visual Studio"; "Emacs"; "Visual Studio Code"; "Rider"], horizontalOptions=LayoutOptions.CenterAndExpand,
|
||||
itemSelected=(fun item -> dispatch (ListViewSelectedItemChanged item)))
|
||||
|
||||
]))
|
||||
])))
|
||||
|
||||
do
|
||||
let page =
|
||||
Program.mkSimple init update
|
||||
(fun _ _ -> HelperPage(), [], view)
|
||||
Program.mkSimple init update view
|
||||
|> Program.withConsoleTrace
|
||||
|> Program.runDynamicView
|
||||
|> Program.withDynamicView
|
||||
|> Program.run
|
||||
|
||||
base.MainPage <- page
|
||||
|
|
|
@ -10,15 +10,16 @@
|
|||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Droid</RootNamespace>
|
||||
<AssemblyName>Droid</AssemblyName>
|
||||
<TargetFrameworkVersion>v7.1</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
|
||||
<AndroidApplication>True</AndroidApplication>
|
||||
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
|
||||
<AndroidResgenClass>Resource</AndroidResgenClass>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||
<AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk>
|
||||
<SelectedDevice>Google Pixel 2</SelectedDevice>
|
||||
<DefaultDevice>new_device</DefaultDevice>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
|
|
@ -29,8 +29,9 @@ type CounterApp () =
|
|||
| SetStep n -> { model with Step = n }
|
||||
|
||||
let view (model: Model) dispatch =
|
||||
Xaml.StackLayout(padding=20.0,
|
||||
children=[
|
||||
Xaml.ContentPage(
|
||||
content=Xaml.StackLayout(padding=20.0,
|
||||
children=[
|
||||
yield Xaml.StackLayout(padding=20.0, verticalOptions=LayoutOptions.Center,
|
||||
children=[
|
||||
Xaml.Label(text= sprintf "%d" model.Count, horizontalOptions=LayoutOptions.Center, textColor=Color.Blue)
|
||||
|
@ -42,13 +43,13 @@ type CounterApp () =
|
|||
])
|
||||
if model <> init() then
|
||||
yield Xaml.Button(text="Reset", horizontalOptions=LayoutOptions.Center, command= (fun () -> dispatch Reset))
|
||||
])
|
||||
]))
|
||||
|
||||
do
|
||||
let page =
|
||||
Program.mkSimple init update
|
||||
(fun _ _ -> HelperPage(), [], view)
|
||||
Program.mkSimple init update view
|
||||
|> Program.withConsoleTrace
|
||||
|> Program.runDynamicView
|
||||
|> Program.withDynamicView
|
||||
|> Program.run
|
||||
|
||||
base.MainPage <- page
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace CounterApp
|
|||
|
||||
open Elmish
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.StaticViews
|
||||
open Xamarin.Forms
|
||||
|
||||
type Model =
|
||||
|
@ -39,8 +40,9 @@ type CounterApp () =
|
|||
|
||||
do
|
||||
let page =
|
||||
Program.mkSimple init update (fun _ _ -> view())
|
||||
Program.mkSimple init update view
|
||||
|> Program.withConsoleTrace
|
||||
|> Program.withStaticView
|
||||
|> Program.run
|
||||
|
||||
base.MainPage <- page
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.props" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.props')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.props" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
|
@ -46,22 +46,22 @@
|
|||
</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.FSharp.targets" Condition="Exists('$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.FSharp.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Compat\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Compat\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Core.UI\build\MonoAndroid70\Xamarin.Android.Support.Core.UI.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Core.UI\build\MonoAndroid70\Xamarin.Android.Support.Core.UI.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Core.Utils\build\MonoAndroid70\Xamarin.Android.Support.Core.Utils.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Core.Utils\build\MonoAndroid70\Xamarin.Android.Support.Core.Utils.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Media.Compat\build\MonoAndroid70\Xamarin.Android.Support.Media.Compat.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Media.Compat\build\MonoAndroid70\Xamarin.Android.Support.Media.Compat.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Fragment\build\MonoAndroid70\Xamarin.Android.Support.Fragment.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Fragment\build\MonoAndroid70\Xamarin.Android.Support.Fragment.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v4\build\MonoAndroid70\Xamarin.Android.Support.v4.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v4\build\MonoAndroid70\Xamarin.Android.Support.v4.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Transition\build\MonoAndroid70\Xamarin.Android.Support.Transition.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Transition\build\MonoAndroid70\Xamarin.Android.Support.Transition.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.CardView\build\MonoAndroid70\Xamarin.Android.Support.v7.CardView.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.CardView\build\MonoAndroid70\Xamarin.Android.Support.v7.CardView.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.Palette\build\MonoAndroid70\Xamarin.Android.Support.v7.Palette.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.Palette\build\MonoAndroid70\Xamarin.Android.Support.v7.Palette.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.RecyclerView\build\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.RecyclerView\build\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Animated.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Animated.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.AppCompat\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.AppCompat\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Design\build\MonoAndroid70\Xamarin.Android.Support.Design.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Design\build\MonoAndroid70\Xamarin.Android.Support.Design.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.MediaRouter\build\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.MediaRouter\build\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Compat\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Compat\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Core.UI\build\MonoAndroid70\Xamarin.Android.Support.Core.UI.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Core.UI\build\MonoAndroid70\Xamarin.Android.Support.Core.UI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Core.Utils\build\MonoAndroid70\Xamarin.Android.Support.Core.Utils.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Core.Utils\build\MonoAndroid70\Xamarin.Android.Support.Core.Utils.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Media.Compat\build\MonoAndroid70\Xamarin.Android.Support.Media.Compat.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Media.Compat\build\MonoAndroid70\Xamarin.Android.Support.Media.Compat.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Fragment\build\MonoAndroid70\Xamarin.Android.Support.Fragment.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Fragment\build\MonoAndroid70\Xamarin.Android.Support.Fragment.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v4\build\MonoAndroid70\Xamarin.Android.Support.v4.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v4\build\MonoAndroid70\Xamarin.Android.Support.v4.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Transition\build\MonoAndroid70\Xamarin.Android.Support.Transition.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Transition\build\MonoAndroid70\Xamarin.Android.Support.Transition.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.CardView\build\MonoAndroid70\Xamarin.Android.Support.v7.CardView.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.CardView\build\MonoAndroid70\Xamarin.Android.Support.v7.CardView.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.Palette\build\MonoAndroid70\Xamarin.Android.Support.v7.Palette.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.Palette\build\MonoAndroid70\Xamarin.Android.Support.v7.Palette.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.RecyclerView\build\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.RecyclerView\build\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Animated.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Animated.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.AppCompat\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.AppCompat\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Design\build\MonoAndroid70\Xamarin.Android.Support.Design.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Design\build\MonoAndroid70\Xamarin.Android.Support.Design.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.MediaRouter\build\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.MediaRouter\build\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.targets')" />
|
||||
<ItemGroup>
|
||||
<Compile Include="MainActivity.fs" />
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
|
@ -90,76 +90,76 @@
|
|||
<Name>Elmish.XamarinForms</Name>
|
||||
</ProjectReference>
|
||||
<Reference Include="Xamarin.Android.FSharp.ResourceProvider.Runtime">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.FSharp.ResourceProvider/lib/Xamarin.Android.FSharp.ResourceProvider.Runtime.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.FSharp.ResourceProvider/lib/Xamarin.Android.FSharp.ResourceProvider.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Animated.Vector.Drawable">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Animated.Vector.Drawable/lib/MonoAndroid70/Xamarin.Android.Support.Animated.Vector.Drawable.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Animated.Vector.Drawable/lib/MonoAndroid70/Xamarin.Android.Support.Animated.Vector.Drawable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Annotations">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Annotations/lib/MonoAndroid70/Xamarin.Android.Support.Annotations.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Annotations/lib/MonoAndroid70/Xamarin.Android.Support.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Compat">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Compat/lib/MonoAndroid70/Xamarin.Android.Support.Compat.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Compat/lib/MonoAndroid70/Xamarin.Android.Support.Compat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Core.UI">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Core.UI/lib/MonoAndroid70/Xamarin.Android.Support.Core.UI.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Core.UI/lib/MonoAndroid70/Xamarin.Android.Support.Core.UI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Core.Utils">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Core.Utils/lib/MonoAndroid70/Xamarin.Android.Support.Core.Utils.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Core.Utils/lib/MonoAndroid70/Xamarin.Android.Support.Core.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Design">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Design/lib/MonoAndroid70/Xamarin.Android.Support.Design.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Design/lib/MonoAndroid70/Xamarin.Android.Support.Design.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Fragment">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Fragment/lib/MonoAndroid70/Xamarin.Android.Support.Fragment.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Fragment/lib/MonoAndroid70/Xamarin.Android.Support.Fragment.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Media.Compat">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Media.Compat/lib/MonoAndroid70/Xamarin.Android.Support.Media.Compat.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Media.Compat/lib/MonoAndroid70/Xamarin.Android.Support.Media.Compat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Transition">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Transition/lib/MonoAndroid70/Xamarin.Android.Support.Transition.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Transition/lib/MonoAndroid70/Xamarin.Android.Support.Transition.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v4">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v4/lib/MonoAndroid70/Xamarin.Android.Support.v4.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v4/lib/MonoAndroid70/Xamarin.Android.Support.v4.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.AppCompat">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.AppCompat/lib/MonoAndroid70/Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.AppCompat/lib/MonoAndroid70/Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.CardView">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.CardView/lib/MonoAndroid70/Xamarin.Android.Support.v7.CardView.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.CardView/lib/MonoAndroid70/Xamarin.Android.Support.v7.CardView.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.MediaRouter">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.MediaRouter/lib/MonoAndroid70/Xamarin.Android.Support.v7.MediaRouter.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.MediaRouter/lib/MonoAndroid70/Xamarin.Android.Support.v7.MediaRouter.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.Palette">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.Palette/lib/MonoAndroid70/Xamarin.Android.Support.v7.Palette.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.Palette/lib/MonoAndroid70/Xamarin.Android.Support.v7.Palette.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.RecyclerView">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.RecyclerView/lib/MonoAndroid70/Xamarin.Android.Support.v7.RecyclerView.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.RecyclerView/lib/MonoAndroid70/Xamarin.Android.Support.v7.RecyclerView.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Vector.Drawable">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Vector.Drawable/lib/MonoAndroid70/Xamarin.Android.Support.Vector.Drawable.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Vector.Drawable/lib/MonoAndroid70/Xamarin.Android.Support.Vector.Drawable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Forms.Core">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Core.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Forms.Platform">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Platform.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Platform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Forms.Platform.Android">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Platform.Android.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Platform.Android.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FormsViewGroup">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/FormsViewGroup.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/FormsViewGroup.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Forms.Xaml">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Xaml.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Xaml.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FSharp.Core">
|
||||
<HintPath>..\..\..\packages\FSharp.Core.4.3.4\lib\netstandard1.6\FSharp.Core.dll</HintPath>
|
||||
<HintPath>../../../../packages/FSharp.Core.4.3.4/lib/netstandard1.6/FSharp.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FSharp.Control.AsyncSeq">
|
||||
<HintPath>..\..\..\packages\FSharp.Control.AsyncSeq.2.0.21\lib\netstandard2.0\FSharp.Control.AsyncSeq.dll</HintPath>
|
||||
<HintPath>../../../../packages/FSharp.Control.AsyncSeq.2.0.21/lib/netstandard2.0/FSharp.Control.AsyncSeq.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.props" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.props')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.props" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
|
@ -48,22 +48,22 @@
|
|||
</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.FSharp.targets" Condition="Exists('$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.FSharp.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Compat\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Compat\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Core.UI\build\MonoAndroid70\Xamarin.Android.Support.Core.UI.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Core.UI\build\MonoAndroid70\Xamarin.Android.Support.Core.UI.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Core.Utils\build\MonoAndroid70\Xamarin.Android.Support.Core.Utils.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Core.Utils\build\MonoAndroid70\Xamarin.Android.Support.Core.Utils.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Media.Compat\build\MonoAndroid70\Xamarin.Android.Support.Media.Compat.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Media.Compat\build\MonoAndroid70\Xamarin.Android.Support.Media.Compat.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Fragment\build\MonoAndroid70\Xamarin.Android.Support.Fragment.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Fragment\build\MonoAndroid70\Xamarin.Android.Support.Fragment.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v4\build\MonoAndroid70\Xamarin.Android.Support.v4.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v4\build\MonoAndroid70\Xamarin.Android.Support.v4.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Transition\build\MonoAndroid70\Xamarin.Android.Support.Transition.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Transition\build\MonoAndroid70\Xamarin.Android.Support.Transition.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.CardView\build\MonoAndroid70\Xamarin.Android.Support.v7.CardView.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.CardView\build\MonoAndroid70\Xamarin.Android.Support.v7.CardView.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.Palette\build\MonoAndroid70\Xamarin.Android.Support.v7.Palette.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.Palette\build\MonoAndroid70\Xamarin.Android.Support.v7.Palette.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.RecyclerView\build\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.RecyclerView\build\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Animated.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Animated.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.AppCompat\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.AppCompat\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.Design\build\MonoAndroid70\Xamarin.Android.Support.Design.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.Design\build\MonoAndroid70\Xamarin.Android.Support.Design.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Android.Support.v7.MediaRouter\build\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Android.Support.v7.MediaRouter\build\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.targets')" />
|
||||
<Import Project="..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.targets" Condition="Exists('..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Compat\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Compat\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Core.UI\build\MonoAndroid70\Xamarin.Android.Support.Core.UI.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Core.UI\build\MonoAndroid70\Xamarin.Android.Support.Core.UI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Core.Utils\build\MonoAndroid70\Xamarin.Android.Support.Core.Utils.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Core.Utils\build\MonoAndroid70\Xamarin.Android.Support.Core.Utils.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Media.Compat\build\MonoAndroid70\Xamarin.Android.Support.Media.Compat.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Media.Compat\build\MonoAndroid70\Xamarin.Android.Support.Media.Compat.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Fragment\build\MonoAndroid70\Xamarin.Android.Support.Fragment.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Fragment\build\MonoAndroid70\Xamarin.Android.Support.Fragment.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v4\build\MonoAndroid70\Xamarin.Android.Support.v4.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v4\build\MonoAndroid70\Xamarin.Android.Support.v4.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Transition\build\MonoAndroid70\Xamarin.Android.Support.Transition.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Transition\build\MonoAndroid70\Xamarin.Android.Support.Transition.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.CardView\build\MonoAndroid70\Xamarin.Android.Support.v7.CardView.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.CardView\build\MonoAndroid70\Xamarin.Android.Support.v7.CardView.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.Palette\build\MonoAndroid70\Xamarin.Android.Support.v7.Palette.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.Palette\build\MonoAndroid70\Xamarin.Android.Support.v7.Palette.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.RecyclerView\build\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.RecyclerView\build\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Animated.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Animated.Vector.Drawable\build\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.AppCompat\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.AppCompat\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.Design\build\MonoAndroid70\Xamarin.Android.Support.Design.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.Design\build\MonoAndroid70\Xamarin.Android.Support.Design.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.MediaRouter\build\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Android.Support.v7.MediaRouter\build\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.targets')" />
|
||||
<Import Project="..\..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.targets" Condition="Exists('..\..\..\..\packages\androidapp\Xamarin.Forms\build\netstandard1.0\Xamarin.Forms.targets')" />
|
||||
<ItemGroup>
|
||||
<Compile Include="MainActivity.fs" />
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
|
@ -92,76 +92,76 @@
|
|||
<Name>Elmish.XamarinForms</Name>
|
||||
</ProjectReference>
|
||||
<Reference Include="Xamarin.Android.FSharp.ResourceProvider.Runtime">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.FSharp.ResourceProvider/lib/Xamarin.Android.FSharp.ResourceProvider.Runtime.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.FSharp.ResourceProvider/lib/Xamarin.Android.FSharp.ResourceProvider.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Animated.Vector.Drawable">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Animated.Vector.Drawable/lib/MonoAndroid70/Xamarin.Android.Support.Animated.Vector.Drawable.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Animated.Vector.Drawable/lib/MonoAndroid70/Xamarin.Android.Support.Animated.Vector.Drawable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Annotations">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Annotations/lib/MonoAndroid70/Xamarin.Android.Support.Annotations.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Annotations/lib/MonoAndroid70/Xamarin.Android.Support.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Compat">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Compat/lib/MonoAndroid70/Xamarin.Android.Support.Compat.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Compat/lib/MonoAndroid70/Xamarin.Android.Support.Compat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Core.UI">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Core.UI/lib/MonoAndroid70/Xamarin.Android.Support.Core.UI.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Core.UI/lib/MonoAndroid70/Xamarin.Android.Support.Core.UI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Core.Utils">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Core.Utils/lib/MonoAndroid70/Xamarin.Android.Support.Core.Utils.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Core.Utils/lib/MonoAndroid70/Xamarin.Android.Support.Core.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Design">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Design/lib/MonoAndroid70/Xamarin.Android.Support.Design.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Design/lib/MonoAndroid70/Xamarin.Android.Support.Design.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Fragment">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Fragment/lib/MonoAndroid70/Xamarin.Android.Support.Fragment.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Fragment/lib/MonoAndroid70/Xamarin.Android.Support.Fragment.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Media.Compat">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Media.Compat/lib/MonoAndroid70/Xamarin.Android.Support.Media.Compat.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Media.Compat/lib/MonoAndroid70/Xamarin.Android.Support.Media.Compat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Transition">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Transition/lib/MonoAndroid70/Xamarin.Android.Support.Transition.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Transition/lib/MonoAndroid70/Xamarin.Android.Support.Transition.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v4">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v4/lib/MonoAndroid70/Xamarin.Android.Support.v4.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v4/lib/MonoAndroid70/Xamarin.Android.Support.v4.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.AppCompat">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.AppCompat/lib/MonoAndroid70/Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.AppCompat/lib/MonoAndroid70/Xamarin.Android.Support.v7.AppCompat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.CardView">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.CardView/lib/MonoAndroid70/Xamarin.Android.Support.v7.CardView.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.CardView/lib/MonoAndroid70/Xamarin.Android.Support.v7.CardView.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.MediaRouter">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.MediaRouter/lib/MonoAndroid70/Xamarin.Android.Support.v7.MediaRouter.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.MediaRouter/lib/MonoAndroid70/Xamarin.Android.Support.v7.MediaRouter.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.Palette">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.Palette/lib/MonoAndroid70/Xamarin.Android.Support.v7.Palette.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.Palette/lib/MonoAndroid70/Xamarin.Android.Support.v7.Palette.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v7.RecyclerView">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.v7.RecyclerView/lib/MonoAndroid70/Xamarin.Android.Support.v7.RecyclerView.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.v7.RecyclerView/lib/MonoAndroid70/Xamarin.Android.Support.v7.RecyclerView.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Vector.Drawable">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Android.Support.Vector.Drawable/lib/MonoAndroid70/Xamarin.Android.Support.Vector.Drawable.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Android.Support.Vector.Drawable/lib/MonoAndroid70/Xamarin.Android.Support.Vector.Drawable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Forms.Core">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Core.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Forms.Platform">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Platform.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Platform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FormsViewGroup">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/FormsViewGroup.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/FormsViewGroup.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Forms.Platform.Android">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Platform.Android.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Platform.Android.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Forms.Xaml">
|
||||
<HintPath>../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Xaml.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/Xamarin.Forms/lib/MonoAndroid10/Xamarin.Forms.Xaml.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FSharp.Control.AsyncSeq">
|
||||
<HintPath>../../../packages/FSharp.Control.AsyncSeq.2.0.21/lib/netstandard2.0/FSharp.Control.AsyncSeq.dll</HintPath>
|
||||
<HintPath>../../../../packages/FSharp.Control.AsyncSeq.2.0.21/lib/netstandard2.0/FSharp.Control.AsyncSeq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FSharp.Core">
|
||||
<HintPath>../../../packages/androidapp/FSharp.Core.4.3.4/lib/netstandard1.6/FSharp.Core.dll</HintPath>
|
||||
<HintPath>../../../../packages/androidapp/FSharp.Core.4.3.4/lib/netstandard1.6/FSharp.Core.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -4,6 +4,7 @@ namespace MasterDetailApp
|
|||
open System
|
||||
open Elmish
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.StaticViews
|
||||
open FSharp.Control
|
||||
open Xamarin.Forms
|
||||
open Xamarin.Forms.Xaml
|
||||
|
@ -51,13 +52,11 @@ type MasterDetailApp () as self =
|
|||
|
||||
do
|
||||
let mainPage =
|
||||
Program.mkProgram
|
||||
(fun () -> MainPage.init(), NoCmd)
|
||||
MainPage.update
|
||||
(fun _ _ -> MainPage.view())
|
||||
Program.mkProgram (fun () -> MainPage.init(), NoCmd) MainPage.update MainPage.view
|
||||
|> Program.withConsoleTrace
|
||||
|> Program.withNavigation
|
||||
|> Program.runStaticView
|
||||
|> Program.withStaticView
|
||||
|> Program.run
|
||||
|
||||
base.MainPage <- NavigationPage mainPage
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
open Xamarin.Forms
|
||||
open Xamarin.Forms.Xaml
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.StaticViews
|
||||
|
||||
type AboutPage() =
|
||||
inherit ContentPage()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
open Xamarin.Forms
|
||||
open Xamarin.Forms.Xaml
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.StaticViews
|
||||
|
||||
type ItemDetailPage() =
|
||||
inherit ContentPage()
|
||||
|
|
|
@ -4,6 +4,7 @@ open FSharp.Control
|
|||
open Xamarin.Forms
|
||||
open Xamarin.Forms.Xaml
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.StaticViews
|
||||
|
||||
type ItemsPage() =
|
||||
inherit ContentPage()
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace MasterDetailApp
|
|||
open Xamarin.Forms
|
||||
open Xamarin.Forms.Xaml
|
||||
open Elmish.XamarinForms
|
||||
open Elmish.XamarinForms.StaticViews
|
||||
|
||||
type NewItemPage() =
|
||||
inherit ContentPage()
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="io.jimbobbennett.TicTacToe">
|
||||
<uses-sdk android:minSdkVersion="15" />
|
||||
<application android:label="TicTacToe">
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,25 @@
|
|||
namespace TicTacToe.Droid
|
||||
|
||||
open System.Reflection
|
||||
open System.Runtime.CompilerServices
|
||||
|
||||
// the name of the type here needs to match the name inside the ResourceDesigner attribute
|
||||
type Resources = TicTacToe.Droid.Resource
|
||||
[<assembly: Android.Runtime.ResourceDesigner("TicTacToe.Droid.Resources", IsApplication=true)>]
|
||||
|
||||
[<assembly: AssemblyTitle("TicTacToe.Droid")>]
|
||||
[<assembly: AssemblyDescription("")>]
|
||||
[<assembly: AssemblyConfiguration("")>]
|
||||
[<assembly: AssemblyCompany("")>]
|
||||
[<assembly: AssemblyProduct("")>]
|
||||
[<assembly: AssemblyCopyright("")>]
|
||||
[<assembly: AssemblyTrademark("")>]
|
||||
|
||||
// The assembly version has the format {Major}.{Minor}.{Build}.{Revision}
|
||||
|
||||
[<assembly: AssemblyVersion("1.0.0.0")>]
|
||||
|
||||
//[<assembly: AssemblyDelaySign(false)>]
|
||||
//[<assembly: AssemblyKeyFile("")>]
|
||||
|
||||
()
|
|
@ -186,12 +186,10 @@ type App() =
|
|||
Application.Current.MainPage.DisplayAlert("Game over", msg, "OK") |> ignore
|
||||
|
||||
let page =
|
||||
Program.mkSimple
|
||||
App.init
|
||||
(App.update gameOver)
|
||||
(fun _ _ -> HelperPage(App.viewAllocatedSizeFixup), [], App.view)
|
||||
Program.mkSimple App.init (App.update gameOver) App.view
|
||||
|> Program.withConsoleTrace
|
||||
|> Program.runDynamicView
|
||||
|> Program.withDynamicView
|
||||
|> Program.run
|
||||
|
||||
let mainPage = NavigationPage(page, BarBackgroundColor = Color.LightBlue, BarTextColor = Color.Black)
|
||||
do base.MainPage <- mainPage
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FSharp.Control.AsyncSeq" version="2.0.21" targetFramework="monoandroid71" />
|
||||
<package id="FSharp.Core" version="4.3.4" targetFramework="monoandroid71" />
|
||||
</packages>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FSharp.Control.AsyncSeq" version="2.0.21" targetFramework="monoandroid71" />
|
||||
<package id="FSharp.Core" version="4.3.4" targetFramework="monoandroid71" />
|
||||
</packages>
|
Загрузка…
Ссылка в новой задаче