HostControl
: If I create control and then replace it with something else, will this control live in memory? I'm not sure because every control have Program.run
inside of it and it's an infinite loop, which doesn't listen to detaching from visual tree
let tmenu =
Menu.create [
Menu.viewItems [
yield MenuItem.create [MenuItem.header "File"]
yield MenuItem.create [MenuItem.header "Edit"]
]
]
Hi all,
I'm trying to implement a list of entries (e.g. list of notes) in an app, with the condition that each entry should be easily deletable by pressing a button. Everything has been going fine, except that the deletion part seems to work a bit wonkily - Whenever I press an entry deletion button, instead of sending a message to delete that entry, a removal message with the first entry in that list is sent. Would someone happen to know why this happens, and how to send the correct entry with a button press?
My update function looks like this:
type Entry = {uid:int}
type State = { entries: Entry list; uid : int}
let init = { entries = [];uid = 0 }
type Msg = Add | Remove of Entry | Reset
let update (msg: Msg) (state: State) : State =
match msg with
| Add -> { state with entries = List.append [{uid=state.uid}] state.entries;uid = state.uid + 1 }
| Remove id ->
let entries = state.entries |> List.filter (fun x -> x <> id )
{ state with entries = entries }
| Reset -> init
And my view is quite simple as well:
let emptyEntries =
StackPanel.create[
StackPanel.children[
TextBlock.create [
TextBlock.text "No expenses"
]
]
]
let singleEntry (entry:Entry) dispatch =
Button.create [
Button.content (sprintf "Remove %A" entry.uid)
Button.onClick (fun _ ->
printfn "entry: %A" entry
dispatch (Remove entry))
]
let existEntries (entries:Entry list) dispatch =
StackPanel.create[
StackPanel.children [
for entry in entries do
yield singleEntry entry dispatch
]
]
let entryPanel state dispatch =
match state.entries with
| [] -> emptyEntries :> IView
| xs -> (existEntries xs dispatch) :> IView
let view (state: State) (dispatch) =
DockPanel.create [
DockPanel.children [
Button.create [
Button.dock Dock.Bottom
Button.onClick (fun _ -> dispatch Reset)
Button.content "reset"
]
Button.create [
Button.dock Dock.Bottom
Button.onClick (fun _ -> dispatch Add)
Button.content "+"
]
StackPanel.create [
StackPanel.dock Dock.Bottom
StackPanel.maxHeight 300.0
StackPanel.children [
entryPanel state dispatch
]
]
TextBlock.create [
TextBlock.dock Dock.Top
TextBlock.fontSize 48.0
TextBlock.verticalAlignment VerticalAlignment.Center
TextBlock.horizontalAlignment HorizontalAlignment.Center
TextBlock.text (string state.uid)
]
]
]
I have a minimal reproducing example here:
https://github.com/Lipastomies/AvaFuncProblem
I'm developing on Win 10, with .NET SDK (5.0.301)
I unfortunately don't have a clue on what I'm doing wrong, so any and all help would be appreciated.
I'm trying to make something akin to a svg editor, and I'm having a really hard time trying to get the cursor position relative to the drawing canvas. I'm finding it difficult to be able to get mouse positions relative to objects I'm creating on the canvas.
Canvas.create [
Canvas.onPointerMoved
(fun e ->
e.GetPosition(e.Source :?> IVisual)
|> Msg.MouseMove
|> dispatch) ]
This gets me the cursor position relative to the child object I'm hovering over. If I'm hovering over a child object, it resets the coordinates local to that child instead of being relative to the canvas object
what I really want is something more like
let canvas = Canvas.create [ ... ]
DockPanel.create
<| [ DockPanel.onPointerMoved
(fun e ->
e.GetPosition(canvas :?> IVisual) // <-- this is a compilation error and doesn't work
|> Msg.MouseMove
|> dispatch) ]
Has anyone had a similar issue or knows how to overcome this?
SubpatchOptions.OnChangeOF
in the button pressing event. I think it's time for me to dig into the concepts a bit more :D
/// Try to find a child control of a given name using breadth first search
let findChildControl (name: string) (source: IControl) : IControl option =
let rec findChildControlHelper (children: ILogical list) =
match children with
| first :: remaining ->
if (first :?> IControl).Name = name then
Some(first :?> IControl)
else
findChildControlHelper (remaining @ (List.ofSeq first.LogicalChildren))
| [] -> None
findChildControlHelper (List.ofSeq source.LogicalChildren)
/// Traverse to the root of the tree and do a breadth first search for the element
let findControl (name: String) (source: IControl) : IControl option =
if source.Name = name then
Some source
else
findChildControl name (source.VisualRoot :?> IControl)
/// Given a pointer event, get the position relative to a particular control name
/// Useful for triggering off of mouse movement events
let positionRelativeTo (name: String) (event: PointerEventArgs) =
let maybeVisual =
findControl name (event.Source :?> IControl)
match maybeVisual with
| Some visual -> event.GetPosition(visual)
| None -> Point(infinity, infinity)
Using JaggerJo.Avalonia.FuncUI (0.5.0-beta)
Avalonia.Desktop (>= 0.10.0-rc1)
with the following code:
let textBlock (text: string) =
TextBlock.create [
TextBlock.fontSize 14.0
TextBlock.verticalAlignment VerticalAlignment.Center
TextBlock.horizontalAlignment HorizontalAlignment.Center
TextBlock.text text
] :> IView
let view (state: StationOrchestrator.Model) (dispatch: StationOrchestrator.Msg Dispatch) =
HeaderedContentControl.create [
HeaderedContentControl.header "Hello"
HeaderedContentControl.foreground "White"
HeaderedContentControl.background "Blue"
HeaderedContentControl.width 400.0
HeaderedContentControl.height 400.0
HeaderedContentControl.content (textBlock "Groupbox")
]
Is not showing at all the expected Hello GroupBox (just black screen inside the window). Any idea what I am doing wrong?
let groupBox title (content: IView) =
let headBorder =
Border.create [
Border.zIndex 1
Border.margin (Thickness(15.,0.,0.,0.))
Border.padding (Thickness(5.,0.,5.,0.))
Border.background background
Border.child (
TextBlock.create [
TextBlock.foreground groupBoxHeaderColor
TextBlock.fontWeight FontWeight.Bold
TextBlock.text title
])
]
let contentBorder =
Border.create [
Border.child content
Grid.rowSpan 2
Grid.columnSpan 2
Border.margin (Thickness(5.,10.,5.,5.))
Border.padding 5.0 //(Thickness(4.0, 5.0, 4.0, 4.0))
Border.borderBrush bordersColor
Border.borderThickness 2.0
Border.cornerRadius 3.0
// BoxShadow.Parse "5 5 10 2 DarkGray" |> Border.boxShadow
]
Grid.create [
Grid.rowDefinitions "Auto,*"
Grid.columnDefinitions "Auto,*"
Grid.margin 0.
Grid.children [
headBorder
contentBorder
]
] :> IView
TextBlock
Property to another DependencyProperty
? and how do I do FontStretch
?
Tunnel
event is triggered first then the Bubble
event. I really only want the events to bubble up from bottom to top so I can handle the child elements first. I know that I can change the e.Route
or e.RoutedEvent.RoutingStrategies
where e is a routed event like PointerReleasedEventArgs
. I would have to litter my code with checks for the Bubble
event since the Tunnel
is called first. Is there a way to set the routing strategy at app creation? Also, what use cases would people be using tunneling events?
Not sure if anyone else has wondered about how to unit test update functions after adding saving or loading dialogues but I figured I'd pass that information forward
open Moq
let IWindowImpl = Mock<IWindowImpl>().Object
let window = Mock<Window>(IWindowImpl).Object
You can then just pass this window object to the update function and you can run the tests. If you are actually testing the code that calls the window functionality you will need to create a mock function return for these objects
https://fsharpforfunandprofit.com/posts/low-risk-ways-to-use-fsharp-at-work-3/#15-use-f-to-create-mocks
Hi, I am trying to use/learn funcUI by translating some wpf. Now I want to create a Popup to use as nofication system for login etc.
But I get the error " No logical parent set" and 'PlacementTarget is null'
I don't know where to go from here and can't find any info or repos using this.
let toastPopup (state:State) dispatch =
Border.create [
...
Border.child(
Popup.create [
Popup.width 200.
Popup.height 200.
Popup.isOpen true
Popup.child (TextBlock.create [ Textblock.text "Hi there" ])
])
]
Based on:
https://stackoverflow.com/questions/62194012/avalonia-ui-pop-up-overlay#62196079
Style
objects to swap in the program to achieve this skinning. From what I gathered, the XAML is turned into these Style objects but I haven't found an easy way to create them, let alone with any sort of DSL wrapper. If you make any progress on this one please let me know because I also would like to be able to do that in code! It should still work in XAML though if you are alright hard coding all the hex color codes in those files and then swapping them in the code. Good luck!
@P-Louw I don't know how easy of a task that will be compared to avalonia. Creating multiple windows with FuncUI isn't the easiest. There is this example of showing how to link two windows. One will always maintain the focus, which is presumably what you want. There is this example of a multiwindow that you could take a look at
https://github.com/AngelMunoz/MultiWindow
You probably have to link that popup window to the host window which is this class that you create near the execution point of the program. This can be accessed within the class as this
.
When creating it through the DSL in a more functional style, you can pass this window into the view function through the following code. I'm not sure if you will need this data or not, but I'm assuming this is what your code is complaining about missing.
type MainWindow() as this =
inherit HostWindow()
do
base.Width <- 400
base.Height <- 600
let viewWithWindow (state: State) dispatch = view state dispatch this
Program.mkProgram init update viewWithWindow
|> Program.withHost this
|> Program.run