has anyone written a generic "LaunchForm" function? For all the menu items I have that open a form, I would like to write one function that will launch the form as opposed to writing the same code several times.
any tips would be very helpful.
It's winforms + vb.net
thanks
TR
You mean something like this?
C#
public F Launch<F>() where F : Form, new()
{
F dlg = new F();
dlg.MdiParent = this;
dlg.Show();
return dlg;
}
VB.NET
Public Function Launch(Of F As {Form, New})() As F
Dim dlg As New F()
dlg.MdiParent = Me
dlg.Show()
Return dlg
End Function
You could attach the form type to the menuitem's .Tag property. Hook up a single event handler to all the menu item click events and pass the .Tag property value (form type) to a function that created a new instance and displayed it.
Alternately, if each form is to be a singleton you could create a dictionary of(MenuItem, Form), prepopulate with form instances and do the appropriate lookup/show. Or even skip the dictionary and pop and instance of the form into the menuitem's .Tag property.
There's all kinds of options and without knowing the intended usage it's hard to suggest just one.
Related
I'm using Smart Client Software Factory 2008. In the module controller, I have code that creates a new child controller only if it hasn't been created, by doing something like the following:
Dim key = "Item-" + item.ID.ToString()
Dim childWorkItem = Me.WorkItem.WorkItems.Get(Of ControlledWorkItem(Of ItemWorkItemController))(key)
If childWorkItem Is Nothing Then
childWorkItem = Me.WorkItem.WorkItems.AddNew(Of ControlledWorkItem(Of ItemWorkItemController))(key)
Else
childWorkItem.Activate()
End If
Multiple items reuse the same key, so when that action is triggered, it shows the tab instead of creating a new instance of it. This works great.
However, there is one drawback. Once activated, I need to run a check within that item's presenter. So I need to call a method on the presenter. Is there a way to invoka a method on the presenter, or is there an event that runs on the view when the work item is activated? I'm not sure how to make that happen?
Thanks.
If you are using a Smart Part as your View you should be able to accomplish this using the IWorkspace.SmartPartActivated event.
This is how I have it setup in my project. I apologize, my code is all in C# but you should be able to apply it in VB relatively easily.
The WorkItemController class has the Activate method setup like this
ISmartPartView _smartPartView
public void Activate()
{
IWorkspace contentWorkspace = this.WorkItem.Workspaces[WorkspaceNames.ShellContentWorkspace];
contentWorkspace.Activate(_smartPartView);
WorkItem.Activate();
}
In the ISmartPartView Presenter class you should be able to create a handler for the SmartPartActivated event like this:
IWorkspace contentWorkspace = this.WorkItem.Workspaces[WorkspaceNames.ShellContentWorkspace];
contentWorkspace.SmartPartActivated += workSpaceSmartPart_ActivatedHandler;
In the workSpaceSmartPart_ActivatedHandler event handler, you can check the SmartPart being activated and if its your ISmartPartView class you can run the desired code.
I'm using a Silverlight 4 dataform and attempting to use the inbuilt add button to create a new item in my collection. It works fine except that I have a number of properties that need to be set behind the scenes. I've tried hooking into the likely looking events such as AddingNewItem but the new item is readonly at that point and I can't set the properties.
Is there a trick to add new items using the Silverlight 4 dataform?
Finally stumbled on the way to do it after much searching and trial and error.
The rather counter-intuitive place to set the properties on a newly created dataform item is in the EditEnding event handler. The dataform Mode property is readonly in the AddingNewItem handler but is equal to AddNew in the EditEnding handler.
My EditEnding handler code is along the lines of:
private void EditEnding(object sender, DataFormEditEndingEventArgs e)
{
if (myDataForm.Mode == DataFormMode.AddNew)
{
MyItem item = myDataForm.CurrentItem as MyItem;
item.ID = Guid.NewGuid().ToString();
}
}
I have a GUI that allows a user to add tabpages to a tab control. Everytime the user adds a tab, a user control is placed on the new tab. The user control has a background worker on it, as well as several other controls. My question is this: Can I access the backgroundworker specific to a tabpage? In other words, can I tell the program to use the backgroundworker found on tab index 0 to run a process, and then tell the backgroundworker on tab index 1 to run a different process?
Maybe you can have a list of BackgroundWorkers and everytime you add a tab you create a new worker something like this in c#
public class Foo : Form{
List<BackgroundWorkers> bgs;
......
void AddNewTab(DoWorkEventHandler bw_DoWork, RunWorkerCompletedEventHandler bw_RunWorkerCompleted, ProgressChangedEventHandler bw_ProgressChanged)
{
//bgs was initialized in the ctr
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true; ;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bgs.Add(bw)
}
.....
void ExecuteBackgroundWorker(int tabindex)
{
bgs[tabindex].RunWorkerAsync();
}
void ExecuteBackgroundWorkerTask(int tabindex, DoWorkEventHandler bw_DoWork)
{
bgs[tabindex] += new DoWorkEventHandler(bw_DoWork);
bgs[tabindex].RunWorkerAsync();
}
}
Something like that.....but translated to vb.net....
I have several textboxes in a winform, some of them are inside a groupbox. I tried to loop over all textboxes in my form:
For Each c As Control In Me.Controls
If c.GetType Is GetType(TextBox) Then
' Do something
End If
Next
But it seemed to skip those inside the groupbox and loop only over the other textboxes of the form. So I added another For Each loop for the groupbox textboxes:
For Each c As Control In GroupBox1.Controls
If c.GetType Is GetType(TextBox) Then
' Do something
End If
Next
I wonder: is there a way to loop over all textboxes in a form--including those inside a groupbox--with a single For Each loop? Or any better/more elegant way to do it?
You can use this function, linq might be a more elegant way.
Dim allTxt As New List(Of Control)
For Each txt As TextBox In FindControlRecursive(allTxt, Me, GetType(TextBox))
'....'
Next
Public Shared Function FindControlRecursive(ByVal list As List(Of Control), ByVal parent As Control, ByVal ctrlType As System.Type) As List(Of Control)
If parent Is Nothing Then Return list
If parent.GetType Is ctrlType Then
list.Add(parent)
End If
For Each child As Control In parent.Controls
FindControlRecursive(list, child, ctrlType)
Next
Return list
End Function
You'd want to do recursion, for example (pseudocode, since I do not know VB):
Sub LoopControls (Control Container)
if (Container has Controls)
LoopControls(Container)
For Each c As Control In Container
if (Control is TextBox)
// do stuff
End Sub
You would pass your form (me) to the sub initially, and it will traverse the controls in it looking for ones that contain more controls.
Also check out this question: VB.NET - Iterating through controls in a container object
If you don't care about the order (and I can't imagine a reason why you should) of the controls, you can do this iteratively in the following fashion (its quite straightforward, so I don't think any explanation is necessary). Should be much more efficient than any recursion, especially if you have many nested controls, though I doubt if the performance gain would be apparent.
Public Function FindAllControlsIterative(ByRef parent As Control) As List(Of TextBox)
Dim list As New List(Of TextBox)
Dim ContainerStack As New Stack(Of Control)
ContainerStack.Push(parent)
While ContainerStack.Count > 0
For Each child As Control In ContainerStack.Pop().Controls
If child.HasChildren Then ContainerStack.Push(child)
If child.GetType Is GetType(TextBox) Then list.Add(child)
Next
End While
Return list
End Function
You will need to use recursion. The following is a C# solution using an extension method and goes a little beyond the scope of your question but I just pulled it from our framework.
static partial class ControlExtensions
{
public static void ApplyToMatchingChild(this Control parent, Action<Control> actionToApplyWhenFound, bool keepApplyingForever, params Func<Control, bool>[] matchingChildCriteria)
{
ControlEventHandler reapplyEventHandler = null;
if (keepApplyingForever)
{
reapplyEventHandler = (s, e) =>
{
ApplyToMatchingChild(e.Control, actionToApplyWhenFound, keepApplyingForever, matchingChildCriteria);
};
}
SearchForMatchingChildTypes(parent, actionToApplyWhenFound, reapplyEventHandler, matchingChildCriteria);
}
private static void SearchForMatchingChildTypes(Control control, Action<Control> actionToApplyWhenFound, ControlEventHandler reapplyEventHandler, params Func<Control, bool>[] matchingChildCriteria)
{
if (matchingChildCriteria.Any(criteria => criteria(control)))
{
actionToApplyWhenFound(control);
}
if (reapplyEventHandler != null)
{
control.ControlAdded += reapplyEventHandler;
}
if (control.HasChildren)
{
foreach (var ctl in control.Controls.Cast<Control>())
{
SearchForMatchingChildTypes(ctl, actionToApplyWhenFound, reapplyEventHandler, matchingChildCriteria);
}
}
}
}
And to call:
myControl.ApplyToMatchingChild(c => { /* Do Stuff to c */ return; }, false, c => c is TextBox);
That will apply a function to all child textboxes. You can use the keepApplyingForever parameter to ensure that your function will be applied when child controls are later added. The function will also allow you to specify any number of matching criteria, for example, if the control is also a label or some other criteria.
We actually use this as a neat way to call our dependency injection container for every UserControl added to our Form.
I'm sure you shouldn't have much issue converting it to VB.NET too... Also, If you don't want the "keepApplyingForever" functionality, it should be easy enough to strip that out too.
I have a complex form to allow the user to configure my app.
What's the best way to save the form state & reload when the program next runs.
I mean text he has entered in list boxes, the selected item of combo/list/radio, whether a checkbox is cheeked, etc
Lots of people here telling me when to save, but not many telling me how ...
In the end I went with WritePrivateProfileString()
You have a few options of where to save the entered settings - in a configuration file, or in the registry, maybe a database (maybe even "the cloud", but i won't go there).
You should have the user carry out a specific action (such as clicking an Apply button) before you save the settings - you shouldn't just save the settings when the user closes the form, as that is ultimately not good UX.
How you persist the settings is totally up to you - you can save them into a straight name/value pair style config file, you may want to use XML in the config file, or you save them as keys and values in a known spot in the registry (or you could save name/value pairs into a database table).
When your application is next run, one of the start up tasks can be to check the known location (whether it be the registry or a config file) for the settings, and then load them into a settings class. Make sure that you have logical default values for each setting in case it has either never been set, or for some reason you cannot read it back in. The settings class can then either be passed in to each form for it to apply whatever settings are relevant, or it could be a static class (globally visible single instance class) so that it can just be read from anywhere in the application.
Edit: after reading your comment to another answer, here is another option, slightly more advanced. Use the settings class i mentioned earlier, but also use binding - you can bind your settings object directly to your form, so any values entered will be updated directly into the settings object without you having to write code to do it (provided you use two way binding). The "streaming" can be achieved by serializing the settings object to a file (or a database), i suggest you look at the XmlSerializer.
Serialize the Form.
Implement ISerializable, and in serializable constructor and GetObject() method load/save your fields.
In OnClosing serialize the form.
///
/// try to obtain the las serialized main form with old data
MainForm mainForm = DeserializeMainForm("mainForm.data");
///
/// if any old data found, create a new(empty) main form
if (mainForm == null) mainForm = new MainForm();
static MainForm DeserializeMainForm(string filePath)
{
MainForm mf = null;
FileStream fileStream = null;
try
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
fileStream = new FileStream(filePath, FileMode.Open);
mf = (MainForm)binaryFormatter.Deserialize(fileStream);
}
catch { }
finally
{
if (fileStream != null)
{
fileStream.Close();
}
}
return mf;
}
MainForm:
[Serializable]
public partial class MainForm : Form, ISerializable
{
protected MainForm(SerializationInfo info, StreamingContext context)
: this()
{
if (info == null)
throw new System.ArgumentNullException("info");
this.tbxServerIp.Text = info.GetString("server ip");
this.tbxServerPort.Text = info.GetString("server port");
this.tbxEventFilter.Text = info.GetString("event filter");
this.tbxWallId.Text = info.GetString("wallId");
foreach (Control control in this.Controls)
{
if (control is EventSender)
{
EventSender eventSender = (control as EventSender);
eventSender.LoadFromSerializationInfo(info);
}
}
}
private void SerializeThis()
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
FileStream fileStream = new FileStream("mainForm.data", FileMode.Create);
try
{
binaryFormatter.Serialize(fileStream, this);
}
catch
{
throw;
}
finally
{
fileStream.Close();
}
}
protected override void OnClosing(CancelEventArgs e)
{
SerializeThis();
base.OnClosing(e);
}
}
Private Sub frm_Closing (sender as Object, e as CancelEventArgs) Handles MyBase.Closing
' save all the values you want'
End Sub
Private Sub frm_Load(sender as Object, e as EventArgs) Handles MyBase.Load
If SaveSettingsExist Then
' restore all the values you want'
End If
End Sub
I actually have a couple of generic routines I use like this for saving the form size/position and ListView column settings. So I have something like...
Private Sub frm_Closing (sender as Object, e as CancelEventArgs) Handles MyBase.Closing
SaveFormPos(Me)
SaveListview(Me, lvuInvoices)
End Sub
Private Sub frm_Load(sender as Object, e as EventArgs) Handles MyBase.Load
RestoreFormPos(Me)
RestoreListview(Me, lvuInvoices)
End Sub
The Me parameter (for the Listview routine) is used to create a key for the values to be saved to the registry. You have all sorts of options in front of you. You could put this functionality into a base class for all your Forms, create a SaveState class, or simply stick routines into a Module. You could save this data to the registry, a database, text files. You could have a generic routine that trawls through the Controls collection looking for TextBoxes, Checkboxes etc.
However, once you've created a useful set of save routines, you can then employ them on any subsequent form you want, so you only need to do the hard work once.
I also agree on having a LoadSettings/SaveSettings set of functions that are called when creating the form/ when closing the application.
As a store location for the application's settings I recommend using the Isolated Storage.
As an addition, depending on the controls you are using on your form, you could have the options of saving their status in XML format format and then restoring it next time.
For example Infragistics controls offer this possibility(e.g UltraDockManager, UltraToolbarManager have a SaveAsXml/LoadFromXml pair of functions).
You can somehow save everything in a hidden textbox in a hidden form.
When the user clicks the apply button, open the text file automatically and make the program read it line by line.
Example:
Line 1 could be the location of an image
Line 2 could be the text for a textbox
Line 3 could be a word or number that the program uses to determine if a
checkbox is true or false