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

Windows form how to call forms correctly from container #97

Open
Kingdom-Of-Heaven opened this issue May 25, 2019 · 5 comments
Open

Windows form how to call forms correctly from container #97

Kingdom-Of-Heaven opened this issue May 25, 2019 · 5 comments

Comments

@Kingdom-Of-Heaven
Copy link

Kingdom-Of-Heaven commented May 25, 2019

I've registered my FormUserNew:

_container.Register<FormUsersNew>();

which is called from FormMain (starting form), however i am not sure how should i take instance of that in FormMain then call it.

Here's my code below:

static class Program
  {
      static void Main()
      {        
          Application.EnableVisualStyles();
          Application.SetCompatibleTextRenderingDefault(false);
          Bootstrap();

          Application.Run(_container.GetInstance<FormMain>());

      private static void Bootstrap()
      {
          _container = new Container();
          _container.Options.DefaultScopedLifestyle = new ThreadScopedLifestyle();
          _container.Register<IUserService, UserService>(Lifestyle.Scoped);
          _container.Register<IUserTypeService, UserTypeService>(Lifestyle.Scoped);     
          _container.Register<FormMain>();
          _container.Register<FormUsersNew>();   //<=========== form
          _container.Verify();         
      }
  }

FormUsersNew:

 public partial class FormUsersNew
    {
        private readonly Sektor _sektor;
        private readonly IUserService _userService;
        private readonly IUserTypeService _userTypeService;

        public FormUsersNew(IUserService userService, IUserTypeService userTypeService, EnumSector sector)
        {
            InitializeComponent();
            _sektor = sektor;
            _userService = userService;
            _userTypeService = userTypeService;
        }

How then to call FormUsersNew to use my dependencies from container? :

public partial class FormMain
{

   private void BtnOpen_Click(object sender, EventArgs e)
   {
         //call FormUserNew from container <=======================
         //var form = new FormUsersNew();
         //form.ShowDialog();
   }
}
@Kingdom-Of-Heaven Kingdom-Of-Heaven changed the title Windows form how to call forms correctly Windows form how to call forms correctly from container May 25, 2019
@dotnetjunkie
Copy link
Collaborator

What you can try is hiding the management around showing (and disposing) your forms behind an abstraction. For instance:

public partial class FormMain
{
    private IFormManager manager;

    public FormMain(IFormManager m) => manager = m;

     private void BtnOpen_Click(object sender, EventArgs e)
    {
        this.manager.ShowDialog<FormUsetsNew>();
    }
}

Such manager can be implemented as follows:

class Manager : IFormManager
{
    private Container container;

    public Manager(Container c) => container = c;

    public void ShowDialog<TForm>()
where TForm : Form
    {
         using (var form = container.GetInstance<TForm>())
         {
             form.ShowDialog();
         }
    }
}

I'm sure @TheBigRic can give you a more detailed, and better, answer.

@Kingdom-Of-Heaven
Copy link
Author

Kingdom-Of-Heaven commented May 26, 2019

hmm, and what about if another form is called from FormUserNew so we would have chain like: FormMain (starting form) --> FormUserNew --> FormOther (how handle this form?)

I was also thinking about instead forms having their dependencies inside constructor to have dependencies as properties and in constuctor body give default dependency value - what about that? in this case i don;t need to give form;s constructor arguments but still can test in unit tests. What you think?

P.S How it's accmomplished in WPF ? If not mistaken there is also main form so same workaround could be done?

@dotnetjunkie
Copy link
Collaborator

what about if another form is called from FormUserNew so we would have chain like: FormMain (starting form) --> FormUserNew --> FormOther (how handle this form?)

In that case you would inject IFormManager in FormUserNew as well.

How it's accmomplished in WPF ? If not mistaken there is also main form so same workaround could be done?

I've actually written how to integrate with UWP in my book, which is very similar to WPF. You can read the section about UWP online here. Manning allows you to read part of that section for free, so it might be enough to get started.

@TheBigRic
Copy link
Contributor

I once wrote an answer on stack overflow with a very similar but more complete IFormManager although I called it IFormOpener. You can find this here

hmm, and what about if another form is called from FormUserNew so we would have chain like: FormMain (starting form) --> FormUserNew --> FormOther (how handle this form?)

You can use the same IFormOpener/IFormManager. I think however from a users point of view showing multiple screens with subtasks should avoided. Try to show a main screen with views of data and modal screens on top to edit or create new data. This screens could be build up from multiple usercontrols to get a good separation.

I was also thinking about instead forms having their dependencies inside constructor to have dependencies as properties and in constuctor body give default dependency value - what about that?

Why? The Windows Forms Designer can handle a non default ctor pretty well. So don't do this, you do not need it I think.

@Kingdom-Of-Heaven
Copy link
Author

Kingdom-Of-Heaven commented May 27, 2019

@TheBigRic I have to more questions:

1. Let's say there is one constructor parameter for some form besides normal dependencies. How can i pass value not using container?

Example:
FormX(IUserRepo, IOrderRepo, EnumSector)

IUserRepo and IOrderRepo will be passed through container using your method by FormOpener. But how can i pass EnumSector?

I mean i could do in container staticly :
_container.RegisterInitializer<FormUsersNew>(instance => { instance._enumSekctor= EnumSector.Fire; });

But i would prefer to say in this place dynamically somehow:

private void BtnAddUser_Click(object sender, EventArgs e)
{
        var ret = this.formOpener.GetForm<FormUsersNew>();
       //ret.EnumSector = EnumSector.Fire <==============
        ret.ShowDialog();
}
  1. Is there any way to get real instance type of form?

We have:

var myform = this.formOpener.GetForm<FormUsersNew>();
ret.ShowDialog();

i mean i want myForm to be of type FormUsersNew - the real instance type (not type of Form)

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