dmdb/view/CustomListView.cs

286 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using dezentrale.model;
namespace dezentrale.view
{
public abstract class CustomListView<T> : ListView where T : class
{
protected abstract List<ConfigLVDataHandler> DefaultColumns { get; }
protected List<T> internalList = null;
private ContextMenu cm = new ContextMenu();
private List<ConfigLVDataHandler> actualColumns = null;
private bool checkBoxes = true;
private bool columnConfiguration = true;
private int internalColumns = 0;
protected void BuildColumns()
{
this.Columns.Add(new ColumnHeader() { Text = checkBoxes ? "[x]" : "", Width = checkBoxes ? 24 : 0, }); //LVI column is always there!
this.ItemCheck += (sender, e) =>
{
//This is a workaround for checkboxes keep changing when MultiSelect with Shift
if ((ModifierKeys & (Keys.Shift | Keys.Control)) != 0)
e.NewValue = e.CurrentValue;
};
//todo: Add column-filtering (implementation with search dialog)
// MenuItem filterMenu = cm.MenuItems.Add("Filter by column...");
foreach (ConfigLVDataHandler col in actualColumns)
{
if(col.FilterAvailable)
{
//Process filter settings from DataHandler --> add menu entries
}
if (col.Visible) this.Columns.Add(new ColumnHeader() { Width = col.Width, Text = col.Display, TextAlign = col.TextAlign, Tag = col, });
}
//todo: Add column-filtering (implementation with search dialog)
/* if(filterMenu.MenuItems.Count < 1)
{
MenuItem unavailable = new MenuItem("unavailable");
unavailable.Enabled = false;
filterMenu.MenuItems.Add(unavailable);
}
*/
}
public CustomListView(List<ConfigLVColumn> actualColumns, EventHandler<ColumnsChangedArgs> ColumnsChanged = null, bool checkBoxes = false, bool columnConfiguration = true) : base()
{
this.ColumnsChanged = ColumnsChanged;
this.checkBoxes = checkBoxes;
this.columnConfiguration = columnConfiguration;
this.View = View.Details; //This gives us a traditional "list" view.
this.CheckBoxes = checkBoxes;
this.FullRowSelect = true;
this.ContextMenu = cm;
bool colsChanged = false;
this.actualColumns = new List<ConfigLVDataHandler>();
foreach (ConfigLVColumn lvc in actualColumns)
{
ConfigLVDataHandler def = DefaultColumns.Find(x => x.Name == lvc.Name);
if (def == null)
{
colsChanged = true;
continue; //this is a column we don't support. Old version? Just throw away.
}
this.actualColumns.Add(new ConfigLVDataHandler(lvc, def));
}
foreach (ConfigLVDataHandler c in DefaultColumns)
{
ConfigLVDataHandler act = this.actualColumns.Find(x => x.Name == c.Name);
if (act == null)
{
//this is a column we expect but didn't find in the serialized data. Old version? Just add it here.
this.actualColumns.Add(new ConfigLVDataHandler(c, c));
colsChanged = true;
}
}
BuildColumns();
if (columnConfiguration)
{
internalColumns += 2;
cm.MenuItems.Add("-");
cm.MenuItems.Add("Configure columns...", mnuConfigColumns_Click);
ColumnWidthChanged += CustomListView_ColumnWidthChanged;
MouseUp += CustomListView_MouseUp;
if (colsChanged)
ColumnsChanged?.Invoke(this, new ColumnsChangedArgs() { Columns = this.actualColumns });
}
}
public event EventHandler<ColumnsChangedArgs> ColumnsChanged;
private void mnuConfigColumns_Click(object sender, EventArgs e)
{
frmConfigureLVColumns colConfigure = new frmConfigureLVColumns(actualColumns);
colConfigure.ShowDialog();
if (colConfigure.ColumnsChanged)
{
ColumnsChangedArgs cca = new ColumnsChangedArgs() { Columns = colConfigure.GetLVColumnList() };
ColumnsChanged?.Invoke(this, cca);
if (cca.Cancel) return;
actualColumns = cca.Columns;
this.Items.Clear();
this.Columns.Clear();
BuildColumns();
LoadFromList(internalList);
}
}
private void SetNewColWidth(ConfigLVDataHandler c, int newWidth)
{
Console.WriteLine($"SetNewColWidth({c},{newWidth})");
List<ConfigLVDataHandler> newList = new List<ConfigLVDataHandler>();
foreach (ConfigLVDataHandler lvc in actualColumns)
{
ConfigLVDataHandler c2 = new ConfigLVDataHandler(lvc, lvc);
if (c.Name == lvc.Name) c2.Width = newWidth;
newList.Add(c2);
}
ColumnsChangedArgs cca = new ColumnsChangedArgs() { Columns = newList };
ColumnsChanged?.Invoke(this, cca);
if (cca.Cancel) return;
actualColumns = cca.Columns;
}
private bool resizeWhileMouseDown = false;
private ConfigLVDataHandler resizedWhileMouseDown = null;
private void CustomListView_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
{
ColumnHeader col = this.Columns[e.ColumnIndex];
ConfigLVDataHandler c = (ConfigLVDataHandler)col.Tag;
if ((c != null) && (c.Width != col.Width))
{
if(Control.MouseButtons == MouseButtons.None)
SetNewColWidth(c, col.Width);
else
{
//Just remember that setting for later
//Console.WriteLine($"ColumnWidthChanged(): MouseButtons={Control.MouseButtons}");
c.NewWidth = col.Width;
resizeWhileMouseDown = true;
resizedWhileMouseDown = c;
}
}
}
void CustomListView_MouseUp(object sender, MouseEventArgs e)
{
//Console.WriteLine($"MouseUp(): MouseButtons={Control.MouseButtons}");
if (resizeWhileMouseDown && (resizedWhileMouseDown != null))
{
//mono: Store new width.
resizeWhileMouseDown = false;
SetNewColWidth(resizedWhileMouseDown, resizedWhileMouseDown.NewWidth);
}
}
protected ListViewItem GetExistingLvi(T tag)
{
foreach (ListViewItem lvi in this.Items)
{
if (lvi.Tag == tag) return lvi;
}
return null;
}
protected ListViewItem UpdateLvi(ListViewItem lvi, T t)
{
if (lvi != null && t != null)
{
lvi.Tag = t;
lvi.SubItems.Clear();
foreach (ConfigLVDataHandler col in actualColumns)
{
if (!col.Visible) continue;
lvi.SubItems.Add(col.CustomToLvsi(lvi, t));
}
}
return lvi;
}
public void AddEntry(T t) { this.Items.Add(UpdateLvi(new ListViewItem(), t)); }
public void UpdateEntry(T t) { UpdateLvi(GetExistingLvi(t), t); }
public void RemoveEntry(T t) { ListViewItem lvi = GetExistingLvi(t); if (lvi != null) this.Items.Remove(lvi); }
public void DeSelectEntry(T t, bool sel = true) { ListViewItem lvi = GetExistingLvi(t); if (lvi != null) lvi.Selected = sel; }
public T GetFirstSelectedItem()
{
if (this.SelectedItems.Count < 1) return null;
return (T)this.SelectedItems[0].Tag;
}
public List<T> GetAllItems()
{
List<T> ret = new List<T>();
for (int i = 0; i < this.Items.Count; i++)
ret.Add((T)this.Items[i].Tag);
return ret;
}
public List<T> GetSelectedItems()
{
List<T> ret = new List<T>();
for(int i = 0; i < this.SelectedItems.Count; i++)
ret.Add((T)this.SelectedItems[i].Tag);
return ret;
}
public List<T> GetCheckedItems()
{
List<T> ret = new List<T>();
foreach (ListViewItem lvi in this.CheckedItems)
ret.Add((T)lvi.Tag);
return ret;
}
public bool ItemSelection(T entry, bool selected)
{
foreach (ListViewItem lvi in this.Items)
if (lvi.Tag.Equals(entry))
{
lvi.Selected = selected;
return true;
}
return false;
}
public MenuItem AddMenuItem(string text, EventHandler handler = null, MenuItem parent = null, bool enabled = true)
{
MenuItem mi = new MenuItem() { Text = text };
mi.Enabled = enabled;
if (handler != null) mi.Click += handler;
if (parent != null) parent.MenuItems.Add(mi);
else
{
cm.MenuItems.Add((cm.MenuItems.Count - internalColumns), mi);
}
return mi;
}
public void ClearMenuItems()
{
while(cm.MenuItems.Count > internalColumns)
{
cm.MenuItems.RemoveAt(0);
}
}
public void LoadFromList(List<T> entries)
{
this.SuspendLayout();
this.Items.Clear();
internalList = entries;
foreach (T t in entries)
{
//TBD implement generic filtering
/*
if (StatusFilter != null)
{
if (!StatusFilter.Contains(t.Status)) continue;
}*/
this.Items.Add(UpdateLvi(new ListViewItem(), t));
}
this.ResumeLayout(false);
}
}
public class ColumnsChangedArgs
{
public List<ConfigLVDataHandler> Columns { get; set; } = null;
public bool Cancel { get; set; } = false;
}
}