3

I have a ComboBox that have a list of manufacturers. When a user selects a manufacturer, a grid below is populated with data for the chosen manufacturer. That data can be modified. The user has to press the Save button after all changes are done.

But the user can forget to press Save and select another manufacturer from the ComboBox and the grid will be populated with another data, so previous changes will be lost.

So I need to ask user if he wants to save changes before selecting another manufacturer.

How can I do this? Or maybe you offer another way of solving my task (looking from another angle)?

10 Answers 10

10

Here is how we can subclass ComboBox to introduce new SelectedIndexChangingEvent with a possibility to cancel the changing:

    public class ComboBoxEx : ComboBox
{
    public event CancelEventHandler SelectedIndexChanging;

    [Browsable(false)]
    public int LastAcceptedSelectedIndex { get; private set; }

    public ComboBoxEx()
    {
        LastAcceptedSelectedIndex = -1;
    }

    protected void OnSelectedIndexChanging(CancelEventArgs e)
    {
        var selectedIndexChanging = SelectedIndexChanging;
        if (selectedIndexChanging != null)
            selectedIndexChanging(this, e);
    }


    protected override void OnSelectedIndexChanged(EventArgs e)
    {
        if (LastAcceptedSelectedIndex != SelectedIndex)
        {
            var cancelEventArgs = new CancelEventArgs();
            OnSelectedIndexChanging(cancelEventArgs);

            if (!cancelEventArgs.Cancel)
            {
                LastAcceptedSelectedIndex = SelectedIndex;
                base.OnSelectedIndexChanged(e);
            }
            else
                SelectedIndex = LastAcceptedSelectedIndex;
        }
    }

}
1
  • ^ This is the actual Answer.
    – CodeAngry
    Commented Dec 19, 2014 at 15:54
4

Create 2 class level variables

private bool selectionCancelled=false;
private int lastSelectedIndex=-1;

Then in SelectedIndex event you can write code as following

        if (!selectionCancelled)
        {
            if (MessageBox.Show("Are you sure you want to change the selection ?", this.Text, MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.No)
            {
                selectionCancelled = true;
                comboBox.SelectedIndex = lastSelectedIndex;
                return;
            }

            lastSelectedIndex = comboBox.SelectedIndex;
            // Normal code of the event handler

        }
        else
        {

            selectionCancelled = false;
        }
1
  • Works since .NET framework 2.0 Really good and simple, thank you ! I've just added possibility to avoid messagebox on programmatical assignement
    – 56ka
    Commented Nov 15, 2013 at 16:25
3

You should handle the ComboBox.SelectedIndexChanged event. Something like:

this.ComboBox1.SelectedIndexChanged += new system.EventHandler(ComboBox1_SelectedIndexChanged);

Then ComboBox1_SelectedIndexChanged() will be called whenever it changes and you can update your manufacturer info in that function. Save the old info before populating the new info. Or prompt the user if they really want to change it before saving.

1
  • 4
    Anything like e.Cancel for Combobox selection change?
    – IsmailS
    Commented Apr 30, 2010 at 14:31
2

Night Coder's solution is elegant and concise. I have packaged it in a dll.
(I call it CustomControls.) To do this create a new class library and add the first few statements to Night Coder's solution (copied here as a convenience).

Once you have compiled the code, you can add it as a reference. I actually loaded the dll into my Visual Studio Tools pane. That way I can drag the control onto my form at design time. Conveniently, the new event shows up in the properties list.

use System.ComponentModel;

use System.Windows.Forms; //this will need to be added as a reference

//your namespace will name your dll call it what you will

namespace CustomControls

Night Coder's solution follows:

public class ComboBoxEx : ComboBox
{
        public event CancelEventHandler SelectedIndexChanging;


    [Browsable(false)]
    public int LastAcceptedSelectedIndex { get; private set; }

    public ComboBoxEx()
    {
            LastAcceptedSelectedIndex = -1;
    }

    protected void OnSelectedIndexChanging(CancelEventArgs e)
    {
            var selectedIndexChanging = SelectedIndexChanging;
            if (selectedIndexChanging != null)
                    selectedIndexChanging(this, e);
    }


    protected override void OnSelectedIndexChanged(EventArgs e)
    {
            if (LastAcceptedSelectedIndex != SelectedIndex)
            {
                    var cancelEventArgs = new CancelEventArgs();
                    OnSelectedIndexChanging(cancelEventArgs);

                    if (!cancelEventArgs.Cancel)
                    {
                            LastAcceptedSelectedIndex = SelectedIndex;
                            base.OnSelectedIndexChanged(e);
                    }
                    else
                            SelectedIndex = LastAcceptedSelectedIndex;
            }
    }
}
1

An easy way to keep track of unsaved changes.

After loading any original values, disable the "Save" button.

When the user attempts to leave, check to see if the "Save" button is enabled.

Enable or disable the "Save" button as required.

1

I know this is an older question but I thought I'd add the method that I used. I'm not sure if it's any better. There should be a IndexChanging event or something in the regular ComboBox that can be cancelled.

The solution is a combination of @AftabAhmedKalhoro's and @jeffamaphone's posts but using the Tag property instead.

I didn't want to sub class the ComboBox, or have any extra private variables floating around in the form. But some might not like the Tag property, because it is kind of hidden if you're not use to using it (kind of left over from VB6).

Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    ComboBox1.Items.Add("Item1")
    ComboBox1.Items.Add("Item2")
    ComboBox1.Items.Add("Item3")
    ComboBox1.Items.Add("Item4")
    ' Load Value from database or whatever and set the value or index.
    ComboBox1.SelectedIndex = 0
    ComboBox1.Tag = ComboBox1.SelectedIndex

    ' I add the handler at the end because I don't want it to fire during loading the form.
    AddHandler ComboBox1.SelectedIndexChanged, New EventHandler(AddressOf ComboBox1_SelectedIndexChanged)
End Sub

Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
    If (ComboBox1.Tag <> ComboBox1.SelectedIndex) Then
        If MessageBox.Show("Warning! You are changing the index." & vbCrLf & _
                           "Do you wish to continue?", _
                           "Changing Index", _
                           MessageBoxButtons.YesNo, _
                           MessageBoxIcon.Warning) = Windows.Forms.DialogResult.Yes Then
            ComboBox1.Tag = ComboBox1.SelectedIndex
            ' Do Something.
        Else
            ComboBox1.SelectedIndex = ComboBox1.Tag
        End If
    End If
End Sub

Note that resetting the SelectedIndex will cause the event to fire again in this line:

ComboBox1.SelectedIndex = ComboBox1.Tag
0

If you wonder how you can receive notification when the selection changes, you can subscribe to the ComboBox.SelectedIndexChanged event.

If you want to offer the user the option to save, only when something has changed and she has forgotten so save her changes, you need to keep track of when these other fields change. This could be accomplished by maintaining a boolean value which is set to true whenever the user edits any fields. When the mentioned event occurs, check this value before deciding whether to offer the option to save.

0

The best thing to do here is compare the data entered in the ComboBox (likewise with other fields) to that already stored (in whatever - the DataSet, list object, etc.) and check for any differences. This way, if the user selects another item from the ComboBox but then changes it back to the original one, the program recognises that the data has still not been modified. (Handling the SelectionChangeCommited event, for example, and setting a boolean to true, wouldn't allow this to be detected, and would additionally be marginally harder to implement.) In this situation, the simplest and most elegant approach would also seem to provide the best functionality.

0

Great job nightcoder. Your code Works perfectly.

Thanks!

For developers who write in VB.NET here you have translation:

Imports System.ComponentModel

Public Class ComboBoxEx
  Inherits ComboBox

  Private pLastAcceptedSelectedIndex As Integer

  Public Event SelectedIndexChanging As CancelEventHandler

  Public Property LastAcceptedSelectedIndex() As Integer
    Get
      Return pLastAcceptedSelectedIndex
    End Get
    Set(ByVal value As Integer)
      pLastAcceptedSelectedIndex = value
    End Set
  End Property

  Public Sub New()
    LastAcceptedSelectedIndex = -1
  End Sub

  Protected Sub OnSelectedIndexChanging(ByVal e As CancelEventArgs)
    RaiseEvent SelectedIndexChanging(Me, e)
  End Sub

  Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)
    If LastAcceptedSelectedIndex <> SelectedIndex Then
      Dim cancelEventArgs As CancelEventArgs

      cancelEventArgs = New CancelEventArgs()
      OnSelectedIndexChanging(cancelEventArgs)

      If Not cancelEventArgs.Cancel Then
        LastAcceptedSelectedIndex = SelectedIndex
        MyBase.OnSelectedIndexChanged(e)
      Else
        SelectedIndex = LastAcceptedSelectedIndex
      End If
    End If
  End Sub
End Class
-2

The ComboBox provides an event called SelectedIndexChanged. This event raises whenever the Property SelectedIndex changes, so you have to handle the event for, Whenever the user wants to change the index of the combo, if the user has not saved the changes, Ask him to do so.

Not the answer you're looking for? Browse other questions tagged or ask your own question.