NEWBIE - Help required with UI based on Ptracker

NEWBIE - Help required with UI based on Ptracker

Old forum URL: forums.lhotka.net/forums/t/4673.aspx


Warren posted on Friday, April 11, 2008


In the last few months I have been learning OOP, VB.NET, CSLA and CSLAGen. I have a small app up and running based on PTracker complete with WinPart, StatusBusy etc. Creating the UI is turning out to be harder than I expected mostly due to my inexperience with .NET and OOP.

I have used the PTracker design in that a main form dynamically loads users controls instead
of a design based on multiple forms. My first CSLA objects are somewhat data centric but are
based on use cases and have a single responsibility. These objects are responsible for editing the
base data. Later the I plan to develop the more business oriented objects. To edit base data I use 3
objects, Editable Root, Readonly Collection and Readonly object like PTracker. 

PTracker  used separate menu options for creating editing or deleting an object. I would like to build
an UI which provides 1 logical form (actually a user control) for the user to perform add, change or delete in one place without needing to access a menu option each for each function. Something like a user control (form) with search(finds record to edit or delete), new, delete, save, and exit buttons. I am also wondering if there is a way to build these buttons and reuse them on each user control via inheritance.

Has anyone done this with the PTtracker model and be willing to share some ideas to help me speed things up?  Alternatively, does anyone have a shell of a VB.Net UI that uses CSLA and a single form one stop shopping approach for editing base data that I could review to assist me?

I am also interested to hear if anyone started with Ptracker but converted it to be able to view multiple forms at one time, - is that MDI vs SDI?

I convinced my shop to allow my to use CSLA and I still believe it was the right decision since more apps are likely to follow but learning curve has been rather steep and I need to push out my prototype soon.

Thanks

Warren

 

 

 

Warren replied on Monday, April 14, 2008

I am thinking that the reason I didn't get a reply on my original post was because the questions I am asking are too broad so I'll narrow it down and be more specific with individual questions.

Now that I have a decent handle on CLSA and code generation via CSLAGen, I want to make sure I my application's architecture and UI design is solid before I add more forms for editing my base objects. 

I now have the start of an application built upon the PTtracker example application. As per Rocky's advice on best practices for WinForms apps I'd like to continue with my forms as user controls instead of Forms.  Just like PTracker I have a mainform with a panel control which contains the individual user controls which implement my forms.

My design consists of approximately 50 forms. Do you think it is acceptable design to have MainForm include code which references the 50 forms as per PTracker or am using the PTracker architecture to do something it was never intended to?

Thanks in advance.

Warren


 

dtabako replied on Tuesday, April 15, 2008

Hi Warren,

I'm developing an app much like yours only bigger in scope (probably 350-400 forms) so I'll try and answer your questions. I am also fairly new to this so I'll tell you what I've got and you can take it for what it's worth.

For the main form I virtually duplicated the PTracker MainForm. But of course, I had to come up with something on my own for the menu. With that many forms (which are, indeed, WinPart user controls) I obviously needed something a bit more dynamic than just adding menu selections and a "click" event for each. What I did was build my menu with ToolStripDropDownButtons with selections that contain subselections (which may contain subselections, etc.). I have one single event handler that handles the "Click" event for all ToolStripMenuItems that actually open a form. The code for that method is as follows (sorry, it's C#):

private void chosenToolStripMenuItem_Click(object sender, EventArgs e)

{

Assembly currentAssembly = Assembly.GetAssembly(typeof(MainForm));

ToolStripMenuItem mi = (ToolStripMenuItem)sender;

string classToInstantiate = "VisionWinForms." + (string)mi.Tag;

object selectedForm = currentAssembly.CreateInstance(classToInstantiate);

ShowSelectedForm((WinPart)selectedForm);

}

...where 'VisionWinForms' is the name of my project. Then on each ToolStripMenuItem I put the class name of form (user control) I want to open in the Tag property. It has to include any namespace portion as well so that your classToInstantiate string can contain a fully qualified classpath. For example, my Tax Code Maintenance form is in my CS classpath. So for its corresponding ToolStripMenuItem, the Tag property would be set to CS.TaxCodeMaintenance. This works well for me - whenever I add a new form all I do is add a new menu item, set its tag property, and set it's click event to my chosenToolStripMenuItem_click event and I'm all set.

As for your other question of an all-in-one setup for creating, editing, saving, deleting all from the same form. I have that setup as well. It took only slight modification to the PTracker model to get it done. The first thing I did was create a user control that handles record selection and contains the New, Save, Cancel, and Delete buttons (although looking back I'm not sure how much I gained from breaking out what I did into a user control other than it takes a little less time to place controls on the form). Record selection is done via a comboBox that utilizes the readOnlyList collection of readOnly Info objects. The only read tricky parts are:

- Knowing when to call RebindUI and what values to pass it in certain situations.

- Making sure you do IsDirty checks in all the right spots: anytime a record changes, whenever a new record request is made, when the for is closed, etc. so you can prompt the user to save any unsaved changes.

It took a little trial and error but I have a very workable standard solution to my object edit forms (we call them "file maintenances"). If you have specific questions about that let me know and I'll answer as best I can.

Dennis

Warren replied on Wednesday, April 16, 2008

Hi Dennis,

Thanks so much for answering my questions, it is very helpful to see how you have modified the Ptracker design. I would like to do something similar in my application.

I was concerned about Mainform becoming too lengthy due to the handling of code for each control. My Mainform is a clone of the PTracker version, for editing my Bank objects I have:

Private Sub NewBankToolStripMenuItem_Click
Private Sub EditBankToolStripMenuItem_Click
Public Sub ShowEditBank(ByVal bankId As Integer
Private Sub DeleteBankToolStripMenuItem_Click

In your design, are these moved to the user control class?

I built a VB version of your chosenToolStripMenuItem to test this out:

Private Sub chosenToolStripMenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles DeleteBankToolStripMenuItem.Click

Dim currentAssembly As Assembly = Assembly.GetAssembly(GetType(MainForm))

Dim mi As ToolStripMenuItem = DirectCast(sender, ToolStripMenuItem)

Dim classToInstantiate As String = "OffsetInterestWin." + DirectCast(mi.Tag, String)

Dim selectedForm As Object = currentAssembly.CreateInstance(classToInstantiate)

'ShowSelectedForm(DirectCast(selectedForm, WinPart))

End Sub

If you create an instance of the control in this manner, how do you get the PTtracker  behavior which cycles through the users controls and to see if the object is already loaded.  Is that what you do in ShowSelectedForm and if so I'm confused with the reason for the CreateInstance code.

Warren

dtabako replied on Wednesday, April 16, 2008

Hi Warren,

My New, Save, Cancel, and Delete buttons are on the user control classes rather than the main form's menu strip. The biggest reason for this I guess is because I have many types of forms for which those buttons are not appropriate (inquiry listing, processing functions, reports, etc.). So it's just easier for me to implement them only on forms where they are appropriate. If those buttons are appropraite on all your forms, you should be able to implement them from the main form menu easily enough.

As for the code in the chosenToolStripMenuItem_click, if you were to write a click event handler for each user tool strip menu item's click event, they would likely be 1 line of code each:

ShowSelectedForm(new SampleForm());

Basically you're doing the same thing. You're still just doing ShowSelectedForm (which handles all the stuff with open documents and adding the new form to the open documents list, etc.). All the other code in the method really just replaces: "new SampleForm()". Because you're trying to create 1 generic method that instantiates a class based on a string value, you need to use that Assembly.CreatInstance() method instead of a 'new xxxx()' instantiation.

Dennis

Warren replied on Wednesday, April 16, 2008

Hi Dennis,

My project manager wants the New Save, Cancel buttons on each formal also so I will need to figure that out next also.

I understand that chosenToolStripMenuItem_Click uses reflection in order to use CreateInstance but am still confused as to why you need to invoke CreateInstance when you may want to simply make the control visible by bringing it to the front if it already exists as per the PTracker model. You mention that ShowSelected Form handles this functionality.

I know this is likely fundemental .NET stuff I am missing here but if you have an existing control  in memory and you call CreateInstance, don't you end up with a duplicate?

Thanks

dtabako replied on Thursday, April 17, 2008

Warren,

That's where the "GetIdValue" method that you put in all your WinPart forms comes into play. You pass 'ShowSelectedForm' an instance of the form (user control) you want to display. If you look at 'ShowSelectedForm', you see a loop that looks through all the forms that are already created and checks their 'GetIdValue' against that of the form instance you passed in. If there's a match, ShowSelectedForm opens that form, effectively discarding the instance you passed in. If there's not, it uses your instance to create a new form.

You can actually use this to go two routes: there's the route I went where you can only have 1 instance of each form open at once. In that case, you would make your "GetIdValue" methods in your forms pass something that will be the same for each form - like the class name or something. Or there's the second route where you can allow users to have multiple instances of the same form open. To do this, you would pass something that could be different based on the data currently in use in the form - like the class name plus the id value of the current record.

Dennis

Warren replied on Thursday, April 17, 2008

Hi Dennis,

I am stuck at the following line in chosenToolStripMenuItem_Click, "constructor not found". I believe

 

Private Sub chosenToolStripMenuItem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles DeleteBankToolStripMenuItem.Click

Dim currentAssembly As Assembly = Assembly.GetAssembly(GetType(MainForm))

Dim mi As ToolStripMenuItem = DirectCast(sender, ToolStripMenuItem)

Dim classToInstantiate As String = "OffsetInterestWin." + DirectCast(mi.Tag, String)

Dim selectedForm As Object = currentAssembly.CreateInstance("OffsetInterestWin.BankEdit")

ShowSelectedForm(DirectCast(selectedForm, WinPart))

End Sub

 

 

Warren replied on Thursday, April 17, 2008

Opps, please disregard previous unfinished message, the time period for deletes has elapsed.

Hi Dennis,

I am wondering how you managed to implement CreateInstance in your 'chosenToolStripMenuItem' method without passing any parameters for the constructor of the form object. Did you modify the constructor in the user control so it doesn't require parameters. My constructor for 'BankEdit', based on Ptracker 'ProjectEdit', looks like this:

Public Sub New(ByVal bank As Bank)
InitializeComponent()
mBank = bank
BindUI()
ApplyAuthorizationRules()

which expects a parameter so CreateInstance Fails.

 

 

 

dtabako replied on Friday, April 18, 2008

Hi Warren,

Yes my user controls all have default constructors that do not have parameters. I haven't researched createInstance deeply enough to know if that's a requirement but one thing you could do is give your user controls empty constructors and create a public property for the info you want to pass to it. So the pseudocode in chosenToolStripMenuItem_click would be something like:

yourForm = createInstance(yourClassName);

yourForm.DataToPass = initializationDataObject;

ShowSelectedForm(yourForm);

You might have to play around with castings a little to get that to work, but I think it can be done.

Dennis

sergeyb replied on Friday, April 18, 2008

CreateInstance method has an overload that takes parameter(s) as array of objects.

 

Sergey Barskiy

Senior Consultant

office: 678.405.0687 | mobile: 404.388.1899

cid:_2_0648EA840648E85C001BBCB886257279
Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

 

From: dtabako [mailto:cslanet@lhotka.net]
Sent: Friday, April 18, 2008 9:23 AM
To: Sergey Barskiy
Subject: Re: [CSLA .NET] UI based on Ptracker

 

Hi Warren,

Yes my user controls all have default constructors that do not have parameters. I haven't researched createInstance deeply enough to know if that's a requirement but one thing you could do is give your user controls empty constructors and create a public property for the info you want to pass to it. So the pseudocode in chosenToolStripMenuItem_click would be something like:

yourForm = createInstance(yourClassName);

yourForm.DataToPass = initializationDataObject;

ShowSelectedForm(yourForm);

You might have to play around with castings a little to get that to work, but I think it can be done.

Dennis



Warren replied on Monday, April 21, 2008

Thanks Sergey, Dennis and ozitraveller for your responses,

I resolved my issue with Creatinstance for now by using a parameterless constructor in the user object. Like Dennis, I am utilizing a design based on PTracker with a difference being
my winpart user controls (which I call maintenance forms) for editing base data, have their own
New/Save/Delete buttons instead of using the buttons on Mainform.

At the moment I am trying to determine best way to implement the constructor and strategy for GetIdValue in the user control.  At the point when the user control is instantiated I don't know if the user wants to add, edit or delete a record. Consequently, I'm not sure what the default constructor should do. 

If my design specifies that there can only be a single instantiation of a user control form, then if I understand correctly as per what Dennis said and Chapter 3 is that it would be adequate if GetIdValue just returns the business object being editied. Does this mean I need to create the business object at the time user control is instantiated even before I know what the user wants to do?

Thanks


 

sergeyb replied on Monday, April 21, 2008

I wonder if simplest solution would be to have add this functionality to your base user control.  For example, you can add sub Post Initialize to your base control that would take description of user action as a parameter.   Even cleaner, have all your user controls implement an interface that you need to tell each control what it needs to do after initialization.  Sounds reasonable?

 

 

Sergey Barskiy

Senior Consultant

office: 678.405.0687 | mobile: 404.388.1899

cid:_2_0648EA840648E85C001BBCB886257279
Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

 

From: Warren [mailto:cslanet@lhotka.net]
Sent: Monday, April 21, 2008 8:06 AM
To: Sergey Barskiy
Subject: Re: [CSLA .NET] RE: UI based on Ptracker

 

Thanks Sergey, Dennis and ozitraveller for your responses,

I resolved my issue with Creatinstance for now by using a parameterless constructor in the user object. Like Dennis, I am utilizing a design based on PTracker with a difference being
my winpart user controls (which I call maintenance forms) for editing base data, have their own
New/Save/Delete buttons instead of using the buttons on Mainform.

At the moment I am trying to determine best way to implement the constructor and strategy for GetIdValue in the user control.  At the point when the user control is instantiated I don't know if the user wants to add, edit or delete a record. Consequently, I'm not sure what the default constructor should do. 

If my design specifies that there can only be a single instantiation of a user control form, then if I understand correctly as per what Dennis said and Chapter 3 is that it would be adequate if GetIdValue just returns the business object being editied. Does this mean I need to create the business object at the time user control is instantiated even before I know what the user wants to do?

Thanks


 



ozitraveller replied on Wednesday, April 16, 2008

Hi

I've also used PTracker as a basis for my app. My app has a LOT of data and so I need to have more than just combo selection to open the Edit 'forms'. I have search 'forms' which are static, so I don't get 2 of the same search form open at once. So doing this 'currentAssembly.CreateInstance(classToInstantiate)' won't work for me. I ended up using MVC as well as WinPart to get the flexibility I wanted and it got a lot of code out of MainForm. I had thought of using an XML file to load the menus on startup so I don't have to look through tags to find things.

 

Copyright (c) Marimer LLC