Closed fwaris closed 7 months ago
Note for some reason I cannot attach files so emdedding the .fsx file inline:
event_loop.fsx
#r "nuget: AVLoop"
#r "nuget: AvaloniaGraphControl"
#r "nuget: Avalonia.FuncUI.Elmish"
open FSharp.Compiler.Interactive
open AVLoop
let install(theme) =
fsi.EventLoop <- {new IEventLoop with
member x.Run() =
createApp(theme, [||])
false //dummy
member x.Invoke(f) = disp f
member x.ScheduleRestart() = () //dummy
}
install(Default,Dark) //wait till initialization message before submitting more code
Graph.fsx
#load "event_loop.fsx" //run by itself first till you see "avalonia initialized" and then submit other code
open Elmish
open Avalonia.Controls
open Avalonia.Media
open Avalonia.FuncUI
open Avalonia.FuncUI.DSL
open Avalonia.FuncUI.Hosts
open Avalonia.FuncUI.Elmish
type [<CLIMutable>] Node = {Label:string}
let order (a,b) = if a >= b then a,b else b,a
let deviceRels =
[
"gnodeb_id", ["cell_id"; "rmod"; "bbmod"]
"enodeb_id", ["cell_id"; "rmod"; "bbomd"]
"cell_id", ["sector_id"]
"rmod",["sfp"]
"bbmod",["sfp"; "smod"; "cabinet"]
"smod", ["cabinet"]
]
let dg = new AvaloniaGraphControl.Graph()
dg.Edges.Clear()
deviceRels |> List.collect (fun (r,xs) -> xs |> List.map(fun b -> order(r,b))) |> List.distinct
|> List.iter (fun (a,b) ->
dg.Edges.Add(AvaloniaGraphControl.Edge(
{Label=a},
{Label=b},
tailSymbol = AvaloniaGraphControl.Edge.Symbol.None,
headSymbol = AvaloniaGraphControl.Edge.Symbol.None)))
[<AutoOpen>]
module GraphPanel =
open Avalonia.FuncUI.Types
open Avalonia.FuncUI.Builder
open AvaloniaGraphControl
open Avalonia.Controls.Templates
let create (attrs: IAttr<GraphPanel> list): IView<GraphPanel> =
ViewBuilder.Create<GraphPanel>(attrs)
type GraphPanel with
static member graph<'t when 't :> GraphPanel>(value:Graph) : IAttr<'t> =
AttrBuilder<'t>.CreateProperty<Graph>(GraphPanel.GraphProperty, value, ValueNone)
static member itemTemplate<'t when 't :> GraphPanel>(value: IDataTemplate) : IAttr<'t> =
let getter : ('t -> IDataTemplate) = (fun t -> if t.DataTemplates.Count > 0 then t.DataTemplates.[0] else null)
let setter : ('t * IDataTemplate -> unit) = (fun (t,v) -> t.DataTemplates.Clear(); t.DataTemplates.Add(v))
AttrBuilder<'t>.CreateProperty<IDataTemplate>("itemTemplate", value, ValueSome getter, ValueSome setter, ValueNone)
static member dataTemplates<'t when 't :> GraphPanel>(value: DataTemplates) : IAttr<'t> =
let getter : ('t -> DataTemplates) = (fun t -> if t.DataTemplates = null then new DataTemplates() else t.DataTemplates)
let setter : ('t * DataTemplates -> unit) = (fun (t,v) -> t.DataTemplates.Clear(); t.DataTemplates.AddRange(v))
AttrBuilder<'t>.CreateProperty<DataTemplates>("DataTemplates", value, ValueSome getter, ValueSome setter, ValueNone)
[<AutoOpen>]
module TextSticker =
open Avalonia.FuncUI.Types
open Avalonia.FuncUI.Builder
open AvaloniaGraphControl
let create (attrs: IAttr<TextSticker> list): IView<TextSticker> =
ViewBuilder.Create<TextSticker>(attrs)
type TextSticker with
static member shape<'t when 't :> TextSticker>(value:TextSticker.Shapes) : IAttr<'t> =
AttrBuilder<'t>.CreateProperty<TextSticker.Shapes>(TextSticker.ShapeProperty, value, ValueNone)
static member text<'t when 't :> TextSticker>(value:string) : IAttr<'t> =
AttrBuilder<'t>.CreateProperty<string>(TextSticker.TextProperty, value, ValueNone)
module Counter =
open AvaloniaGraphControl
open Avalonia.Controls.Templates
type State = { count : int ; graph:AvaloniaGraphControl.Graph}
let init = { count = 0; graph=dg}
type Msg = Increment | Decrement | Reset
let update (msg: Msg) (state: State) : State =
match msg with
| Increment -> { state with count = state.count + 1 }
| Decrement -> { state with count = state.count - 1 }
| Reset -> init
let view (state: State) (dispatch) =
Viewbox.create [
Viewbox.child (
GraphPanel.create [
GraphPanel.graph state.graph
GraphPanel.itemTemplate (
DataTemplateView<Node>.create (fun (data:Node) ->
TextSticker.create [
TextSticker.shape TextSticker.Shapes.Ellipse
TextSticker.text data.Label
])
)
// GraphPanel.dataTemplates (
// let ds = DataTemplates()
// ds.AddRange(
// [
// DataTemplateView<Node>.create (fun (data:Node) ->
// TextSticker.create [
// TextSticker.shape TextSticker.Shapes.Ellipse
// TextSticker.text data.Label
// ])
// ])
// ds
// )
]
)
]
type MainWindow() as this =
inherit HostWindow()
do
base.Title <- "BasicTemplate"
base.Background <- Brushes.White
base.Width <- 400.0
base.Height <- 400.0
Program.mkSimple (fun () -> Counter.init) Counter.update Counter.view
|> Program.withHost this
|> Program.run
let win = MainWindow()
win.Show()
@fwaris could you provide a runnable sample project? (just a single fsproj)
sure ... let me put that together thanks
please see a minimal sample in this repo https://github.com/fwaris/GraphControlSample.
The IDataTemplate is not being applied for node formatting. If it were, the nodes would have an elliptical shape (instead of rounded rectangle)
@JaggerJo for some reason I am not able to attach files to github comments / issues. Instead I have created a minimal standalone sample in my personal github the link to the repo is : https://github.com/fwaris/GraphControlSample.
I would appreciate if you take a look and give me some suggestions or a workaround to fix this issue.
Thanks!
Faisal
Try setting the graph property after the templates property? e.g.
GraphPanel.create [
GraphPanel.dataTemplates (
let ds = DataTemplates()
ds.AddRange(
[
DataTemplateView<Node>.create (fun (data:Node) ->
TextSticker.create [
TextSticker.shape TextSticker.Shapes.Ellipse
TextSticker.text data.Label
])
])
ds
)
GraphPanel.graph state.graph
]
I don't see a itemTemplate
property on GraphPanel
at all?
that works. thanks!
(the itemTemplate was an experiment to see if a single template could be added to the DataTemplates instance variable using just a funcui function - but that does not seem to work)
I am trying to integrate the AvaloniaGraphControl (https://github.com/Oaz/AvaloniaGraphControl) into funcui.
The basic graph shows up fine. The issue is with the formatting of graph elements (e.g. nodes).
In the AvaloniaGraphControl xaml examples, node formatting is handled by DataTemplates that are defined under the GraphPanel control in xaml.
I have tried adding an IDataTemplate to the GraphPanel.DataTemplates variable but its is not being pickup when running under funcui.
Please see attached .fsx file that comprise an example graph with formatting. I would appreciate any quick suggestions to try to fix this issue.