There is a MenuItem click event MainMenu_Define_Material which opens a window called Material. I get info from a textbox called txt_density and I save that info in density and return to main window via OK-button having click event Material_btn_OK_Click.
My question is, how I can avoid passing the last session values every time I open the window Material?
I want, once density is set, every time I open Material window I want to see the values of previous session.
private void MainMenu_Define_Material(object sender, RoutedEventArgs e)
{
newWin_material = new Material();
newWin_material.btn_OK.Click += new RoutedEventHandler(Material_btn_OK_Click);
if (density != -1)
{
newWin_material.txt_density.Text = density.ToString();
}
newWin_material.ShowDialog();
}
private void Material_btn_OK_Click(object sender, RoutedEventArgs e)
{
density = System.Convert.ToSingle(newWin_material.txt_density.Text);
newWin_material.Close();
}
Not very clear where that density comes from, but you can insert that field in uour data class what you can hold on data layer or may be like ObjectDataProvider for modelview.
There are a lor of different solutioms our there. The basic idea is:
Define common, shared data storage and keep there alll values you want to share between different windows.
Define a data class. I used here a static class with a static member Desnity.
public static class DataClass
{
public static double Desnsity;
}
After in any window you're able to access that field (read/write), like
DataClass.Density
Hope this is clear.
There is no concept of Session in WPF. You can try creating a static variable to do this or a app config value or pass a parameter via the constructor.
If you are going to new it ( e.g. new Material(); ) then it is going to start with zero information. That is what new does. What is wrong with passing the value in the ctor?
Try
MainWindow
{
private newWin_material = new Material(); // just new it ONCE
// this may need to be in MainWindow ctor.
newWin_material.btn_OK.Click += new RoutedEventHandler(Material_btn_OK_Click);
Related
I am new to WPF and Coding in general, I am trying to create a small UI to read and write data on .txt files. All the reading part functions works well but now I am having trouble making a small UI to display the information found in the .txt files.
My problem is more with how button clicks work in WPF. All the data is stored in the forms of object of class Static_Data_Scheme and Dynamic_Data_Scheme inside the program, these class are just simple collection of Dictionaries Objects. Part of my data is 'static' meaning it will be stored in .txt files that won't change location and so can be loaded when the program is started, and another part of my data is 'Dynamic' meaning it can be stored anywhere and the file path can be changed using a file explorer in the program. Important thing to note, the Static_Data_Scheme is needed to generate the Dynamic_Data_Scheme.
My initial way of handling it when I made the program to test it out was to generates both Data Scheme with the same button press called load, but since the static dictionaries can be loaded right at the start of the program I want to try and add that method to the MainWindow instead and only have the program generates the Dynamic_Data_Scheme when I press the load button, but I'm unable to find any documentation on how to add arguments to the click method of the button.
Current code that works but that I don't like due to the fact that Static_Data_Scheme.Start method is called each time the load button is pressed and could instead be loaded only once when the program starts :
public MainWindow()
{
InitializeComponent();
}
private void Save_Loader_Click(object sender, RoutedEventArgs e)
{
Static_Data_Scheme static_Data_Scheme = new Static_Data_Scheme();
static_Data_Scheme = static_Data_Scheme.Start();
Dynamic_Data_Scheme dynamic_Data_Scheme = new Dynamic_Data_Scheme();
Save_Parser.Parse_Save(#"file_path", static_Data_Scheme, ref dynamic_Data_Scheme);
}
What I wanna try to achieve is something like that :
public MainWindow()
{
InitializeComponent();
Static_Data_Scheme static_Data_Scheme = new Static_Data_Scheme();
static_Data_Scheme = static_Data_Scheme.Start();
}
private void Save_Loader_Click(object sender, RoutedEventArgs e)
{
Dynamic_Data_Scheme dynamic_Data_Scheme = new Dynamic_Data_Scheme();
Save_Parser.Parse_Save(#"file_path", static_Data_Scheme, ref dynamic_Data_Scheme);
}
But this doesn't work due to the fact that the Save_Parser.Parse_Save method lack the static_Data_Scheme variable and I can't add it to the Save_Loader_Click method either.
So my question is how do I tell my ave_Loader_Click method to get the static_Data_Scheme from the program?
You almost had it, just move the variable outside of your method:
Static_Data_Scheme static_Data_Scheme = new();
public MainWindow()
{
InitializeComponent();
/* static_Data_Scheme = ???? */static_Data_Scheme.Start();
}
private void Save_Loader_Click(object sender, RoutedEventArgs e)
{
var Data_Scheme = new Dynamic_Data_Scheme();
Save_Parser.Parse_Save(#"file_path", static_Data_Scheme, ref dynamic_Data_Scheme);
}
I have a very simple program that for some reason has me stumped. I put it down, came back at it again this morning and I'm still stumped. First off, I'm aware this is not an ideal solution. I have two forms: Main and Log. The Main form has a button that adds to List _debugLog when clicked. When btnDebug is clicked, it opens the Log form, passing _debugLog to it. Everything is fine, the timer is setup and runs, everything is normal. The event log.UpdateLog() is triggered every 2.5 seconds to update the Log form with the updated log. However, mainFormLog.Count and _log.Count are always the same and they BOTH increase when btnAdd is clicked on the main form. How does _log have the new _debugLog (mainFormLog) from the tick event?
namespace Tool
{
public partial class Main : Form
{
private List<string> _debugLog = new List<string>();
public Main()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, EventArgs e)
{
_debugLog.Add("message!");
}
private void btnDebug_Click(object sender, EventArgs e)
{
Log log = new Log(_debugLog);
log.Show();
Timer dt = new Timer();
dt.Interval = 2500;
dt.Enabled = true;
dt.Tick += delegate {
log.UpdateLog(_debugLog);
};
}
}
public partial class Log : Form
{
private List<string> _log;
public Log(List<string> log)
{
InitializeComponent();
_log = log;
}
public void UpdateLog(List<string> mainFormLog)
{
if (mainFormLog.Count > _log.Count)
{
MessageBox.Show("Log has been updated!");
}
else
{
MessageBox.Show("Nothing new!" + mainFormLog.Count.ToString() + " / " + _log.Count.ToString());
}
}
}
}
Well, you're passing the reference to the list from Main to Log, so it's actually the same list.
If you want a separate list that gets initialized with the list from Main you can use:
public Log(List<string> log)
{
InitializeComponent();
_log = new List<string>(log);
}
Maybe this helps to understand the difference between variables and references:
For a value type, the value is the information itself. For a reference
type, the value is a reference which may be null or may be a way of
navigating to an object containing the information.
For example, think of a variable as like a piece of paper. It could
have the value "5" or "false" written on it, but it couldn't have my
house... it would have to have directions to my house. Those
directions are the equivalent of a reference. In particular, two
people could have different pieces of paper containing the same
directions to my house - and if one person followed those directions
and painted my house red, then the second person would see that change
too. If they both just had separate pictures of my house on the paper,
then one person colouring their paper wouldn't change the other
person's paper at all.
All your variables _debugLog, mainFormLog, and _log are pointing to the same list in memory. You've only created one list, and when you assign a new variable to that list, it's just a pointer to some location in memory, it doesn't automatically create a new copy of the list.
I'm running a main WinForm with most UI elements, and then an Add form is called when the Add button is picked. It should receive input, and send it back to the WinForm once accepted.
This code runs when you click the "Add" button on the main class:
public void addButton_Click(object sender, EventArgs e)
{
AddView newadd = new AddView();
newadd.Show();
}
This code (also in the main class) should run based upon a button in the AddView:
public void AddDashObject(string dashName, string dashIdentifier, int dashFunction, string dashFunctionInfo, int dashVerbosity)
{
DashObject tmp = new DashObject("","",0,"",0);
tmp.DashName = dashName;
tmp.DashIdentifier = dashIdentifier;
tmp.DashFunction = dashFunction;
tmp.DashFunctionInfo = dashFunctionInfo;
tmp.DashVerbosity = dashVerbosity;
dashloaded.Add(tmp);
ReloadDashObjects();
}
I'm not really sure how to communicate between the forms - I can use a type created in the main class, and also methods, but I'm pretty sure I'm creating a separate instance. How can I communicate with the existing one?
MainView mnfrm = new MainView();
MainView.DashObject tmp = new MainView.DashObject("","",0,"",1); // Defaults
private void button1_Click(object sender, EventArgs e)
{
mnfrm.dashloaded.Add(tmp); // Add the default DashObject to MainView's currently loaded DashObjects
mnfrm.ReloadDashObjects(); // Reload the list
}
Not sure how to proceed on this - any advice?
Since the Add dialog is expected to be modal, the decoupled way to do this is to only close the dialog with an OK status. I.e. the form is just an input control that doesn't actually "do" anything.
Then the main form can:
1) check how the dialog was closed right after ShowDialog(),
2) call some GetResult() function to get the values from the add form before disposing it.
3) call the business logic that actually creates and reloads the DashObjects.
I have this code, in which I read the data i deserialized in a gridview, let's name it FormReadDatabases
It gets populated like this:
xmlData = (xml.ServiceConfig)serializer.Deserialize(reader);
dataGridView1.DataSource = xmlData.Databases;
Then in each row of the grid I have a button 'Tables'
After I click it a new form appears FormReadTables
It gets populated like this:
BindingList<xml.Table> table = new BindingList<xml.Table>();
dataGridView4.DataSource = table;
Then I have a button which helps me add a new table, it works fine, the new row appears in the FormReadTables, But when i close the form and I am now at the FormReadDatabases if I click again on the Table button the changes are not saved.
Any idea how to avoid this?
This should be simple, data binding needs to happen using a mechanism that can hold value even when forms are opened or closed:
First way could be use a static type as follows:
static BindingList<xml.Table> table;
public BindingList<xml.Table> FetchTable()
{
if(table == null)
{
table = new BindingList<xml.Table>();
}
return table
}
dataGridView4.DataSource = FetchTable();
There's a catch out here what if the form can have multiple instances than can access the static variable, then while updating the table type it needs to be locked / synchronized
Another option would be table type is part of the main form, which loads the child form and in the constructor of the child form it gets the instance of the parent form, which is used updated and is retained even after closing child form. This will also need synchronization for multiple user / thread access
public class ParentForm
{
public BindingList<xml.Table> table = new BindingList<xml.Table>();
}
public class ChildForm
{
ParentForm localPf;
pulic ChildForm(ParentForm pf)
{
localPf = pf;
}
dataGridView4.DataSource = localPf.table;
}
Noe any change to parent form object's table variable will persist till the point Parent form is in the memory, but please note this implementation is not yet threads safe
Each time the form opens, you are creating a new BindingList.
BindingList<xml.Table> table = new BindingList<xml.Table>();
Instead, have the other page contain a variable for this, and when you 'new' the other form, pass in the variable.
The Actions taken on the opened form are byref, and therefore will update your host forms variable. This means the next time you open the form, the variable you pass to it will already have the previous changes already stored in it.
Example as requested:
I don't have my WinForms environment at hand, but this shows the important concepts.
namespace Bob
{
public class FormLucy
{
private BindingList<xml.Table> table = new BindingList<xml.Table>();
// your form stuff..
protected void ButtonClick(object sender, EventArgs e)
{
var frm = new FormTracy(table);
// init your forms properties, position etc
fmr.ShowDialog();
}
}
}
In my project on the WindowsForms, if I have a static instance inside the form, when I'm opening my form at the first time, it works. But if I'll close it and open again, the form will be empty. Why can it be?
public partial class Computer : Form
{
static Indicators indicators = new Code.Indicators();
}
P.S. I'm making it static, because I want to save it's value after the form will be closed.
Edit 1: Opening the form
private void button3_Click(object sender, EventArgs e)
{
Computer computer = new Computer();
computer.ShowDialog();
}
Edit 2: Computer Form
namespace WF
{
public partial class Computer : Form
{
static Code.Indicators indicators = new Code.Indicators();
public Computer()
{
if (indicators.isComputerAlreadyRunning == false)
{
InitializeComponent();
pictureBox1.Image = Properties.Resources.Computer1;
indicators.isComputerAlreadyRunning = true;
}
}
// My not successful try to save the value of the variable
public Code.Indicators ShowForm()
{
return new Code.Indicators(indicators.isComputerAlreadyRunning);
}
}
}
I don't think that static members work well with the Windows Form lifecycle.
I suggest you make Indicators a normal instance member of your form. To preserve state beyond the life of a form you can copy your state from the form and copy it back to the form when you open it.
// Keep this in the proper place
var indicators = new Code.Indicators();
...
// Copy back and forth for the life time of the form
using (var form = new Computer())
{
form.Indicators.AddRange(indicators);
form.Close += (s, e) =>
{
indicators.Clear();
indicators.AddRange(form.Indicators);
}
}
...
According to the constructor in the Computer class, the indicators.isComputerAlreadyRunning is set to true the first time the form is created.
So when Computer is created the second time, the if condition will fail and the whole if block will be skipped. That means your InitializeComponent(); won't get run and hence nothing in the form will shows up.
Put the InitializeComponent(); outside the if clause to make it work.