我正在使用一个包含一堆项目的ListView的Windows应用程序。 当用户点击某个项目时,该应用会显示该项目的详细信息。该 用户然后有机会编辑这些细节。用户应该单击 每次更改后都会保存按钮,但当然并不总是这样。
如果用户进行了更改但未单击“保存”,则应用会显示一条消息 框询问他们是否愿意保存他们的更改。此框包含取消 按钮,如果他们点击取消,我想短路选择 另一个项目,并将用户保持在他们正在编辑的那个项目上。
我找不到这样做的方法,如果项目更改而不保存,我会显示itemselecedchanged事件中的对话框,如果用户单击取消,我从事件中删除我的功能并手动更改所选项目我将函数返回到事件,但在此之后,事件调用和我手动选择的项目未被选中。
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 ::
我尝试了这个解决方案:
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;
}
}
但这没有用,第一次挂起的更改为true时,消息框调用了两次,第二次没有发生任何事情。
答案 0 :(得分:4)
首先,每当你选择另一个项目时,该事件应该发射两次。
首先取消选中的项目(其中e.IsSelected
为false
)
第二个选择的项目(e.IsSelected
为true
)
假设您设置了一个标记pending_changes
,每当有未保存的更改时,下面的代码应该取消项目选择。
不幸的是,无论何时显示MessageBox,listView都会再次失去焦点。当您单击MessageBox时,焦点将返回到listView,这会导致控件再次触发其事件。这就是为什么需要一个肮脏的解决方法,需要记住我们点击了"取消"在消息框上,再次对下一个事件执行操作。
以下是包含解决方法的代码:
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;
}
}
}
答案 1 :(得分:1)
您可以重新选择项目并将当前状态保留在布尔标志中,以避免导致不必要的代码运行(例如,如果实际没有更改,则重新加载所选项目的值)。
另一种方法是处理LVN_ITEMCHANGING
事件,遗憾的是,事件并未在WinForms中实现。您可以找到支持此事件的扩展列表视图类,并允许阻止更改ListView item changing event thread on MSDN forums中的选择。
以下是该主题的代码:
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;
}
}