i have this WPF application in which i am trying to make popup window. well window is created and working fine, but what i want to do. that if i press OK/Update button in that popup, The selected values should be passed the the parent window and that popup should be closed.
i have seen this problem solution here..
C# - Return variable from child window to parent window in WPF
But i do not understand how this delegates works..
I have done it like this..
When click on button this method will opens the popup window.
private void btnAddBeneficiaryPopup_Click(object sender, RoutedEventArgs e)
{
try
{
AddBeneficiaryPopup addBen = new AddBeneficiaryPopup(refCustId);
addBen.selectedBeneID += value => selectedBeneficiaryID = value;
addBen.Show();
}
catch (Exception ex)
{ this.MyErrorMessage(ex); }
}
In Popup window in the constructor i have code like this.
public AddBeneficiaryPopup(int id)
{
InitializeComponent();
refCustId = id;
this.LoadReferenceBeneficiary();
}
Now this below Method i am working on and want to change it mostly..
private void cmbRefBene_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string beneId = null;
if (cmbRefBene.SelectedIndex >= 0)
{
try
{
beneId = ((DataRowView)cmbRefBene.SelectedItem).Row.ItemArray[0].ToString();
selectedBeneID = beneId;
bene.OpenConnection(str);
SqlDataReader reader = bene.LookUpSingleBene(int.Parse(beneId));
if (reader.Read())
{
tbName.Text = reader["Name"].ToString();
tbContactNo.Text = reader["ContactNo"].ToString();
btnUpdate.IsEnabled = true;
}
reader.Close();
bene.CloseConnnection();
}
catch (Exception ex)
{
MyErrorMessage(ex);
}
finally
{
bene.CloseConnnection();
}
}
}
As you can see in above code selectedBeneID = beneId; this beneId gives error. as i am trying to assign it selectedBeneID, as i think its a delegate to there must be another way to assigning values to it and passing it to the Parent Window..
But really am not sure how to work with this delegate and what to write to assign value to it.
i am getting error
"cannot implicitly convert type string to "System.Action<string>"
Solution A (getting your one working)
To get your solution running, change the following line in your cmbRefBene_SelectionChanged function:
selectedBeneID = beneId;
to
selectedBeneID(beneId);
Now you should not get the error message and the value should be set correctly.
Solution B
The following solution isn'n the most elegant but it always works:
Give your Popup Window a public Property (selectedBeneID).
public partial class AddBeneficiaryPopup : Window {
public string selectedBeneID;
.....
}
}
Set this property in your cmbRefBene_SelectionChanged function.
MainWindow:
change addBen.Show(); in your Main Window
to
addBen.ShowDialog();
idreturned = addBen.selectedBeneID;
Now The program will wait until you close the Popup.
After that you can access the property of your popup Window and read it out.
Related
I'm getting my way around c# slowly but surely lol in this code:
// create an instance of the main form
public formMain _formMain;
public void btnDynaDotCheck_Click(object sender, EventArgs e)
{
if (_formMain.bgWorker.IsBusy != true)
{
this.btnDynaDotCheck.Enabled = false;
_formMain.bgWorker.RunWorkerAsync("dr_begin_dd_check");
}
else
{
_formMain.returnMessage("Please wait untill the current task is finished...");
return;
}
}
I'm trying to access the background worker in formMain.cs from anotherForm.cs there is no errors in VS, but when run i get
"An unhandled exception of type 'System.NullReferenceException'
occurred in " and "Additional information: Object reference not set to
an instance of an object."
On this line:
if (_formMain.bgWorker.IsBusy != true)
So i'm not really getting access in this case eh?
Use dependency injection to inject a reference to your mainform into the otherone : somewhere in your mainform code do the following :
anotherForm _anotherForm = new anotherForm(this);
_anotherForm.Show();
assuming you are creating anotherform from code within the mainform, this is actually referring to the mainform.
In the constructor of anotherFrom do this :
public anotherForm(MainForm formMain){
_formMain = formMain;
}
This is by far the most elegant way to solve this issue. Because it makes clear that there is a dependency from one form to the other and makes the design intention clear.
Using a parent is also fine, but only if the mainform is really a parent of the other form.
Going via Application object will work, but the application object is a global and you hide your dependency that way.
_formMain = Application.OpenForms["formMain"];
Add this code in button click and try it.
When accessing _formMain from anotherForm:
I assume anotherForm is instantiated and called from _formMain like this:
anotherForm _anotherForm = new anotherForm();
_anotherForm.Show();
there's now several ways to access _formMain from _anotherForm but the easiest I think is to set _formMain as the parent of _anotherForm:
_anotherForm.Parent = this; // insert before _anotherForm.Show()
this way you can get hold of it in _anotherForm like this
public void btnDynaDotCheck_Click(object sender, EventArgs e)
{
...
formMain _formMain = this.Parent as formMain;
if(_formMain != null)
{
... // do whatever ever you have to do
}
}
but be careful... getting your BackgroundWorker in _formMain requires public methods you can call and return your BackgroundWorker.
Hope this helps!
Thanks for the help guys :)
I now have:
// create an instance of the formMain
formMain _formMain = new formMain();
public void btnDynaDotCheck_Click(object sender, EventArgs e)
{
if (_formMain.bgWorker.IsBusy != true)
{
this.btnDynaDotCheck.Enabled = false;
_formMain.bgWorker.RunWorkerAsync("dr_begin_dd_check");
}
else
{
_formMain.returnMessage("Please wait untill the current task is finished...");
return;
}
}
Which works :) it gets through to the main form:
public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// action to string format
string action = e.Argument as string;
if (action == "dr_begin_dd_check")
{
BeginInvoke((Action)(() =>
{
statusLabel.Text = "Access the bgw...";
}
));
} // dr_begin_dd_check
I'm now getting the error in the formMain:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
I'm not sure where the error lies in my above code or actually in the formMain section, or should i open a new question? :)
cheers guys
Graham
Hope this makes sense.
I have something like this:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (!Directory.Exists(dataFolder))
{
Directory.CreateDirectory(dataFolder);
}
try
{
using (DataContext context = new DataContext())
{
context.Database.CreateIfNotExists();
}
}
catch (IOException ex)
{
}
KeyProgram.Show();
if (Manager.KeyExists == true)
{
MainWindowViewModel viewModel = new MainWindowViewModel();
this.MainWindow = new MainWindow();
this.MainWindow.DataContext = viewModel;
this.ShutdownMode = System.Windows.ShutdownMode.OnMainWindowClose;
Helper.WindowDialogService.SetOwner(this.MainWindow);
viewModel.Init();
this.MainWindow.Show();
}
else {
Console.WriteLine("Please try again");
}
}
After showing my licensekey window, I want to break so that the system wants for the user input (Have the user type in license key) then proceed to run the if-else statement (at if (LicenseKeyManager.licenseKeyExists == true)).
However currently, onStartup, the application just runs all of the code first then if I type in the key and validate it, it does not run that if statement because it has already ran.
How do I break for the user input from the view before continuing to that if-statement?
Right now after LicenseKeyProgram.Show(), if I set a breakpoint at the if-statement, the application won't let the user input anything because it's stuck on loading (Can't perform any action on the window).
Do I need an event handler here or...?
Use ShowDialog instead than Show.
Relevant MSDN link:
http://msdn.microsoft.com/en-us/library/system.windows.window.showdialog%28v=vs.110%29.aspx
Oh wait, one more question, I can get the user input but the appplication does not ever run/hit the if-else statement? Why?
You have to setup the mainwindow before calling the showdialog on your other window or else it'll trigger the shutdown of the application (since the application will shutdown when all windows are closed).
Example:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
Window1 test = new Window1();
test.ShowDialog();
if (test.InvalidLicense)
{
Shutdown();
return;
}
window.Show();
}
Ressources:
WPF showing dialog before main window
http://www.ageektrapped.com/blog/the-wpf-application-class-overview-and-gotcha/
I am new to WPF and have been hunting for an answer, surely this is not difficult?
I have created a main window with links to multiple windows, and I want them to run modelessly alongside one another, but I don't want to open multiple instances of the SAME window.
In simple terms, I can have Windows A, B, C open at once, but not Windows, A, A, B, C, C.
I need to implement a check for the window I'm trying to open (in this case, EditSettings).
If open - activate it
if not open, open it.
I have the following code in Main, which is not working.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
EditSettings winEditSettings = new EditSettings();
string isOpen = null;
if (isOpen == "true")
{
winEditSettings.Activate();
}
else
{
winEditSettings.Show();
isOpen = "true";
}
}
}
Now I know what's wrong with this logic - every time I press the button to open EditSettings, it's setting isOpen to null again. If I don't set a value to isOpen, the If condition breaks.
I could initialise the variable 'isOpen' as a public variable outside the MenuItem_Click method, but then I think I would need an isOpen variable for each window I create!! Surely there is a better way?
The other option I tried is:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
EditSettings winEditSettings = new EditSettings();
if (winEditSettings.IsLoaded)
{
winEditSettings.Activate();
}
else { winEditSettings.Show(); }
I can't figure out why this isn't working, I tried isVisible, isLoaded, isActive - nothing is stopping the window from opening more than once. Thank you for the help!
There are people who'll perhaps throw a fit at the idea, but whenever I've needed to do this, I made the child window objects part of the application. Then, in your MenuItem_Click(), test if winEditSettings is null, instead.
It's still a member variable for each window (like your provisional isOpen solution), but having the window objects available can have advantages later, if you need to bridge information between the windows. In my cases, we wanted to be able to close all the child windows together, which (most trivially) meant keeping track of those objects in a central location.
Alternatively, if you want the setup completely decoupled, you could take a singleton-like approach and put the logic into your child window classes. Specifically, you could call EditSettings.Activate and let the class keep track of whether a window needs to be created or the existing window merely Show()n.
If I were handed your code to rewrite, I'd move it something like this:
private static EditSettings winEditSettings = null;
public static void WakeUp()
{
if (winEditSettings == null)
{
winEditSettings = new EditSettings();
}
winEditSettings.Activate(); // This may need to be inside the block above
winEditSettings.Show();
}
Both of those are part of the class (static), rather than an instance. Your application object therefore calls EditSettings.WakeUp() inside the original MenuItem_Click(), and never actually sees the child window, itself.
If you change your mind about the decoupled architecture later, by the way, you can add a get accessor to your winEditSettings and keep everybody fairly happy.
if (_adCst == null)
{
_adCst = new AddCustomerPage();
_adCst.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
_adCst.WindowState = System.Windows.WindowState.Normal;
_adCst.ResizeMode = System.Windows.ResizeMode.NoResize;
_adCst.Activate(); // This may need to be inside the block above
_adCst.Show();
}
else
{
if (!_adCst.IsLoaded == true)
{
_adCst = new AddCustomerPage();
_adCst.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
_adCst.WindowState = System.Windows.WindowState.Normal;
_adCst.ResizeMode = System.Windows.ResizeMode.NoResize;
_adCst.Show();
}
_adCst.Activate();
}
My suggestion would be that you set some form of a counter. This will prevent more than one instance of the window being opened.
int windowOpen = 1;
private void button_Click(object sender, RoutedEventArgs e)
{
if (windowOpen == 1)
{
WindowA winA = new WindowA();
winA.Show();
windowOpen++; //increments windowOpen by 1, windowOpen will now = 2
}
else if (windowOpen > 1)
{
MessageBox.Show("Window is already open");
}
}
I hope this helps.
For anyone else with this question, I have found another solution - which works except that it doesn't manage to bring the open window to the front (Activate). It does, however, prevent opening the same window more than once.
foreach (Window n in Application.Current.Windows)
if (n.Name == "winEditSettings")
{ winEditSettings.Activate(); }
else
{ winEditSettings.Show(); }
Can anyone speculate on why the window is not brought to the front, with Activate()?
EDIT
For others with this question, placing the winEditSettings.Activate() outside of the foreach loop does everything I'm trying to achieve:
foreach (Window n in Application.Current.Windows)
if (n.Name == "winEditSettings")
{ }
else
{ winEditSettings.Show(); }
winEditSettings.Activate();
This will stop multiple instances of the same window from opening, and will bring the window to the front if the user attempts to reopen it.
I have a main form that has most of the functionality in it. I was just wondering how would I pass on a variable from say a pop up form, to that main form.
Like for instance:
I have a main form that needs some connection info. So when you click the button "Enter Connection Info", it opens up a new form that the user can type in the IP Address he wants to use for his connection.
On this new form, I have a textbox and a button and once you enter the information it should close and pass on the string that contains the ip back to the original form.
Any suggestions? Do you think there is a better method of accomplishing this than using a windows form, and just going ahead and using a windows form or something? I'm quite perplexed on this issue at the moment.
Expose the textbox text as a public read only property. Show the connection form as a dialog and when it closes, get the connection from the property and then dispose the form:
in open form handler (button click, menu, whatever)
string connectionString = null;
using (ConnectionForm form = new ConnectionForm())
{
DialogResult result = form.ShowDialog();
if (result == DialogResult.Ok)
connectionString = form.ConnectionString
}
In you connection form:
public class ConnectionForm: Form
{
....
public string ConnectionString { get { return textBox1.Text; } }
}
You can create a public property in your main form and pass main form instance in pop-up constructor. In this way you can change the main form property.
You can also create an event in your pop-up form and hook it in your main form.
I like to use a pattern sort of like this (bear with me, c# is not my first language):
public class ValueForm: Form
{
public static string GetFromUser(string originalValue)
{
using (ConnectionForm form = new ConnectionForm())
{
form.TheValue = originalValue;
var result = form.ShowDialog();
if (result == DialogResult.Ok)
return form.TheValue;
else
return originalValue;
}
}
public string TheValue {
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
/* also some code behind your OK & cancel buttons to set
DialogResult appropriately,
and do any validation that you need to do
*/
}
and then you would use this like:
string newValue = ValueForm.GetFromUser(oldValue);
Reference Bind the controls on the dialog Form to properties of the Parent Form.
public dlgDbConnProps ( Form Owner )
{
// TODO: Complete member initialization
InitializeComponent ( );
owner = Owner;
}
private void cbo_ProviderLst_SelectedIndexChanged ( object sender, EventArgs e )
{
owner.Provider = cboLst.Text;
}
Here is another method that I have implemented:
... pass a Func to the child form constructor:
public dlgRequestLogin ( Func<string, string, bool> LoginMethod )
{
InitializeComponent ( );
p_loginMethod = LoginMethod;
}
... then handle on button click (or other appropriate event):
private void cmd_SendLoginCredentials_Click ( object sender, EventArgs e )
{
bool res = p_loginMethod.Invoke ( txt_UserID.Text, txt_UserPassword.Text );
}
I have a simple winforms application, on performing operations it shows a child window everytime. If I open a browser window (fully maximized) or some other window as usual
the application goes back with its childwindow, on clicking the exe which is in the taskbar
only the child window gets visible,but the application window doesn't come into view. I want to know how to show both the windows when I select it from taskbar.
childwindow is also a winform,whose toplevel property is set as true,apart from it nothing
is new(JUST BY CLICKING A BUTTON OR CELL IN GRID I CREATE AN OBJECT FOR THE FORM AND USES IT SHOW PROPERTY TO SHOW)
AlertMsgWindow _alertMsg;
void dataGridViewAlerts_MouseDoubleClick(object sender, MouseEventArgs e)
{
try
{
if (!string.IsNullOrEmpty(this.dataGridViewAlerts.getValue(0, this.dataGridViewAlerts.SelectedRow)))
{
this.dataGridViewAlerts.setCellImage(0, this.dataGridViewAlerts.SelectedRow, "NewsIconRead");
if (_alertMsg == null || _alertMsg.IsDisposed)
{
if (_alertMsg != null)
{
_alertMsg.onDeleteMessageRequest -= new DeleteMessage(_alertMsg_onDeleteMessageRequest);
_alertMsg.Dispose();
}
_alertMsg = new AlertMsgWindow();
_alertMsg.onDeleteMessageRequest += new DeleteMessage(_alertMsg_onDeleteMessageRequest);
}
_alertMsg.FillDetails(alertDetails[IDcollection[this.dataGridViewAlerts.SelectedRow]]);
if (!_alertMsg.Visible)
{
_alertMsg.Location = PointToScreen(new Point(this.Width / 4, -this.Height));
_alertMsg.Show(this);
}
if (onReadMessageReq != null)
onReadMessageReq(IDcollection[this.dataGridViewAlerts.SelectedRow]);
}
}
catch (Exception)
{ }
}
Note: THIS IS HAPPENING ONLY IN WINDOWS2000
I used a component named Dotnetmagic.dll,i dont know whether it causes the problem.can somebody helps me to solve this
I replaced these lines
_alertMsg.Location = PointToScreen(new Point(this.Width / 4, -this.Height));
With
_alertMsg.Left = x;
_alertMsg.Top = y;
and it solved my problem