none
my own message box

    Question

  • hi,
    I'm writing a war-game and have reached the point where i want the user to confirm an attack : barrage, normal, charge or cancel.  so I've created a 'messagebox' that inherits 'form'.  this should probably inherit 'messagebox' and that may be my problem.
    the way i'm proceeding is that i build the form dynamically and add the buttons and their event-handlers intending to respond via a reference variable.  then i set this form's 'owner' to the calling form and .showdialog();  locking the system onto this 'messagebox' until it is disposed.  the reply is set to 'cancel' before the showdialog() and each button on this msgbox form has its own event handler which sets the reference 'reply' variable to the correct value for that button and then disposes the form.  this all works except the reference variable remains 'cancel' in the calling form.
    here's some of the relevant code :

      public class classPlyrAttackInterfaceMessageBox : Form
    
        {
    
            public enum enuPlyrAttackInterfaceResults { confirmNormalAttack, confirmBarrageAttack, confirmCharge, cancelAttack }
    
    
    
            enuPlyrAttackInterfaceResults reply;
    
    
    
            public classPlyrAttackInterfaceMessageBox(classBattleUnit attacker, classBattleUnit defender, ref enuPlyrAttackInterfaceResults reply_local)
    
            {
    
                reply = reply_local;
    
    
    
    

    with a typical button event handler :

        void btnNormal_Click(object sender, EventArgs e)
    
            {
    
                reply = enuPlyrAttackInterfaceResults.confirmNormalAttack;
    
                Dispose();
    
            }
    
    
    
    

    and the call sequence in the calling form looks like this :

    void PlyrMove_AttackInterface(classBattle.udtCartesian udrMapSquare)
    
    {
    
        PlyerSelectedUnit.unitIAmAttacking = terrain[udrMapSquare.x, udrMapSquare.y].unit;
    
        if (PlyerSelectedUnit.unitIAmAttacking != null)
    
        {
    
            classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults reply = classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.cancelAttack;
    
            classPlyrAttackInterfaceMessageBox msgBox = new classPlyrAttackInterfaceMessageBox(PlyerSelectedUnit, PlyerSelectedUnit.unitIAmAttacking, ref reply);
    
            msgBox.Owner = callingForm.mainForm;
    
            msgBox.ShowDialog();
    
            switch (reply)
    
            {
    
                case classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.confirmNormalAttack:
    
                    PlyerSelectedUnit.attackType = classBattle.enuAttackTypes.normal;
    
                    break;
    
    
    but the reference variable passed to the new instance of classPlyrAttack\InterfaceMessageBox() doesn't return with the value set in the event handler which disposes it.

    can i inherit the messagebox and use it? if so how?  if not, why is my reference variable not being set?

    can some one help?
    BadButBit
    my code is perfect until I don't find a bug
    Monday, October 19, 2009 6:38 PM

Answers

  • Hi,

    I just looked at your code a bit deeper. Why not change the design... instead of passing in the reply value by ref which is a bit ugly, add a public property to your dialog that allows you to set or return the backing field in the form, i.e something along the lines of this;

    public class classPlyrAttackInterfaceMessageBox : Form

        {

            public enum enuPlyrAttackInterfaceResults { confirmNormalAttack, confirmBarrageAttack, confirmCharge, cancelAttack  }



            enuPlyrAttackInterfaceResults reply;



            public classPlyrAttackInterfaceMessageBox(classBattleUnit attacker, classBattleUnit defender, enuPlyrAttackInterfaceResults defaultReply)

            {

                reply = reply_defaultReply;

    }

    public enuPlyrAttackInterfaceResults Reply
    {
      get { return reply; }
      set { reply = value; } //Could remove the set, since it's not actually used. Up to you.
    }

    ....

    void PlyrMove_AttackInterface(classBattle.udtCartesian udrMapSquare)

    {

        PlyerSelectedUnit.unitIAmAttacking = terrain[udrMapSquare.x, udrMapSquare.y].unit;

        if (PlyerSelectedUnit.unitIAmAttacking != null)

        {

            classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults reply = classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.cancelAttack;

            classPlyrAttackInterfaceMessageBox msgBox = new classPlyrAttackInterfaceMessageBox(PlyerSelectedUnit, PlyerSelectedUnit.unitIAmAttacking, reply);

            msgBox.Owner = callingForm.mainForm;

            msgBox.ShowDialog();
            reply = msgBox.Reply;
            msgBox.Dispose();

            switch (reply)

            {

                case classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.confirmNormalAttack:

                    PlyerSelectedUnit.attackType = classBattle.enuAttackTypes.normal;

                    break;



    I think the problem with your original code is that even though you pass reply by 'ref' the assignment of the parameter to the field inside the constructor isn't assigning a reference, but is assigning the value to the field... so if you changed the parameter inside the constructor the calling code would see the change, but changing the field won't do the same thing as it's not a reference to the same memory address.

    The property should work, and in my opinion is neater code too.

    • Marked as answer by BadButBit Monday, October 19, 2009 11:04 PM
    Monday, October 19, 2009 10:43 PM

All replies

  • Hi,

    Try closing the form with .Close instead of disposing it. When a form is shown using ShowDialog() the Close method explicitly DOES NOT call Dispose (this is one of the rare instances where Close & Dispose are different). The reason it doesn't call Dispose is because it assumes you may want to retrieve some kind of result (possibly from a variable, possibly from a control) and so it doesn't do a full clean up in order to enable you to get the result. That means that whenever ShowDialog is used the calling code must dispose the form. A common pattern is;

    using (MyDialog dialog = new MyDialog())
    {
      dialog.ShowDialog();
      result = dialog.MyResult;
    }

    The 'using' statement then disposes the form, but not until after the result is retrieved.

    You do not (and I believe can't) inherit from MessageBox, and doing so probably wouldn't help you anyway. If the above suggestion doesn't work, please post back here and I or someone else will see if we can spot another flaw.

    Good luck.
    Monday, October 19, 2009 7:03 PM
  • thanks for your reply,
    but it didn't work, the result is the same.
    there are a few articles on CodeProject that might help. 
    if you have something more I'll want to hear it.

    BadButBit


    my code is perfect until i find a bug
    Monday, October 19, 2009 10:26 PM
  • Hi,

    I just looked at your code a bit deeper. Why not change the design... instead of passing in the reply value by ref which is a bit ugly, add a public property to your dialog that allows you to set or return the backing field in the form, i.e something along the lines of this;

    public class classPlyrAttackInterfaceMessageBox : Form

        {

            public enum enuPlyrAttackInterfaceResults { confirmNormalAttack, confirmBarrageAttack, confirmCharge, cancelAttack  }



            enuPlyrAttackInterfaceResults reply;



            public classPlyrAttackInterfaceMessageBox(classBattleUnit attacker, classBattleUnit defender, enuPlyrAttackInterfaceResults defaultReply)

            {

                reply = reply_defaultReply;

    }

    public enuPlyrAttackInterfaceResults Reply
    {
      get { return reply; }
      set { reply = value; } //Could remove the set, since it's not actually used. Up to you.
    }

    ....

    void PlyrMove_AttackInterface(classBattle.udtCartesian udrMapSquare)

    {

        PlyerSelectedUnit.unitIAmAttacking = terrain[udrMapSquare.x, udrMapSquare.y].unit;

        if (PlyerSelectedUnit.unitIAmAttacking != null)

        {

            classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults reply = classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.cancelAttack;

            classPlyrAttackInterfaceMessageBox msgBox = new classPlyrAttackInterfaceMessageBox(PlyerSelectedUnit, PlyerSelectedUnit.unitIAmAttacking, reply);

            msgBox.Owner = callingForm.mainForm;

            msgBox.ShowDialog();
            reply = msgBox.Reply;
            msgBox.Dispose();

            switch (reply)

            {

                case classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.confirmNormalAttack:

                    PlyerSelectedUnit.attackType = classBattle.enuAttackTypes.normal;

                    break;



    I think the problem with your original code is that even though you pass reply by 'ref' the assignment of the parameter to the field inside the constructor isn't assigning a reference, but is assigning the value to the field... so if you changed the parameter inside the constructor the calling code would see the change, but changing the field won't do the same thing as it's not a reference to the same memory address.

    The property should work, and in my opinion is neater code too.

    • Marked as answer by BadButBit Monday, October 19, 2009 11:04 PM
    Monday, October 19, 2009 10:43 PM
  • Yup, you've got it.  Your type is a value type, the code in the dialog will update a copy.  The ref keyword doesn't help, that allows you to update the value only while the method is running.  It's long gone by the time the dialog completes.  There are lots of ways to fix this  The cleaner ones:

    - wrap the return value in a little helper class
    - expose the reply as a public property in the dialog form
    - create a new public method in the dialog form that calls ShowDialog for you.

    Also, don't forget to take advantage of the return value of ShowDialog().

    Hans Passant.
    Monday, October 19, 2009 11:04 PM
  • yea!
    that works great.

    thanks,
    BadButBit
    my code is perfect until i don't find a bug
    Monday, October 19, 2009 11:05 PM