Skip to content
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

Would it be possible to add a diagram to the readme? #11

Open
Greedquest opened this issue Apr 12, 2021 · 7 comments
Open

Would it be possible to add a diagram to the readme? #11

Greedquest opened this issue Apr 12, 2021 · 7 comments

Comments

@Greedquest
Copy link

Hey Matt,

I've been taking a look at this - I'd really like to get to grips with how it works a little better. I've read the blog posts, had a look at the readme and codereview posts etc. and I understand the principle & motivation (I think), but the implementation with all these 100+ modules is kinda confusing me still :)

I was wondering if you'd consider adding some kind of high level block diagrams to the readme? I'd understand if it's not your top priority right now, and IIRC I read in one of your blog posts that you don't like diagrams that much, but personally I find visual aids valuable in providing an overview of how the code fits together. I'm still not fluent enough in polymorphism to just see some code and piece it together in my head.

In the past I've done things like:

jMLJB 1 ...or this (please ignore the erroneous hungarian notation):
hl2Sl 1

... and I've found them really useful coming back to the code months later, I hope reviewers have found them useful too getting to grips with the code, even if they are quite informal (well you could easily confirm/deny the reviewer perspective;).

So IDK how you'd feel about adding this/ whether you would have the time at all, but I think different people learn differently and although the blog posts are excellent for explaining concepts precisely and with clear examples, for large projects where you might want a zoomed out view, I find supplementing the code with visual aids is really helpful, although that's just my opinion.


P.s. I would consider adding this myself if you like the sound of it, and will at some point in the future if you don't do it yourself, but I thought I'd ask in advance as you're probably much better placed to make it and of course this way I could benefit too.

@retailcoder
Copy link
Member

Hello! I have started porting this project over to twinBASIC (currently in preview/beta), as in the end a MVVM framework for VBA in VBA is rather heavy, and I can't see a framework pick up any level of traction, that requires bloating one's VBA project with 125 code files before writing a single line of code!

So, short-term, the VBA MVVM project is getting nuked and replaced with its twinBASIC... twin. Work done to document the many components isn't work gone to waste, because the tB version is basically the same (but with method overloading, parameterized constructors, and generics in the private implementation), but indeed my priority is to complete this port first (all while providing feedback to Wayne while tB is still beta).

Mid-term, when tB is ready for it, this repo will release both x86 and x64 builds of the library (will compile to a small vbmvvm.dll file) and the source code will be split up into separate source files (tB currently only supports one single source file), and by then I'm planning to have a wiki and readme and as much documentation as possible on the framework and both its internal and public APIs.

So you're a bit ahead of me here =)

Feel free to submit a PR to document the current form of the project; it'll probably make a very good starting point for the twinBASIC library/project documentation.

@Greedquest
Copy link
Author

Greedquest commented Aug 7, 2021

Just been playing around with it, this is so cool. Nice work!

@tothzola
Copy link

tothzola commented Apr 28, 2022

@Greedquest
Wanted to ask, have you made a diagram or some visual representation on how you understood the MVVM classes communication and couplings?
I`m interested how you see this, thanks for the reply.

@Greedquest
Copy link
Author

@tothzola Sorry, I didn't make one, and now I haven't looked at the framework again for a while (was waiting for the tB version) so would have to relearn it a bit to produce a diagram.

I did make a fairly minimal working example which I posted on code review - I don't know whether I've used the framework perfectly idiomatically as I never received any reviews but I think a simple and pretty minimal example might help you figure things out.

It was not too difficult to create by following the Rubberduck articles that Matt wrote, copying and pasting in the examples, and then just recompiling the VBA project to fix any errors of joining together the different components.

Sorry that's not what you're after I know, but I'm tied up with other projects at the moment so can't pick this up just now I'm afraid. Hope the additional code review example helps illustrate the mvvm technique in practice.

@tothzola
Copy link

tothzola commented Apr 28, 2022

Thanks @Greedquest for your replay, thought you hade made something, no problem if not.

The MVVM technique in practice, I do have some understanding, I also made a simple example based from the descriptions from here the project and also from the website. I find that @retailcoder is a great explainer 🥇, I really like his articles, would love to read something new. I know that tB is here and find it great but for now I cant use it so it remains the good old VBA. :)

I would also like to have a working framework and not as a library, so I started to adapt it to my needs.

What I find difficult to understand is when things get complex lets say we have a View and many controls on different frames,
how would I couple to the main ViewModel, this is where I'm kind of stuck.

Found this explanation on the website but without an example don't really understand it.

ViewModel
Every ViewModel class is inherently application-specific and will look different, but there will be recurring themes:

Every field in the View wants to bind to a ViewModel property, and then you’ll want extra properties for various other things, so the ViewModel quickly grows more properties than comfort allows. Make smaller “ViewModel” classes by regrouping related properties, and bind with a property path rather than a plain property name.

Property changes need to propagate to the “main” ViewModel (the “data context”) somehow, so making all ViewModel classes fire a PropertyChanged event as appropriate is a good idea. Hold a WithEvents reference to the “child” ViewModel, and handle propagation by raising the “parent” ViewModel’s own PropertyChanged event, all the way up to the “main” ViewModel, where the handler nudges command bindings to evaluate whether commands can execute. One solution could be to register all command bindings with some CommandManager object that would have to implement IHandlePropertyChanged and would relieve the ViewModel of needing to do this.

Especially this part:
Property changes need to propagate to the “main” ViewModel (the “data context”) somehow, so making all ViewModel classes fire a PropertyChanged event as appropriate is a good idea. Hold a WithEvents reference to the “child” ViewModel, and handle propagation by raising the “parent” ViewModel’s own PropertyChanged event, all the way up to the “main” ViewModel
For this part an example would be great.

@retailcoder can you please detail how you would implement this? I would appreciate it, thanks.

@retailcoder
Copy link
Member

retailcoder commented Apr 29, 2022

In WPF/XAML, each UI element inherits a DataContext object from its parent element, so you set that property to the view model, and then the bindings in UI element properties are all relative to this DataContext, so when you bind the value of a TextBox to the Description property of your view model object, the context of the binding is the DataContext inherited from the parent UI element.

The property might look something like this in the ViewModel:

private string _description;
public string Description
{
    get { return _description; }
    set
    {
        if (_description != value)
        {
            _description = value;
            OnPropertyChanged(nameof(Description));
        }
    }
}

Where value is the RHS assignment value provided by the caller (C# makes this seem a little bit magical coming from VBA!), and OnPropertyChanged is a helper private method that fires the PropertyChanged event mandated by the framework's INotifyPropertyChanged interface.

So the idea was to replicate this mechanism and make it work in classic-VB - obviously without fancypants XAML markup or an elaborate object/inheritance hierarchy, but it seemed to me (still does) nothing in VBA/VB6 inherently makes MVVM completely impossible. Not without challenges (the impossibility for an abstract interface to expose a Public Event, for one; organizing so many classes too!), but certainly not impossible. Rubberduck's Code Explorer and annotations system (eh, Rubberduck as a whole!) has absolutely been a life saver here.

When the UI is a simple form, you write a single ViewModel class and use it as your DataContext; the binding paths then refer to ViewModel properties by name e.g. "{binding Description}", and the engine will be looking for a Description property in that context.

But sometimes you need a more complex UI and the ViewModel class begins to get really crowded... with properties that can often easily be bunched into groups of closely related things... and be pulled into a "child" ViewModel class to tidy things up (Single Responsibility Principle 😉).

In such cases the ViewModel exposes a get-only property that bindings can use to refer to the "child" VM, and so the binding path accordingly looks like {binding Child.SomeProperty}:

private readonly ChildViewModel _child;
public ChildViewModel Child
{
    get { return _child; }
}

So that's why property binding paths get evaluated (recursively) the way they are, but then the child VM needs to fire property change notifications... and we want our VBA MVVM code to register a single VM per View/form, but then with how the MVVM infrastructure does it, that makes the "parent" ViewModel the only INotifyPropertyChanged we're handling property changes for, and it's on the client VBA code to propagate this notification from any child VM.

What this means is that the "child" view model classes should expose a Public Event PropertyChanged, and the "parent" view model should hold the reference to the child VM with a Private WithEvents module variable, and propagate the event to the Notifier helper, passing the ChildViewModel as a source:

Private WithEvents ChildVM As ChildViewModel

Private Sub OnPropertyChanged(ByVal PropertyName As String)
    This.Notifier.OnPropertyChanged Me, PropertyName
End Sub

Private Sub Class_Initialize()
    Set This.Notifier = New PropertyChangeNotifierBase
End Sub

Private Sub INotifyPropertyChanged_OnPropertyChanged(ByVal Source As Object, ByVal PropertyName As String)
    This.Notifier.OnPropertyChanged Source, PropertyName
End Sub

Private Sub INotifyPropertyChanged_RegisterHandler(ByVal Handler As IHandlePropertyChanged)
    This.Notifier.RegisterHandler Handler
End Sub

Private Sub ChildVM_PropertyChanged(ByVal Name As String)
    This.Notifier.OnPropertyChanged ChildVM, PropertyName
End Sub

The child VM can then use standard VB events to propagate changes into the MVVM binding and command managers:

Public Event PropertyChanged(ByVal Name As String)

Private Type TViewModel
    Description As String
    '...
End Type
Private This As TViewModel

Public Property Get Description() As String
    Description = This.Description
End Property

Public Property Let Description(ByVal Value As String)
    If This.Description <> Value Then
        This.Description = Value
        OnPropertyChanged "Description"
    End If
End Property

'...

Private Sub OnPropertyChanged(ByVal Name As String)
    RaiseEvent PropertyChanged(Name)
End Sub

Child view models could also propagate changes through the INotifyPropertyChanged mechanism, but it would be overkill to do so because at the end of the day the binding and command managers are only listening to the parent VM's notifications; the Source parameter holds a reference to the VM (child or parent) that emitted the notification, so everything just works.

Eh, battery is running out, hope it helps!

@tothzola
Copy link

tothzola commented May 1, 2022

@retailcoder

Thank you for the quick detailed reply!
Will start refactoring some things! ☺️

Oh by the way how does RubberduckVBA going when do we have a new release and do you plan some new articles on the site? Looking forward to it!!

Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants