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

Source Generators for specialized ReactiveUI controls (Winforms, WPF, etc) #35

Open
Micha-kun opened this issue Aug 27, 2024 · 10 comments

Comments

@Micha-kun
Copy link

I'm working with ReactiveUI in a WinForms app with DevExpress but I'm having a hard time because ReactiveUI winforms controls (ReactiveUserControl, RoutedControlHost, RoutedControlHost , etc) inherits directly from UserControl, and because i'm working with DevExpress, I'm losing DevExpress benefits like Skin customization or better UI integration because they doesn't implement XtraUserControl (UserControl from DevExpress). The only thing I can do is go to ReactiveUI Source Code, copy the source from those controls and change in my code UserControl for XtraUserControl. And the pain is that I need to check every time ReactiveUI has upgraded if the Source Code changed and update manually.

But now in the Source Generators era... Maybe we can make it easy this "specialization". Why not create a SourceGenerators for Winforms (ReactiveUI.SourceGenerators.WinForms) that would generate an specialization by attribute (like [ReactiveUserControlAttribute()] that can get a type parameter as argument implying which base class to inherit, so if no parameter used, standard UserControl class is applied? Could be an assembly attribute, so only a single time is required to write, or an specialization per class if required.

It's doable? I think it can be exportable to any technology that currently ReactiveUI has specialized controls (WPF, etc)

Thank you for your attention

@ChrisPulman
Copy link
Member

ChrisPulman commented Aug 27, 2024

https://github.com/reactiveui/ReactiveUI.SourceGenerators?tab=readme-ov-file#usage-iviewfor-iviewfornameofviewmodelname
Is this kind of functionality what you are hoping for?

GitHub
Use source generators to generate objects. . Contribute to reactiveui/ReactiveUI.SourceGenerators development by creating an account on GitHub.

@Micha-kun
Copy link
Author

Micha-kun commented Aug 27, 2024

No, that's a helper to implement IViewFor interface (and it's pretty good). I'm talking about specific controls that exists in ReactiveUI.Winforms NuGet like this:

https://github.com/reactiveui/ReactiveUI/blob/main/src/ReactiveUI.Winforms/ReactiveUserControl.cs

Those controls are used as base classes, and they force it's parent as (in this case) UserControl base class:

public partial class ReactiveUserControl<TViewModel> : **UserControl**, IViewFor<TViewModel> where TViewModel : class
What would be incredible is if those base controls where generated via Source Generators and that UserControl parent reference be changed by our custom base control, so, in case of DevExpress, could use XtraUserControl and gain benefits of both functionalities.

GitHub
An advanced, composable, functional reactive model-view-viewmodel framework for all .NET platforms that is inspired by functional reactive programming. ReactiveUI allows you to abstract mutable st...

@ChrisPulman
Copy link
Member

Technically the [IViewFor(nameof(MyViewModel))] Attribute will do exactly what you are wishing, it hasn't yet been released but we are working on getting our release mechanism ready to produce a release

using ReactiveUI.SourceGenerators;

namespace MyApp.WinForms;

[IViewFor(nameof(MyViewModel))]
public partial class MyReactiveUserControl : MyBaseUserControl
{
		/// <summary>
		/// Initializes a new instance of the <see cref="MyReactiveUserControl "/> class.
		/// </summary>
		public MyReactiveUserControl()
		{
		    InitializeComponent();
		    ViewModel = new MyViewModel();
		}
}

This will produce the following additional code which should maintain compatibility with the WinForms Designer.

using ReactiveUI;
using System.ComponentModel;

// <auto-generated/>
#pragma warning disable
#nullable enable
namespace MyApp.WinForms
{
    [global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.IViewForGenerator", "1.1.0.0")]
    public partial class MyReactiveUserControl : IViewFor<MyViewModel>
    {
        /// <inheritdoc/>
        [Category("ReactiveUI")]
        [Description("The ViewModel.")]
        [Bindable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public MyViewModel? ViewModel { get; set; }

        /// <inheritdoc/>
        object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = (MyViewModel? )value; }
    }
}
#nullable restore
#pragma warning restore

@Micha-kun
Copy link
Author

Micha-kun commented Aug 28, 2024

Well, in the case of ReactiveUserControl, this could be the replacement. But there are other specialized controls, like RoutedControlHost or ViewModelControlHost that are more than that:

https://github.com/reactiveui/ReactiveUI/blob/main/src/ReactiveUI.Winforms/RoutedViewHost.cs

https://github.com/reactiveui/ReactiveUI/blob/main/src/ReactiveUI.Winforms/ViewModelViewHost.cs

Those controls are the ones that currently could improve if we could change which base class is using (for example, instead of UserControl, use XtraUserControl).

Do you think is a bad idea?

GitHub
An advanced, composable, functional reactive model-view-viewmodel framework for all .NET platforms that is inspired by functional reactive programming. ReactiveUI allows you to abstract mutable st...
GitHub
An advanced, composable, functional reactive model-view-viewmodel framework for all .NET platforms that is inspired by functional reactive programming. ReactiveUI allows you to abstract mutable st...

@ChrisPulman
Copy link
Member

Okay this is a valid idea, I will try to put something together in the next few days to cover these too. All platforms could benefit from this too.

@Micha-kun
Copy link
Author

Micha-kun commented Aug 28, 2024

Great to hear that! I was thinking it would require an specialized version of IViewFor attribute but implementing only the generic part of IViewFor, not the non-generic part, because the non-generic part would be on those controls and the property implementation would use the base ViewModel property from ReactiveUI specialized control. Something like this:

using ReactiveUI;
using System.ComponentModel;

// <auto-generated/>
#pragma warning disable
#nullable enable
namespace MyApp.WinForms
{
    [global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.IViewForGenerator", "1.1.0.0")]
    public partial class MyReactiveUserControl : IViewFor<MyViewModel>
    {
        /// <inheritdoc/>
        [Category("ReactiveUI")]
        [Description("The ViewModel.")]
        [Bindable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public MyViewModel? ViewModel { get =>  (MyViewModel?)base.ViewModel; set => base.ViewModel = value; }
    }
}
#nullable restore
#pragma warning restore

@Micha-kun
Copy link
Author

Won't be better to separate those specialized WinForms controls in a separate Nuget? Like ReactiveUI.SourceGenerators.WinForms. So each platform could have it's specialized nuget (Wpf, etc). They can require the "Core" one so everything is integrated.

@ChrisPulman
Copy link
Member

As the code is generated in your project and you get no reference to the Source Generators in your final assembly I don't see the benefits of having separated packages, opted for the namespace difference to identify the functionality is for a particular platform.
I need to look at the Wpf specialist functionality more carefully to work out the way to implement them properly.

@ChrisPulman
Copy link
Member

V1.1.26 has been released, please try the WinForms Generators included

@Micha-kun
Copy link
Author

Ok, after testing it's a great solution but I think it can be easier if it wouldn't require to pass as argument the base type. Base type can be applied directly in the "master class" as this:

[RoutedControlHost]
public partial class TestWinFormsRCHost : XtraUserControl
{
}

What do you think?

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

4 participants
@Micha-kun @ChrisPulman and others