I have a form that loads from a click of any of three buttons (Add, Modify or Delete). When the form loads there is a 'Confirm' button which will perform a task depending on which button was originally used to show the form.
Is there an easy way to determine which button was originally clicked so that the right code can be executed?
Thanks
Well suppose to define at the global level an enum like this
Public Enum CommandAction
Create
Modify
Delete
End Enum
Now in the code used to launch your second form to execute the Add command, you could write code like this (of course you repeat the same but varying the CommandAction in the other buttons).
Dim myFormInstance = new MyForm(CommandAction.Create)
myFormInstance.ShowDialog()
Finally, add a specfic constructor for your second form (MyForm in this example).
A constructor that receives and saves for future usage a CommandAction
Public Class MyForm
Dim cmd as CommandAction
Public Sub New(command as CommandAction )
InitializeComponent()
cmd = command
End Sub
Public Sub New()
InitializeComponent()
cmd = CommandAction.Create ' Set a default'
End Sub
End Class
Now in the code where you need to decide which kind of action to execute, just look at the value of the global cmd variable and execute the appropriate block of code
NOTE Adding a specific constructor to a form class requires the explicit presence of the standard empty constructor.
first put a hidden field with some name like "clicked". Make a javascript function with one parameter to put values in to the hidden field. Call the function on click of every button but set some specific value for each button. In this way you will be able to run right code for each button on submit. If not understand then do tell me, i'll help you in the code.
I made a Windows Form in VB.NET that has a Panel with a Datagridview on it, and underneath it a FlowLayoutPanel with Save and Close buttons on it, call it frmParent. Then I created frmChild and on it I inherited frmParent. I then hooked up my DataGridView to some data and it worked fine.
Next I tried to add code to save changes to the datagrid and the buttons are locked. I checked the modifiers: both buttons and the FlowLayoutPanel that contains them are Protected. I read that there are frequently problems accessing controls that are contained in a collection of another control. But the DataGridView is fine, so my question:
What do I need to do to get the Save and Cancel buttons to be available on the child form? Do I need to do away with the FlowLayoutPanel, or is there another way? In this simple example it isn't that big of a deal, but I would like to start using visual inheritance regularly and I would like to know the best way to do things.
Something like this ?
Public Class frmParent
Private Sub Button_SaveClick(sender As Object, e As EventArgs)
ButtonSaveClicked(...) ' pass here any argument you want (e, sender, anything...)
End Sub
Protected Overridable Sub ButtonSaveClicked(...)
' Do here what frmParent should do upon Button_Save Click...
End Sub
End Class
Public Class frmChild
Inherits frmParent
Protected Overrides Sub ButtonSaveClicked(...) ' same arguments
' Do here what frmChild should do upon Button_Save Click...
' Eventually call MyBase.ButtonSaveClicked(...)
' if you want frmParent to act aswell...
End Sub
End Class
EDIT : For the following parts of the question :
dissociating user interface and object datas
passing an instance of the object or not ?
I'm no expert on programming, but I've read one interresting thing about that user interface/object.
What come to my mind first talking about user interface is a blog post (don't recall the link, sorry) talking about Windows way of programming and Linux way. Before starting to drift to an off topic ride, I want make clear that I don't care about linux vs windows and I'm not interrested in such debate. Just want to share what I remember from this blog post :
In windows programming, the first thing you usually think of (and invited to do) is to create an user interface : the windows form (or the window in WPF) Then you'll create the objects that will be manipulated through that interface. In linux programming, it's the inverse : you'll create first the objects with everything they should be able to do, and only after you'll think of the user interface that will "enhance" the way to manipulate them.
^^ Or alike.. I'm zero linux knowledge, so I can't compare, but I assume the above is true, mostly. And I reckon that since OOP existed, this "windows way" of programming (that I'm used to) has a lot of drawbacks and messy code : you're constantly asking yourself wether a specific part of the code should be enclosed in the object or in the user interface.
So, I've come to an idea of mine, it may be stupid, but I'm fine with. I started to try that "linux way"... at least, "the way I'm thinking a linux programmer does it"
Let's take that control called DataGridView. It can contain text, dropboxes, buttons, images, etc. I'm not digging in bounding Dataset to keep it simple. Basically, the DataGridView is an object having everything to manipulate the above sub-objects within it. Unfortunately, a String, or an Image is not a (visual) control, while a dropbox, or a button is. You have a typical case of windows created object (DataGridView) that is a melting pot of datas and user interface, but you don't have an object that handles only the data part of the DataGridView, and another object that handles only the visual user interface of the thing (or at least, I'm not aware of such objects in .Net) AFAIK You're forced to handle both data and user interface when using a DGV !
So I created a class and simply called it Table_Class that can contain such objects (that are not controls) like String, Bitmap, List (of String), XML... almost anything as the contained datas are binary coded.
Then I created a Form, and called it Table_Form, that can display the content of a Table_Class. But almost all edits are functions and methods that are members of the Table_Class. If I want to order the grid by columns, it's the class that does it through one of its inbuilt function.
Of course, I'm not here to show that I'm wasting my time to reinvent the wheel, I know I'm doing it wrong but this approach is required by the fact I'm dealing with a massive amound of various datas loaded from files and saved as files. I'm just glad I have my own class of datas and happy to extends its capabilities when I want.
The main purpose of this blabla is to illustrate my opinion about passing an entire class or some of its members to a form. In this tandem Table_Class/Table_Form, of course, I'm passing the entire Table_Class on the Table_Form and vice versa. In fact, it's two different thing that can be used at the same time, or one at a time :
' SAMPLE CODE : Entire classes takes several files...
Public Partial Class Table_Class
' here is the object that is invisible to the user
Private _ContainerForm As Table_Form = Nothing
Public Sub ShowForm(Optional ByVal OwnerForm As Form = Nothing)
If _ContainerForm Is Nothing Then
_ContainerForm = New Table_Form(Me)
End If
If OwnerForm Is Nothing Then
_ContainerForm.Show()
Else
_ContainerForm.Show(OwnerForm)
End If
End Sub
Public Sub New()
End Sub
Public Sub New(ByRef NewContainerForm As Table_Form)
_ContainerForm = NewContainerForm
_ContainerForm.SetTable(Me)
End Sub
End Class
And here is the (associated) Form (constructor and associated Object initialization)
Public Partial Class Table_Form
Private _SelectedTable As Table_Class = Nothing
Public Sub SetTable(ByRef NewTable As Table_Class)
RemoveHandlers() ' Table_Class has events.
' If _SelectedTable is not Nothing,
' remove handlers prior to redefining a new selected Table.
_SelectedTable = NewSelectedTable
_SelectedTable.SetContainerForm(Me)
InitializeHandlers() ' Add all handlers for UI updates upon Table_Class edits.
End Sub
Public Sub New()
End Sub
Public Sub New(ByRef NewSelectedTable As Table_Class)
SetTable(NewSelectedTable)
End Sub
End Class
As you can see, I can manipulate the Table_Class either in the background when the user shouldn't care about its content, or via a user interface. With a DataGridView, you must take care of all the visual appearance in order to build an output to the Data/DataSet/Database.
What is important to note here is from the moment you want a visual display of an object through an user interface, you MUST pass critical members of that object to the displaying interface. You can't avoid doing it at a moment or another. In my example, I've passed the ENTIRE Table_Class since I'm doing every data modification in that class.
The difference with a DataGridView is :
with a DGV, when you edit one (String) entry in a Cell using KeystrokeOrF2, the Value is stored in that Cell of that DGV, then, either your application directly sends programatically the value to the associated Database, either you have a save button or a save menu to update the edits.
With my Table_Class, of course I'm using a DataGridView to display its contents, and I have a save button on a toolbar and in a menuitem. But when I click on those buttons, the Save method is not in my Table_Form Form. The Save Method is a method belonging to the Table_Class itself.
That's an example where lies the separation between the visual interface (of the DataGridView/Form) and the object actually containing the datas.
I know this Table_Class/Table_Form example is a complex one, but the way I understand half of the secund part of your question in your original post is : "how do I separate user interface and data/object manipulation"
The answer : it's up to you !
I think we have similar ways of doing it : my Table_Class does everything as your BlackBox Class. My Table_Form shows the datas and your Parent/Child Form so does.
I assume the remaining half of your issue is : "I can't call BlackBox.SaveChanges from the Click method of my button when it is in the FlowLayoutPanel". That's not related to the Object/User Interface limits syndrome, and is an entirely different issue. That's the main purpose of this answer before this long edit : I suggested a workaround by calling a method (ButtonSaveClicked()) instead of writing the save part of the code direclty under the Button_SaveClick() method that handles the click. I don't know if this approach worked for your "locked" button or not.
Back to the user interface/object thing : So comes the above : "you must pass critical members of your object to the user interface at a certain point", and that critical member must be an object (with the critical datas you want to manipulate, in this case, I assume the data to save) In my Table_Class/Table_Form example, I've passed the entire Table_Class in the Table_Form :
Public Partial Class Table_Form
Private _SelectedTable As Table_Class
' ...
End Class
so when I want the Table_Class (object containing data to manipulate) to save edits, through the table Form (form acting as an user interface) I have those methods sets in the Form :
Public Partial Class Table_Form
Private Sub Menu_Main_File_SaveClick(ByVal sender As Object, ByVal e As EventArgs)
' MenuStrip -> File -> Save
SaveTable()
End Sub
Protected Sub ToolBar_Main_SaveClick(ByVal sender As Object, ByVal e As EventArgs)
' A Save Button shortcut on a toolbar.
SaveTable()
End Sub
' ^^ Both controls calls the same method SaveTable()
' This is similar to the ButtonSaveClicked method I initially suggested
' What SaveTable does ?
Protected Overridable Sub SaveTable()
If _SelectedTable IsNot Nothing Then
_SelectedTable.Save(True)
End If
' It just calls the Table_Class.Save(... arguments)
End Sub
' ...
End Class
In the end, the code handling how the datas should be saved doesn't exists in the user interface. To save changes, the Table_Class object has a handfull set of Save(...) methods that handle extension like SaveAs (opens a SaveFileDialog), Overwrite without Prompt, Save as binary or save as textual... And if I want more extensions like sending the datas on the web, in a database, or even packing/unpacking the table contents in a directory, all I have to do is to implement that in the Object (Table_Class) and only the visual part will be handled in the form, the same way I did it from the start.
So when you ask me "Wouldn't I end up with child-specific code in the parent form that way?" I would say : "it's up to you". I've decided that the Table_Class does everything : data loading/parsing, data updating and data saving. If at a time in the future I have to implement a new way of saving the datas, I have the choice to add an overload of the Save() method of my Table_Class, or building a specific Save method in another form for another application using my Table_Class if I want. And that's possible because the Table_Class :
is not an User Interface
has an associated user interface in case it's required
while it has everything to read from and write datas to, programmatically.
and yes, I'm passing the entire Table Class in the UI, no matter it's a parent one or a child form that use inheritance.
I think "having messed parent/child code in your user interface side" highly depends on the capabilities of your non user interface object that (should) manipulate the datas.
(Actually, the Table Class is not a single Table. It's the Node of a Tree of Tables, or a Table that contains datas, and Tables children that contains other datas each. You can think of it like the merger of a DataGridView and a TreeView, that's why the Private variable un the UI Form is named _SelectedTable.)
I'm mentioning that last complexity of the Table_Class to clearly notify that no matter how complex the object is, it's easy to use whatever the user interface you use, because there isn't a single visual code in the object, and there is very little code manipulating datas in the user interface (all the UI does is to read datas from the object, display/edit or use them for other purposes, and send the validated modifications to the object, then call Object.Save())
I've made a long explaination of the way I'm doing it to let you have the latitude to decide how you want your application to behave, what your BlackBox class should be able to achieve alone and what are the limits of your user interface. There is no simple answer to that analysis, and highly depends on who is going to use your application for what purpose; if you have deadlines, keep it simple, but if your goal requires complex and highly manipulable datas, I think you should not mix user interface code and data manipulation code; this is a hell to alter if you have to upgrade your class with new features at a later time.
Sorry for the long post. I'm french speaking and I don't know if my english is okay. Don't mind the blabla, that's just me, can't keep an idea in my mind without telling it in details.
Try the ButtonSaveClicked() call workaround. That worked for me with a flow layout panel : I've made an Overridable ButtonSaveClicked() in the parent Form with a message box saying "Hello", and an Override ButtonSaveClicked() in the child Form saying "Coucou", and it worked well.
Then you can create a variable containing your BlackBox object in the parent form like I did with my Table_Form, and a way to make it accessible from the child form (Protected, or Public Property or a set of Protected methods and functions, I don't know...) Then decide what would be your BlackBox members (properties, methods, functions, events...) and how they should/could be used from the parent and the child forms.
To expose an action on a base class control as an event (another answer shows how to do this via an overridable method):
Base Form:
Public Event SaveClick As EventHandler
Protected Overridable Sub OnSaveClick(ByVal e As EventArgs)
RaiseEvent SaveClick(Me, e)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
OnSaveClick(e)
End Sub
Child Form:
' the event will show up in the event list like any other
' this may be what you want if each child form is supposed to do everything
Private Sub Form1_SaveClick(sender As Object, e As EventArgs) Handles Me.SaveClick
' save some stuff
End Sub
' hybrid where the child form does some stuff, base form does some stuff
Protected Overrides Sub OnSaveClick(e As EventArgs)
MyBase.OnSaveClick(e) ' base form activities
'save some stuff
End Sub
So the base form could raise some abstracted events like SaveItem, NewItem, DeleteItem and the child forms act on that much like how an interface works.
The flaw in this approach is that the controls are still locked on the BaseForm, as they should be (controls are Friend). Each child could pass a parameter to the base form indicating a "mode":
Public Sub New()
MyBase.New("Customer")
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
But to make use of it means that the base form has to have logic to handle all those various "modes" which negates the value of inherited forms specialized to handle Foo, Bar, Customer etc. So, I am not sure form inheritance is what you want: the controls described seem too specialized.
Your best bet, both from a functional standpoint and design standpoint, is to create events in your child form, i.e. OnSaveRequested and OnCancelRequested. In your parent form, declare your child form using WithEvents and create handlers for those events. When you want to save from the child form, raise the OnSaveRequested event and handle it in the parent form (same with Cancel).
Of course, in this scenario your parent form will need to know what to save, so you can either pass the data in as an argument to the event or expose a public property to the data in your child form.
I have a form that loads from a click of any of three buttons (Add, Modify or Delete). When the form loads there is a 'Confirm' button which will perform a task depending on which button was originally used to show the form.
Is there an easy way to determine which button was originally clicked so that the right code can be executed?
Thanks
Well suppose to define at the global level an enum like this
Public Enum CommandAction
Create
Modify
Delete
End Enum
Now in the code used to launch your second form to execute the Add command, you could write code like this (of course you repeat the same but varying the CommandAction in the other buttons).
Dim myFormInstance = new MyForm(CommandAction.Create)
myFormInstance.ShowDialog()
Finally, add a specfic constructor for your second form (MyForm in this example).
A constructor that receives and saves for future usage a CommandAction
Public Class MyForm
Dim cmd as CommandAction
Public Sub New(command as CommandAction )
InitializeComponent()
cmd = command
End Sub
Public Sub New()
InitializeComponent()
cmd = CommandAction.Create ' Set a default'
End Sub
End Class
Now in the code where you need to decide which kind of action to execute, just look at the value of the global cmd variable and execute the appropriate block of code
NOTE Adding a specific constructor to a form class requires the explicit presence of the standard empty constructor.
first put a hidden field with some name like "clicked". Make a javascript function with one parameter to put values in to the hidden field. Call the function on click of every button but set some specific value for each button. In this way you will be able to run right code for each button on submit. If not understand then do tell me, i'll help you in the code.
okay... How do I explain this without being totally confusing?... Alright, I have this form that has MenuScripts (top-levels and second-levels). The problem that I am having is one of the second-levels is "Add" which brings you to another form when clicked. This other form has a button ("Record") and text boxes. This other form allows the user to input data and when the record button is clicked, the inputted data is written into a text file. Ok, so back to the first form. Another second-level MenuScript is "Update" which also brings the user to the other form; but first, the user has to click an item within a listbox to proceed. How do I get the data from the selected item to appear in the appropriate textboxes and how do I get the record button to update data instead of being confused and thinking it is only a add-data button?
Is there a way to use an "if" statement to say something like "if mnuAdd is clicked then" "elseif mnuUpdate is clicked then". Would something like that work for giving the record button multiple uses?
Also, if someone can give me some pointers on making sure the user selects an item within the listbox would definitely be a plus! Thanks, guys!
Unfortunately, I cannot add images since my reputation is too low.
Here is a visual representation of my ultimate goal
Easiest way: before displaying the second form set it's Tag property to something distinct – say "Add" or "Update" – depending on which menu item is selected. Then you just test the Tag value in the button's Click event and proceed accordingly.
As for determining whether a list item is selected: well if there isn't the ListBox's SelectedIndex property will be set to -1.
You need to put a public property on the second form (Details) which specifies which mode it is in. For instance, you could create a mode enumeration like this:
Public Enum EntryModes
AddBook
UpdateBook
End Enum
Then, define a public mode property on the second form, like this:
Public Property EntryMode As EntryModes
Get
Return _entryMode
End Get
Set(ByVal value As EntryMode)
_entryMode = value
End Set
End Property
Private _entryMode As EntryMode
Then, when you show the second form from the menu, just set the property first, before showing it:
Private Sub mnuAdd_Click(sender As Object, e As EventArgs)
Dim dialog As New DetailsDialog()
dialog.EntryMode = EntryModes.AddBook
dialog.ShowDialog()
End Sub
Private Sub mnuUpdate_Click(sender As Object, e As EventArgs)
Dim dialog As New DetailsDialog()
dialog.EntryMode = EntryModes.UpdateBook
dialog.BookToUpdate = ListBox1.SelectedItem
dialog.ShowDialog()
End Sub
As you can see, in the Upate menu click, I also added a line that passes the information for which book should be updated.
I'm trying to do the following:
I need a static variable to get a ListItemCollection from a List control (I can do this, but if I don't set it as Shared It's not preserving the values as it should). The thing is that this class is a SharePoint webpart, so I most probably will be using the webpart more than once, and I need this variable to be unique to each webpart, which shared doesn't accomplish.
I tried everything you can imagine. I placed a Static variable within a Sub (shared and not shared), I tried it with Properties (also Shared and not shared)...
Any Ideas are welcome.
Thanks.
By definition, static members are per-class (or per-thread with a ThreadStatic attribute).
If you need to save the property on the webpart, add the WebPartStorageAttribute on the property, also throw on a FriendlyNameAttribute on there to make it clean:
C# Version:
[FriendlyNameAttribute("What the setting will be called")]
[WebPartStorage(Storage.Shared)]
private string MyStringThatGetsSaved { get; set; }
VB.Net Version:
<WebPartStorage(Storage.Personal), FriendlyNameAttribute("What the setting will be called")>
Private mMyStringThatGetsSaved As String
Public Property MyStringThatGetsSaved () As String
Get
Return mMyStringThatGetsSaved
End Get
Set(ByVal Value As String)
mMyStringThatGetsSaved = Value
End Set
End Property
Is this what you're after? If not can you clarify a bit further?
I finally went on another way, I just added some checkboxes to the toolpart and setted the properties on the webpart.
Anyway, what I was trying to do is:
Have a Web Part that changes its controls on Edit & Browse mode. In Edit mode I show two ListBox controls, two buttons (add, remove). When I click the add button, the value has to be removed from the left ListBox and be added to the right ListBox, so far so good I was able to make this functionality with no problems... The thing is when I go back to Browse mode I need to use the items in the ListBox from the right to show (so I added a ListItemCollection control that would store the values from the ListBox on the right), the text of the item and a TextBox control, then the user would enter their text in that textbox and hit the "Search" button and a search query would be executed.
My problem is that when I go from Edit to Browse the ListItemCollection variable I added is getting restarted. So I declared it as Shared, and that does work, but when I add a new instance of the WebPart, they have the same fields displayed... I don't know if there is a way of doing a Static Class-Level variable that is unique to each instance, so I went the ToolPart way...