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

Add design-time view models to all samples #246

Closed
TysonMN opened this issue Jul 23, 2020 · 17 comments · Fixed by #249
Closed

Add design-time view models to all samples #246

TysonMN opened this issue Jul 23, 2020 · 17 comments · Fixed by #249

Comments

@TysonMN
Copy link
Member

TysonMN commented Jul 23, 2020

This is a spinoff of #244 (comment).

The samples are more instructive if they also show how design-time support works. It is a relatively minor change to add this support, so the plan is to make this happen. In particular, the C# project will become the executable project with the F# project as a dependency (which is the opposite of what it is now).

I have one question. Each sample currently has the following naming convention for its projects:

  • F#: SampleName
  • C#: SampleName.Views

Since this change will swap the dependency relationship between the two projects, do we also want to change the names? Here are some suggestions.

  1. Keep them the same
  • F#: SampleName
  • C#: SampleName.Views
  1. Swap the primary/secondary naming to something like
  • F#: SampleName.Models
  • C#: SampleName
  1. Have an explanatory suffix on both projects with something like
  • F#: SampleName.Models
  • C#: SampleName.Views
@ScottHutchinson
Copy link
Contributor

I don't understand why this change is needed. If it is needed, then I think you could leave the project names as is.

@TysonMN
Copy link
Member Author

TysonMN commented Jul 23, 2020

Which change do you think is not needed? The change to add design-time support or the idea to change the project naming convention?

@ScottHutchinson
Copy link
Contributor

The change to add design-time support. Does it refer to the designer for the XAML file(s)?

@TysonMN
Copy link
Member Author

TysonMN commented Jul 23, 2020

It refers to the design-time creation of a view model that uses the Elmish.WPF bindings to display data in the designer.

I think a picture (or two!) is worth a thousand words here.

Consider the the designer for the file MainWindow.xaml in the SingleCounter sample. In master, it looks like...

2020-07-23_11-25-48_196_devenv

...but in this branch, which adds this "design-time support" for this sample, it looks like

2020-07-23_11-25-01_195_devenv

The values 0 and 1 in the designer are coming from lines 17 and 18 in the above file. If you change them and recompile, then those new values will appear in the designer.

The present issue about adding design-time support to all samples it to replicate the change made to the SingleCounter sample in that branch to all the samples.

@ScottHutchinson
Copy link
Contributor

That definitely clarifies things. And that is very interesting. I've always had the F# project as the .exe in all my WPF F# solutions. I never knew what I was missing.

@TysonMN
Copy link
Member Author

TysonMN commented Jul 23, 2020

If you only have one F# project, then also having this design-time support means that the C# project must contain the entry point. Because, as the README says, the XAML needs to be able to reference the bindings.

With two F# projects, it is possible to keep an F# project as the executable and also have this design-time support. It would look like this.

  • F# A: Contains the entry point. Depends on B to instantiate MainWindow and C to get init, update, and bindings.
  • C# B: Contains all XAML including MainWindow. Depends on C to get the design-time view model from the bindings.
  • F#: C: Contains init, update and bindings.

I don't see any advantage to this though.

There is more discussion about various solution structures in #93.

@cmeeren
Copy link
Member

cmeeren commented Jul 23, 2020

Since this change will swap the dependency relationship between the two projects, do we also want to change the names?

I think we should change them, but I'm unsure what to. The F# project contains both the core domain logic (.Core, .Domain), but also the bindings and other hypothetical UI-related helpers. The C# project contains the views (.Views, .UI), but also the entry point (no suffix, .App).

Perhaps .Core and .UI? I'm open to other suggestions.

@ScottHutchinson
Copy link
Contributor

The names seem fine as is. Don't make work for yourself. They're just samples.

@cmeeren
Copy link
Member

cmeeren commented Jul 23, 2020

I appreciate your concern, but that's exactly the point - they're samples, intended for learning and demonstrating good practices. With naming things being one of the hard things in software development, a minimum of consideration about names in the samples is entirely appropriate. 😊

@TysonMN
Copy link
Member Author

TysonMN commented Jul 25, 2020

The main reason I think we should consider a name change now is that swapping the executable project while leaving the names the same is likely to be confusing for anyone that is already familiar with the samples. If we also change the names, then I think they will be a bit less surprised when they try to start an F# project and Visual Studio tells them that it is not executable.

I think the most accurate names would simply be FSharp and CSharp, but they might not be the most helpful.

@cmeeren
Copy link
Member

cmeeren commented Jul 25, 2020

I think the most accurate names would simply be FSharp and CSharp, but they might not be the most helpful.

I agree they are accurate but not particularly helpful. I favor semantics over implementation details.

@TysonMN
Copy link
Member Author

TysonMN commented Jul 26, 2020

As I see it, there is no good description for the F# projects because they contains too many things. I think of it as containing

  1. Model, Msg, CmdMsg, and update, which have no dependencies,
  2. CmdMsg -> Cmd<Msg>, which depends on Elmish, and
  3. the bindings, which depends on Elmish.WPF.

I feel like the current names of the F# projects are best then.

For the C# project, I feel like Views and UI are synonyms. I don't mind changing from Views to UI, but I am mostly indifferent.

@cmeeren
Copy link
Member

cmeeren commented Jul 26, 2020

Given the present difficulty, we can always keep the current names and change them later, when we or someone else comes up with something better.

@TysonMN TysonMN changed the title Add design-time support to all samples Add design-time view models to all samples Jul 27, 2020
@MicaelMor
Copy link

If you only have one F# project, then also having this design-time support means that the C# project must contain the entry point. Because, as the README says, the XAML needs to be able to reference the bindings.

With two F# projects, it is possible to keep an F# project as the executable and also have this design-time support. It would look like this.

  • F# A: Contains the entry point. Depends on B to instantiate MainWindow and C to get init, update, and bindings.
  • C# B: Contains all XAML including MainWindow. Depends on C to get the design-time view model from the bindings.
  • F#: C: Contains init, update and bindings.

I don't see any advantage to this though.

There is more discussion about various solution structures in #93.

There might be some advantages with a solution similar to the one you posted with regard to instantiating new windows, although I haven't tested it so it might not work (especially for design-time VM).

The structure would be the following structure:

F# A: Contains the entry point and bindings. Depends on B to instantiate MainWindow and C to get init, update.
C# B: Contains all XAML including MainWindow and any other windows. Depends on C to get IValueConverters.
F#: C: Contains init, update.

This would I think allow for the bindings in project A to initiate windows using Binding.subModelWin without having the current problem of having to pass functions of type "unit -> window" to the initialization, which could be a bit of an issue if a project has a significant number of submodel windows, as can be seen by your update of the NewWindow sample, where there is clearly already some extra steps to get it to work with just 2 windows.

@TysonMN
Copy link
Member Author

TysonMN commented Aug 4, 2020

Yes, I think that's right @MicaelMor. Some bindings need to know how to create a Window and some Windows/UserControls want to bind at design time to a view model, which is created from the bindings. Therefore, if you want to implement both, then at least one side needs to obtain its data (namly a Window or view model) via a layer of indirection.

@cmeeren
Copy link
Member

cmeeren commented Aug 4, 2020

There might be some advantages with a solution similar to the one you posted with regard to instantiating new windows, although I haven't tested it so it might not work (especially for design-time VM).

@bender2k14 essentially said it, but to make it entirely explicit: No, design-time VMs won't work with that structure, since the XAML doesn't reference the bindings.

As @bender2k14 says, you need a layer of indirection, but AFAIK there's no way to do that in XAML for design-time data, so if you want both, you have to make the XAML reference the bindings and pass the window constructors to update. If you find lots of unit -> Window parameters to be a problem, you can of course just wrap them all up in a nice interface that the entry point implements and passes to update.

@TysonMN
Copy link
Member Author

TysonMN commented Aug 4, 2020

...AFAIK there's no way to do that in XAML for design-time data...

Oh, I think you are right. I had some ridiculous hack in mind, but while trying to write it down, I realized it doesn't work.

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

Successfully merging a pull request may close this issue.

4 participants