-
-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Best practices for mutable objects in model? #113
Comments
Ideally the model and the
I haven't checked out AvalonEdit, so I can't comment directly on that. But in general, mutable objects in the model should be avoided (that is, objects which are actually mutated, e.g. not
You may not care about the first point, and you may be able to work around the second using appropriate lazy binding overloads with a custom equality. But in general I would strongly discourage mutating the model.
I'm having as much trouble wrapping my head around this as I would making toast with a hammer. Like, I'm sure it's possible (use a torch to heat the hammer?), but I haven't given much thought to it and I think and hope there are better ways of solving the problem. Also see the point above regarding simplicity.
Again, I haven't investigated AvalonEdit, but in general, no. In general, anything designed to be MVVM-friendly (using data bindings) should be compatible with Elmish.WPF. It may seem like AvalonEdit requires a complex class ( |
Thanks for the quick reply. I've managed to get a subscription to the It seems like it should work fine, but if there are unforeseen risks, I should probably abandon that idea. And yeah, the I guess I could wrap the |
I think what I'd try is to create a custom control that wraps the UI stuff and the |
I think the only issue with that approach is that in order to avoid having to work with a string that's potentially thousands of lines of text long any time the user hits a key, I have to be able to expose a bindable collection, and as far as I know, there isn't a way to bind from a collection in the view to a collection in the model. |
I'd try it before passing judgement on the performance. But if you want to bind to each line separately, you can probably do it with |
I'm not sure I've grasped subModelSeq 100% yet, but it doesn't look like there's any way to add or remove items from the binding to the model? I've actually tried re-parsing entire documents before, and it gets too sluggish, which is why I do individual lines. I guess I could do some heavy-duty diffing, but that could potentially get even more complicated. |
Adding/removing items is possible but not from within a "sub-model" itself; it must be done at the level where you have the Each text line (i.e. sub-model) would have to have an ID, so I'm unsure how you can solve that. Line number / index is probably not a good idea, but I can't think of anything else. |
I had a case with Live-Charts that looks like yours. Live-Charts comes with its own MVVM mutable objects and UI elements can only be bound to them rather than to raw collections. In my case, I conventionally choose to use a list in my model and deal with the MVVM class (a module level instance) in my binding function (creation and synchronization, which was fortunately easy since this MVVM class seems to update its points cleverly — and there were load of points). Lazy bindings have been helpful for performance, though. (Also, It was just for displaying after loading them, no modifications.) After a fast overview of AvalonEdit, my approach would be to create an app level instance of TextDocument reachable by your bindings and cmds functions and deal with it the OOP-way it is intended to be used.
Of course, that’s unorthodox and if your app is nothing more than the text control alone, that make the elmish take-on rather moot. But if there’s more around, that would be still interesting. |
I'm looking at the subModelSeq sample, but I can't quite see how I would use this to be able to update the model with new/removed lines from the UI element.
That seems reasonable. I actually need the text in the model for a bunch of things, including undo/redo for the text and other state, parsing out each line to display individually with formatting both within the text editor and in the output control, and keeping track of various markers and anchors. So I can do most of the massaging in a changing/changed handler, and just feed whatever I need back to Elm through a subscription. If I ever need the full text, I can just access it from the TextDocument directly. I could probably just keep the undo stack in the control side as well, and just give each operation a sequence token to make sure everything happens in order. This is for an application that'll be displaying text in soft real-time. It's not exactly a full-fledged RTF editor, but it's pretty involved, and all the VMs were starting to get a bit unwieldy. React can only do so much to wrangle all the state propagation, so Elmish's single-source-of-truth is really attractive. |
Who is this program for? For example, is this a personal project or are you creating it at work as part of your job? |
Personal project. Mostly just tinkering and having fun. I'll use it for work if I can get it to maturity, but we do have our own tools that work already. |
You can try to look at how the removal of the counters is handled. In any case, I haven't looked at AvalonEdit, so this might not be feasible at all. Perhaps try the suggestion by @JDiLenarda? |
Yeah, that seems to be the best approach. Haven't run into any horrible roadblocks yet. Will post here if I notice anything that might be worth mentioning. Thanks for all the input! |
@reinux : can we have a feedback, please ? I’m interested to know how you dealt with it. |
The general feedback would be not to do it. Why are you considering using |
I genuinely don’t get if it’s a technical opinion or if I’m told I broke some etiquette. If so, I apologize. |
Oh, sorry. I thought you were requesting general feedback about having the model contain mutable data. I was stating a technical opinion. To be clear, you are welcome to ask any question. We will do our best to answer it. @reinux, do you have an update that you can share about your use of mutability in the model? |
So you mean mutability, not mutuality ? This word lost me. No worry, though. More specifically, I’d like to know how reinux dealt with its use of a third-party component. |
Oh, yes. I corrected my "mutuality" for "mutability" typo. Thanks :) |
@JDiLenarda I think I've stumbled on a few repeatable patterns for factoring mutable state out of the model and dealing with non-MVVM stuff. @bender2k14 I would love to have your input to know if I'm on the right track. The editor and the document live happily in XAML now, and there are no direct references to it in the model or in The general idea is that there are only a handful of reasons to interact with a mutable object, and only a handful of ways to deal with each:
The last case was the big sticking point for AvalonEdit. I optimized the heck out of the update loop using lazy bindings. Assuming this is the right way to think about the problem, I think a simple media player would be a good place to illustrate these ideas, since Also, this is probably dangerous information, but it turns out that a giant performance bottleneck in WPF when you have a ton of elements bound to F# objects is accessibility, because it calls |
I use DevExpress WPF components heavily, and have exactly these kind problems to solve, all the time. I mostly use EventToCommand and in a few cases also KeyToCommand in the XAML. There's also other tools from DevExpress, but there's no point mentioning them I believe.
These are DevExpress specific helpers, but I believe similar functionality exists in some Microsoft library that I can't remember the name of right now, but it deals with MVVM. Also elsewhere I would guess it can be found. |
That looks to be comparable to Microsoft.Xaml.Behaviors.
|
In fact, it is sufficient that an object is never mutated. If you use Array.map etc. then it's no problem using an array.
If you think that can be a useful sample, you're more than welcome to create a PR! 😊
How about not binding to F# records and using subModelSeq instead? Or alternatively only binding to specially designed F# records that use reference equality? I think that in general, it would not be wise to bind to domain objects if that's what was happening. |
Huh, that worked. I would never have suspected |
To be honest I'm not sure exactly why I created In any case, If each item has several bindings, then in general I would always reach for |
Ah, good to know. Are there any plans for a |
I literally just made an issue for it 😆 #143 I personally have no immediate plans to implement it. Let me know in that issue if you have measured Not sure if combining with |
Sweet :D I'll have something working soon which will require 60fps rendering, with a 1000+ element list in the model which doesn't need to be updated all the time. I also happen to have a version that doesn't use Elmish, so I can get a pretty good comparison of real world-ish performance. Will test it on my old-ish desktop and mid-range i5 Surface Book and get back to you. But first, I'll get that MediaElement sample going. |
That's very, very interesting. Looking forward to hearing about it! |
So now that I have a better grasp of Elmish, the media player sample I've been working on seems weirdly trivial. I'll probably return to it sometime if I can think of a way to make it more compelling. Meanwhile, having 1000+ items bound using
For the time being, I'll just not use a binding for the giant list. |
@bender2k14, perhaps some of that magical fairy dust in #137 could help significantly here? Also probably relevant: #143. Tips/PRs welcome. |
I'm working with a text/code editor control called AvalonEdit, which has a
TextDocument
class that contains all the text as well as methods for manipulating the text and events for changes to the text/filename/etc.I thought about just wrapping it in a control along with the editor, and binding to a collection of lines, but it turns out there's no way to two-way bind to a collection, which is sensible.
So now I'm putting the
TextDocument
inside the model, which means my model is now mutable. I have a few questions about this:Thanks in advance.
The text was updated successfully, but these errors were encountered: