C # listview deselect position

I am working on a Windows application that has a ListView containing a bunch of items. When the user clicks on an item, the application displays the details of the item. the user has the ability to edit this data. The user must click the "Save" button after each change, but, of course, this does not always happen.

If the user makes changes and does not click the "Save" button, the application displays a message asking if they want to save their changes. This box includes Cancel, and if they click Cancel, I would like to briefly close the selection of another item and save the user in the one they edited.

I cannot find a way to do this. I show the dialog from the itemselecedchanged event, if the item is changed and not saved, if the user clicks cancel, I remove my function from the event and manually change the selected item and after that I return the function to the event, but after that the event is called and the item that I select manually, not selected.

private bool EnsureSelected() { bool continue_ = true; if (_objectChange) { var res = MessageBox.Show("Do you want to save changes?", "Warning", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning); switch (res) { case DialogResult.Cancel: if (!string.IsNullOrEmpty(_selectedKey)) { listView_Keys.ItemSelectionChanged -= listView_Keys_ItemSelectionChanged; listView_Keys.Focus(); listView_Keys.Items[_selectedKey].Selected = true; listView_Keys.ItemSelectionChanged += listView_Keys_ItemSelectionChanged; } continue_ = false; break; case DialogResult.Yes: button_Save.PerformClick(); _objectChange = false; break; case DialogResult.No: _objectChange = false; break; default: throw new ArgumentOutOfRangeException(); } } return continue_; } 

UPDATE ::

I tried this solution:

  public partial class Form1 : Form { public Form1() { InitializeComponent(); } private ListViewItem currentSelection = null; private bool pending_changes = false; private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) { if (e.Item == currentSelection) { // if the current Item gets unselected but there are pending changes if (!e.IsSelected && pending_changes) { var res = MessageBox.Show("Do you want to save changes?", "Warning", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning); switch (res) { case DialogResult.Cancel: // we dont want to change the selected item, so keep it selected e.Item.Selected = true; break; case DialogResult.Yes: //button_Save.PerformClick(); pending_changes = false; break; case DialogResult.No: pending_changes = false; break; default: throw new ArgumentOutOfRangeException(); } } } else // not the selected button { if (!pending_changes && e.IsSelected) { // Item may be selected and we save it as the new current selection currentSelection = e.Item; } else if (pending_changes && e.IsSelected) { // Item may not be enabled, because there are pending changes e.Item.Selected = false; } } } private void Form1_Load(object sender, EventArgs e) { listView1.Items[0].Selected = true; } private void button1_Click(object sender, EventArgs e) { pending_changes = true; } } 

but this did not work, the first time the pending changes were correct, the message box was called up twice, and the second time nothing happened.

+7
c # listview winforms
source share
2 answers

First of all, whenever you select another item, the event should fire twice.

first for the element that was canceled (where e.IsSelected is false )

seconds for the selected item (where e.IsSelected is true )

Assuming you have the pending_changes flag pending_changes , when there are unsaved changes, then the following code should deselect the item.

Unfortunately, whenever you show a MessageBox, the listView loses focus again. When you click MessageBox, focus returns to listView, and this causes the control to fire its events again. That's why there is a dirty workaround that should remember that we clicked β€œCancel” in the message box and again performed the action for the next event.

Here is the code including a workaround:

 private ListViewItem currentSelection = null; private bool pending_changes = false; private bool cancel_flag = false; private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) { Console.WriteLine("Item " + e.ItemIndex + " is now " + e.IsSelected); if (e.Item != currentSelection) { // if another item gets selected but there are pending changes if (e.IsSelected && pending_changes) { if (cancel_flag) { // this handles the second mysterious event cancel_flag = false; currentSelection.Selected = true; e.Item.Selected = false; return; } Console.WriteLine("uh oh. pending changes."); var res = MessageBox.Show("Do you want to save changes?", "Warning", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning); switch (res) { case DialogResult.Cancel: // we dont want to change the selected item, so keep it selected currentSelection.Selected = true; e.Item.Selected = false; // for some reason, we will get the same event with the same argments again, // after we click the cancel button, so remember our decision cancel_flag = true; break; case DialogResult.Yes: // saving here. if possible without clicking the button, but by calling the method that is called to save pending_changes = false; currentSelection = e.Item; break; case DialogResult.No: pending_changes = false; currentSelection = e.Item; break; default: throw new ArgumentOutOfRangeException(); } } else if (e.IsSelected && !pending_changes) { currentSelection = e.Item; } } } 
+4
source share

You can simply reselect the elements and save the current state in Boolean flags to avoid running unnecessary code (for example, reloading the values ​​for the selected element if it has not actually changed).

Another way is to handle the LVN_ITEMCHANGING event, which, unfortunately, is not implemented in WinForms. You can find an extended list view class that supports this event, and helps prevent changes to being made to a ListView item that changes the flow of events on MSDN forums .

Here is the code from this thread:

 using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; public partial class Form1 : Form { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } public Form1() { ListViewEx listView; Controls.Add(listView = new ListViewEx { Dock = DockStyle.Fill, Items = { "One", "Two", "Three" } }); listView.ItemSelectionChanging += (s, e) => { if (e.Index == 1) e.Cancel = true; Debug.WriteLine(e); }; } } public class ItemSelectionChangingEventArgs : CancelEventArgs { public int Index { get; private set; } public bool NewValue { get; private set; } public bool CurrentValue { get; private set; } public ItemSelectionChangingEventArgs(int index, bool newValue, bool currentValue) { Index = index; NewValue = newValue; CurrentValue = currentValue; } public override string ToString() { return String.Format("Index={0}, NewValue={1}, CurrentValue={2}", Index, NewValue, CurrentValue); } } public class ListViewEx : ListView { private static readonly Object ItemSelectionChangingEvent = new Object(); public event EventHandler<ItemSelectionChangingEventArgs> ItemSelectionChanging { add { Events.AddHandler(ItemSelectionChangingEvent, value); } remove { Events.RemoveHandler(ItemSelectionChangingEvent, value); } } protected virtual void OnItemSelectionChanging(ItemSelectionChangingEventArgs e) { EventHandler<ItemSelectionChangingEventArgs> handler = (EventHandler<ItemSelectionChangingEventArgs>)Events[ItemSelectionChangingEvent]; if (handler != null) handler(this, e); } protected override void WndProc(ref Message m) { if (m.Msg == 0x2000 + 0x004E) // [reflected] WM_NOTIFY { uint nmhdrCode = (uint)Marshal.ReadInt32(m.LParam, NmHdrCodeOffset); if (nmhdrCode == LVN_ITEMCHANGING) { NMLISTVIEW nmlv = (NMLISTVIEW)Marshal.PtrToStructure(m.LParam, typeof(NMLISTVIEW)); if ((nmlv.uChanged & LVIF_STATE) != 0) { bool currentSel = (nmlv.uOldState & LVIS_SELECTED) == LVIS_SELECTED; bool newSel = (nmlv.uNewState & LVIS_SELECTED) == LVIS_SELECTED; if (newSel != currentSel) { ItemSelectionChangingEventArgs e = new ItemSelectionChangingEventArgs(nmlv.iItem, newSel, currentSel); OnItemSelectionChanging(e); m.Result = e.Cancel ? (IntPtr)1 : IntPtr.Zero; return; } } } } base.WndProc(ref m); } const int LVIF_STATE = 8; const int LVIS_FOCUSED = 1; const int LVIS_SELECTED = 2; const uint LVN_FIRST = unchecked(0U - 100U); const uint LVN_ITEMCHANGING = unchecked(LVN_FIRST - 0); const uint LVN_ITEMCHANGED = unchecked(LVN_FIRST - 1); static readonly int NmHdrCodeOffset = IntPtr.Size * 2; [StructLayout(LayoutKind.Sequential)] struct NMHDR { public IntPtr hwndFrom; public IntPtr idFrom; public uint code; } [StructLayout(LayoutKind.Sequential)] struct NMLISTVIEW { public NMHDR hdr; public int iItem; public int iSubItem; public int uNewState; public int uOldState; public int uChanged; public IntPtr lParam; } } 
+1
source share

All Articles