Tabbed Menu - each tab as a separate CS file - c#

I'm new to C# and have created a basic program.
My "Main menu" is setup with 7 tabs along the top.
I have set up CASE so when the user selects the tab it adds that specific submenu (frmXXXmenu) to the controls.
The issue is
Is this correct
Now each form will open each click, thus I keep generating instances of the forms (and I can see my process memory increasing!)
private void tabmain_SelectedIndexChanged(object sender, EventArgs e)
{
string curtab = tabmain.SelectedTab.Name.ToString();
switch (tabmain.SelectedTab.Name)
{
case "tabcollect":
frmcollectmenu frmcollectmenu = new frmcollectmenu();
frmcollectmenu.TopLevel = false;
tabcollect.Controls.Add(frmcollectmenu);
frmcollectmenu.Dock = DockStyle.Fill;
frmcollectmenu.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
frmcollectmenu.Show();
break;
case "tabpack":
frmpackmenu frmpackmenu = new frmpackmenu();
frmpackmenu.TopLevel = false;
tabpack.Controls.Add(frmpackmenu);
frmpackmenu.Dock = DockStyle.Fill;
frmpackmenu.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
frmpackmenu.Show();
break;
}
}
Etc for 7 tabs.
Am I in the right direction and if so, how do I fix this instance situation?
I was thinking either Dispose when no longer active.. or somehow not creating a new instance when one is already open?
Second issue
One of the forms takes about 2 seconds to open (it is showing a chart based on sql). How can i load this page in the tab when i open frmmain() to start the program and then when i open the tab it will be loaded?
I tried putting
frmcollectmenu frmcollectmenu = new frmcollectmenu();
frmcollectmenu.TopLevel = false;
tabcollect.Controls.Add(frmcollectmenu);
frmcollectmenu.Dock = DockStyle.Fill;
frmcollectmenu.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
frmcollectmenu.visible = false;
frmcollectmenu.Show();
(added the visible part) then i made it visible again when selecting the tab, but it seems it still takes a few seconds and doesnt really work right.

If you want only one instance of your forms opened then you should check the Application.OpenForms collection if one of your forms is already referenced there by the system
case "tabcollect":
frmcollectmenu f = Application.OpenForms["frmcollectmenu"];
if(f == null)
{
frmcollectmenu frmcollectmenu = new frmcollectmenu();
frmcollectmenu.TopLevel = false;
tabcollect.Controls.Add(frmcollectmenu);
frmcollectmenu.Dock = DockStyle.Fill;
frmcollectmenu.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
frmcollectmenu.Show();
}
else
f.Show();
break;
Notice that this works if you leave the name property of the form to its default name (same as the class name of the form)
You can also try to isolate the logic about your open forms in separate methods for each form and implement a dictionary where you store the tab key and the action to execute when the user select that particular tab
// At form global level....
Dictionary<string, Action> tabActions = new Dictionary<string, Action>()
{
{ "tabCollect", runCollect },
{ ..... },
so on for other tabs
}
// The logic for a particular form (the same are needed for others forms)
private static void runCollect()
{
frmcollectmenu f = Application.OpenForms["frmcollectmenu"];
if(f == null)
{
frmcollectmenu frmcollectmenu = new frmcollectmenu();
frmcollectmenu.TopLevel = false;
tabcollect.Controls.Add(frmcollectmenu);
frmcollectmenu.Dock = DockStyle.Fill;
frmcollectmenu.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
frmcollectmenu.Show();
}
else
f.Show();
}
Now you can remove the switch in your SelectedIndexChanged method and just select the appropriate action from the dictionary statically build at startup time of your main form
private void tabmain_SelectedIndexChanged(object sender, EventArgs e)
{
string curtab = tabmain.SelectedTab.Name.ToString();
if(tabActions.ContainsKey(curTab))
tabActions[curTab].Invoke();
}

Related

How to pass EventHandler to enable button in parent form from a panel?

I have followed this https://rjcodeadvance.com/iu-moderno-temas-multicolor-aleatorio-resaltar-boton-form-activo-winform-c/ to create a Windows Forms user interface for an application I am planning.
I am trying to enable a button in the parent form based on the results of a successful login from a child form.
I have managed to do it, and it works, with this code. When login is successful, the button in the parent form is enabled.
private void OpenChildForm(Form childForm, object btnSender)
{
if (activeForm != null)
activeForm.Close();
ActivateButton(btnSender);
activeForm = childForm;
childForm.TopLevel = false;
childForm.FormBorderStyle = FormBorderStyle.None;
childForm.Dock = DockStyle.Fill;
this.panelDesktopPane.Controls.Add(childForm);
this.panelDesktopPane.Tag = childForm;
childForm.BringToFront();
FormLogin frmlgn = new FormLogin();
frmlgn.DataAvailable += new EventHandler(child_DataAvailable);
frmlgn.Show();
lblTitle.Text = childForm.Text;
btnClose.Visible = true;
btnMaximize.Visible = true;
btnMinimize.Visible = true;
btnClose.BringToFront();
btnMaximize.BringToFront();
btnMinimize.BringToFront();
}
The problem I am facing is that the child form isn't shown inside a panel anymore, which breaks the user interface.
As you can guess, the method performs several modifications to show the form passed as a parameter inside the panel so everything is shown in the same "window".
If I change frmlgn.Show(); to childForm.Show();, I break the EventHandler mechanism, and the button is not enabled, although I keep the user interface as I would like to. When I run the code, as shown above, event handling works but the Login form is shown as a separate window, instead of inside the main window.
I can't figure out how to keep the user interface intact while transferring the data to enable the button.
Everything I try shows some kind of error while compiling. The closest I have been is trying to change frmlgn.DataAvailable += new EventHandler(child_DataAvailable); to childForm.DataAvailable += new EventHandler(child_DataAvailable) but it says *CS1061 'Form' does not contain a definition for 'DataAvailable' and no accessible extension method ... *
Any suggestion?
Thanks.
Update!!
Well I have managed to get it working, although I would like to have a "cleaner" approach, if possible.
I have just created an if loop that executes the modifications to the particular Login form and then shows it in the panel. If the Form to load is different from Login then it executes the code as "is". Thankfully it works, but I don't feel comfortable with this little trick. I am sure there has to be a clean way of doing this.
private void OpenChildForm(Form childForm, object btnSender)
{
if (childForm.Name == "FormLogin")
{
FormLogin frmlgn = new FormLogin();
if (activeForm != null)
activeForm.Close();
ActivateButton(btnSender);
activeForm = childForm;
frmlgn.TopLevel = false;
frmlgn.FormBorderStyle = FormBorderStyle.None;
frmlgn.Dock = DockStyle.Fill;
panelDesktopPane.Controls.Add(frmlgn);
this.panelDesktopPane.Tag = childForm;
frmlgn.BringToFront();
frmlgn.DataAvailable += new EventHandler(child_DataAvailable);
frmlgn.Show();
}
else
{
if (activeForm != null)
activeForm.Close();
ActivateButton(btnSender);
activeForm = childForm;
childForm.TopLevel = false;
childForm.FormBorderStyle = FormBorderStyle.None;
childForm.Dock = DockStyle.Fill;
this.panelDesktopPane.Controls.Add(childForm);
this.panelDesktopPane.Tag = childForm;
childForm.BringToFront();
childForm.Show();
}
lblTitle.Text = childForm.Text;
btnClose.Visible = true;
btnMaximize.Visible = true;
btnMinimize.Visible = true;
btnClose.BringToFront();
btnMaximize.BringToFront();
btnMinimize.BringToFront();
}
Please note that compiling showed a serious exception (not shown in errors tab) that said that Top-level control cannot be added to a control.
I had to change this line this.panelDesktopPane.Controls.Add(childForm); to panelDesktopPane.Controls.Add(frmlgn); and I don't really know what consequences it may have.
Thanks again.

CefSharp WPF and Offscreen perfomance

i m trying to suppot an app, that uses cefsharp(v79.1.360).
There are list of things I need to implement:
1) ChromiumWebBrowser in WPF (using CefSharp.Wpf minimum example)
2) This browser can go offscreen(with collapsing window or closing it)
3) Work with JavaScriptObjectRepository, and launch some code, that will be do work with web pages(click buttons, change text of elements). Pages may use frameworks, websockets, Http requests and the other stuff
web pages usually do.
After pages work is done, i send results to C# by calling Methods of object, i bounded in jsObjectRepository/
Expectations:
Offscreen prefomance(time delay) should be as well as With opened window/
Reality:
Offscreen perfomance sometimes is really bad, it take time to do work up to 10 seconds(when wpf is only 1-5).
My code:
Initialization
CefSharpSettings.LegacyJavascriptBindingEnabled = true;
CefSharpSettings.WcfEnabled = true;
CefSettings cefSettings = new CefSettings
{
LocalesDirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "locales"),
Locale = appsettings.CurrentChromeLanguage.ToLocal(),
AcceptLanguageList = appsettings.CurrentChromeLanguage.ToAcceptList(),
};
if (!cefSettings.CefCommandLineArgs.ContainsKey("disable-gpu"))
{
cefSettings.CefCommandLineArgs.Add("disable-gpu", "1");
}
if (cefSettings.CefCommandLineArgs.ContainsKey("enable-system-flash"))
{
cefSettings.CefCommandLineArgs.Remove("enable-system-flash");
}
if (cefSettings.CefCommandLineArgs.ContainsKey("enable-media-stream"))
{
cefSettings.CefCommandLineArgs.Remove("enable-media-stream");
}
cefSettings.CefCommandLineArgs.Add("enable-begin-frame-scheduling", "1");
cefSettings.CefCommandLineArgs.Add("disable-gpu-vsync", "1");
cefSettings.CefCommandLineArgs.Add("mute-audio", "true");
cefSettings.CefCommandLineArgs.Add("enable-media-stream", "0");
cefSettings.CefCommandLineArgs.Add("disable-3d-apis", "1");
cefSettings.CefCommandLineArgs.Add("renderer-process-limit", "10");
cefSettings.CefCommandLineArgs.Add("js-flags", "--lite_mode");
if (!appsettings.IsLoadImage)
{
cefSettings.CefCommandLineArgs.Add("disable-image-loading", "1");
}
cefSettings.LogFile = Path.Combine(ClientConfig.ChromeDataPath, "Log.txt");
cefSettings.LogSeverity = LogSeverity.Error;
cefSettings.IgnoreCertificateErrors = true;
cefSettings.SetOffScreenRenderingBestPerformanceArgs();
Browser creating and usage:
ChromiumWebBrowser browser = new ChromiumWebBrowser();
//xaml window with <ContentControl> with browser
//need hide means when window is closing, we cancel it, and using Hide()
NewBrowserView view = new NewBrowserView(new ChromeTabViewModel(browser));
view.Closing += BrowserView_Closing;
Browser.FrameLoadStart += _browser_FrameLoadStart;
var options = new BindingOptions { CamelCaseJavascriptNames = false };
browser.JavascriptObjectRepository.Register("resultController", this, false, options);
//we can just hide window
void BrowserView_Closing(object sender, CancelEventArgs e)
{
if (_needHide)
{
e.Cancel = true;
Hide();
}
}
//on page load
void _browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e) {
string code = "";
code += "(async function(){ " +
"await CefSharp.BindObjectAsync('resultController'); " +
code += TestJsCode;
code += " })();";//AddWorker
e.Frame.ExecuteJavaScriptAsync(code, $"about:blank/myCode");
Consol.WriteLine(DateTime.Now);
}
public void OnGoodResult()
{
Consol.WriteLine(DateTime.Now);
}
public void OnBadResult()
{
Consol.WriteLine(DateTime.Now);
}
//then i just go by differnet pages and await results
As i mentioned before, when i hide wnd, its taking too long time to print result
I really depended on Layouts and other visuals, so i figured this out. I should just set this code, when window is collapsing:
GetBrowser().GetHost().WasHidden(false);

Previous User Controls stay active after changing ContenControl content

In my WPF application I have one main window and two User Controls and I use the following code to change in between my User Controls:
private void SwitchControls(MyButton btnCurrent)
{
switch (btnCurrent.Name)
{
case "btnLine":
{
winLine win = new winLine();
ccContent.Content = win;
}
break;
case "btnHistory":
{
winHistory win = new winHistory();
ccContent.Content = win;
}
break;
}
}
ccContent is my ContentControl inside my main window. winLine and winHistory are my two User Controls. In my winLine I have a timer that Ticks every 5 secs and whenever I switch to winHistory timer in previous User Control keeps ticking. I tried to do ccContent.Content = null; before switching User Controls, but it didn't help.
How do I disable or remove previous User Control and keep only an active one? I hope my question is clear.
Stop and dispose the timer before you reset the Content property. You could do this by trying to cast the value of the Content property to winLine using the as operator:
private void SwitchControls(MyButton btnCurrent)
{
winLine current = ccContent.Content as winLine;
if (current != null && current.Timer != null)
{
current.Timer.Stop();
current.Timer.Dispose();
}
switch (btnCurrent.Name)
{
case "btnLine":
{
winLine win = new winLine();
ccContent.Content = win;
}
break;
case "btnHistory":
{
winHistory win = new winHistory();
ccContent.Content = win;
}
break;
}
}

C# Initializing form upon default text

I have Form 1 With a label that has been set to "Company Name" by default.
CompanyNameLBL.Text
I placed a timer here so that if the database is not setup, then initialize Database Setup.
if (CompanyNameLBL.Text == "Company Name")
{
//Instantiating Timer Class
databaseTimer.Interval = 2000; // 1000 milliseconds
databaseTimer.Enabled = true;
databaseTimer.Tick += new EventHandler(dbTimer);
}
protected void dbTimer(object source, EventArgs e)
{
databaseTimer.Stop();
DatabaseSetup setupDatabase = new DatabaseSetup();
this.Hide();
setupDatabase.Show();
}
I have Form 2, which is my Database Setup. Once entering the right credentials, I can change the name of the label like this:
formLogin loginScreen = new formLogin();
loginScreen.CompanyNameLBL.Text = "a new company name";
loginScreen.Show();
It changes the label, but my timer still goes off. What gives?
The problem is here:
formLogin loginScreen = new formLogin();
You are creating a new formLogin instance, not a reference to the existing instance. You need to figure out how to communicate between the two forms. One option is to make the result of the login a property of the DatabaseSetup form, then pull that after closing the form:
databaseTimer.Stop();
DatabaseSetup setupDatabase = new DatabaseSetup();
this.Hide();
setupDatabase.Show();
loginScreen.CompanyNameLBL.Text = setupDatabase.CompanyName; // new property
this.Show();
I simply used
public formLogin(string dbName)
and passed it through that variable.

How to reload or refresh Windows Form into original state?

How to reload or refresh the Windows Form into original state? i have used this.Refresh();,this.Invalidate();,form.Refresh(),form.Invalidate()
private void AdduserBtn_Click_1(object sender, EventArgs e)
{
UserManagement obj = new UserManagement ();
obj.CourseCategoryId = (int) CourseRegCbox.SelectedValue;
obj.IDNumber = IDNumberTbox.Text;
obj.Password = PasswordRegTbox.Text;
obj.FName = FnameRegTbox.Text;
obj.LName = LnameRegTbox.Text;
obj.Gender = GenderTbox.Text;
obj.Email = EmailRegTbox.Text;
obj.PhoneNumber = PhonenumberRegTbox.Text;
obj.Address = AddressRegTbox.Text;
if ( UserManagement != null && UserManagement.Id > 0 )
{
obj.Id = UserManagement.Id;
if ( UserManagement.UserInfo_Update (obj) > 0 )
{
MessageBox.Show ("Record Succesfully Updated!");
UserInfoForm form = new UserInfoForm ();
form.Refresh ();
}
else
{
MessageBox.Show ("An error occured!");
}
}
else
{
if ( UserManagement.UserInfo_Insert (obj) > 0 )
{
MessageBox.Show ("Record Succesfully Added!");
UserInfoForm form = new UserInfoForm ();
form.Refresh ();
}
else
{
MessageBox.Show ("An error occured!");
}
}
}
I just want to reload the form into original state once the data properly save or updated.
"this.Refresh();,this.Invalidate();,form.Refresh(),form.Invalidate()"
These functions just tell the window manager to redraw the form graphic; they have nothing to do with the state of the form's data.
Seems that all you need to do is set your control values back to their original values So, make a function on the form:
private void ResetForm()
{
//write code here to setup your dropdowns, put empty strings into textboxes, etc.
//pretty much the reverse of the process by which you copy the values into your user object.
}
and then in the sucess part of the code call the function:
if ( UserManagement.UserInfo_Update (obj) > 0 )
{
MessageBox.Show ("Record Succesfully Updated!");
//reset this form, no need to make another one...
ResetForm();
}
and you can also include a call to ResetForm() somewhere in your Form_Load, etc.
However
I'd recommend that once you are comfortable with doing this, you then stop doing it and use the data-binding facility that's built into Winforms; what it allows you to do is use the designer to bind user interface elements on the form (textboxes, etc) to various Class properties (e.g. UserManagement class).
This way you can simply "reset" your form by creating a new instance of UserManagement without having to deal with all the cruddy details of clearing out textboxes, etc. Otherwise you will find as your objects grow more complex, writing the code to manually reset form UI elments becomes more and more tedious and error-prone.
Hope that helps.
This is simple. You must create a new form object and close current form.
Form fr = new Form();
fr.Show();
this.Close();
I accomplished this by closing and opening the form. So replace the ***** with your form name
***your form name** ss = new **your form name***();
ss.Show();
this.Hide();
I would also recommend putting a form close to fully close the form:
private void ***Your form name***Closed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}
Try this :
this.Controls.Clear();
this.InitializeComponent();

Categories

Resources