WinForms: Use an ImageList abstraction

This commit is contained in:
Ben Olden-Cooligan 2022-11-27 18:16:20 -08:00
parent 82b3456ec8
commit 7542765d45
3 changed files with 187 additions and 44 deletions

View File

@ -0,0 +1,157 @@
using System.Drawing;
using System.Windows.Forms;
using Eto.WinForms;
namespace NAPS2.EtoForms.WinForms;
public abstract class WinFormsImageList<T> where T : notnull
{
private readonly WinFormsListView<T> _listView;
private readonly ListViewBehavior<T> _behavior;
private WinFormsImageList(WinFormsListView<T> listView, ListViewBehavior<T> behavior)
{
_listView = listView;
_behavior = behavior;
}
private Image ItemToImage(T item)
{
return _behavior.GetImage(item, _listView.ImageSize).ToSD();
}
public abstract void Clear();
public abstract void Append(T item, ListViewItem listViewItem);
public abstract void Replace(T item, int index);
public abstract void DeleteFromEnd();
public abstract Image Get(ListViewItem listViewItem);
public abstract Image PartialAppend(T item);
public abstract void FinishPartialAppends(List<Image> images);
public class Custom : WinFormsImageList<T>
{
private readonly List<Image> _images = new();
public Custom(WinFormsListView<T> listView, ListViewBehavior<T> behavior) : base(listView, behavior)
{
}
public override void Clear()
{
foreach (var image in _images)
{
image.Dispose();
}
_images.Clear();
}
public override void Append(T item, ListViewItem listViewItem)
{
_images.Add(ItemToImage(item));
}
public override void Replace(T item, int index)
{
_images[index].Dispose();
_images[index] = ItemToImage(item);
}
public override void DeleteFromEnd()
{
_images[_images.Count - 1].Dispose();
_images.RemoveAt(_images.Count - 1);
}
public override Image Get(ListViewItem listViewItem)
{
return _images[listViewItem.Index];
}
public override Image PartialAppend(T item)
{
return ItemToImage(item);
}
public override void FinishPartialAppends(List<Image> images)
{
_images.AddRange(images);
}
}
public class Native : WinFormsImageList<T>
{
private readonly ImageList.ImageCollection _images;
public Native(WinFormsListView<T> listView, ListViewBehavior<T> behavior) : base(listView, behavior)
{
_images = _listView.NativeControl.LargeImageList.Images;
}
public override void Clear()
{
_images.Clear();
}
public override void Append(T item, ListViewItem listViewItem)
{
_images.Add(_behavior.GetImage(item, _listView.ImageSize).ToSD());
listViewItem.ImageIndex = _images.Count - 1;
}
public override void Replace(T item, int index)
{
_images[index] = ItemToImage(item);
}
public override void DeleteFromEnd()
{
_images.RemoveAt(_images.Count - 1);
}
public override Image Get(ListViewItem listViewItem) => throw new NotSupportedException();
public override Image PartialAppend(T item)
{
return ItemToImage(item);
}
public override void FinishPartialAppends(List<Image> images)
{
_images.AddRange(images.ToArray());
}
}
public class Stub : WinFormsImageList<T>
{
public Stub(WinFormsListView<T> listView, ListViewBehavior<T> behavior) : base(listView, behavior)
{
}
public override void Clear()
{
}
public override void Append(T item, ListViewItem listViewItem)
{
}
public override void Replace(T item, int index)
{
}
public override void DeleteFromEnd()
{
}
public override Image Get(ListViewItem listViewItem) => throw new NotSupportedException();
public override Image PartialAppend(T item)
{
return null!;
}
public override void FinishPartialAppends(List<Image> images)
{
}
}
}

View File

@ -50,14 +50,24 @@ public class WinFormsListView<T> : IListView<T> where T : notnull
_view.MouseMove += OnMouseMove;
_view.MouseLeave += OnMouseLeave;
_view.OwnerDraw = true;
_view.DrawItem += ViewOnDrawItem;
ImageList = UseCustomRendering
? new WinFormsImageList<T>.Custom(this, _behavior)
: !_behavior.Checkboxes
? new WinFormsImageList<T>.Native(this, _behavior)
: new WinFormsImageList<T>.Stub(this, _behavior);
if (UseCustomRendering)
{
_view.OwnerDraw = true;
_view.DrawItem += CustomRenderItem;
}
}
private void ViewOnDrawItem(object sender, DrawListViewItemEventArgs e)
private bool UseCustomRendering => !_behavior.ShowLabels && !_behavior.Checkboxes;
private void CustomRenderItem(object sender, DrawListViewItemEventArgs e)
{
int width, height;
var image = ImageList[e.Item.Index];
var image = ImageList.Get(e.Item);
if (image.Width > image.Height)
{
width = ImageSize;
@ -119,7 +129,7 @@ public class WinFormsListView<T> : IListView<T> where T : notnull
private ListView.ListViewItemCollection Items => _view.Items;
private List<Image> ImageList { get; } = new();
private WinFormsImageList<T> ImageList { get; }
public void SetItems(IEnumerable<T> items)
{
@ -129,29 +139,17 @@ public class WinFormsListView<T> : IListView<T> where T : notnull
}
_refreshing = true;
Items.Clear();
ClearImageList();
ImageList.Clear();
foreach (var item in items)
{
if (!_behavior.Checkboxes)
{
ImageList.Add(_behavior.GetImage(item, ImageSize).ToSD());
}
var listViewItem = Items.Add(GetLabel(item));
listViewItem.Tag = item;
ImageList.Append(item, listViewItem);
}
SetSelectedItems();
_refreshing = false;
}
private void ClearImageList()
{
if (!_behavior.Checkboxes)
{
foreach (var image in ImageList) image.Dispose();
ImageList.Clear();
}
}
private void SetSelectedItems()
{
for (int i = 0; i < Items.Count; i++)
@ -176,17 +174,15 @@ public class WinFormsListView<T> : IListView<T> where T : notnull
}
_refreshing = true;
_view.BeginUpdate();
ClearImageList();
ImageList.Clear();
foreach (var image in Items.OfType<ListViewItem>().Select(x => (T) x.Tag))
var images = new List<Image>();
foreach (ListViewItem listViewItem in Items)
{
ImageList.Add(_behavior.GetImage(image, ImageSize).ToSD());
}
foreach (ListViewItem item in Items)
{
item.ImageIndex = item.Index;
var item = (T) listViewItem.Tag;
images.Add(ImageList.PartialAppend(item));
}
ImageList.FinishPartialAppends(images);
_view.EndUpdate();
_refreshing = false;
@ -208,7 +204,7 @@ public class WinFormsListView<T> : IListView<T> where T : notnull
if (!diffs.AppendOperations.Any() && !diffs.ReplaceOperations.Any() &&
diffs.TrimOperations.Any(x => x.Count == Items.Count))
{
ClearImageList();
ImageList.Clear();
Items.Clear();
}
else
@ -219,32 +215,21 @@ public class WinFormsListView<T> : IListView<T> where T : notnull
// TODO: Use AddRange instead?
// TODO: Add this to the new ImageListViewBehavior
// _thumbnailProvider.GetThumbnail(append.Image.Source, ThumbnailSize)
var item = Items.Add(GetLabel(append.Item));
item.Tag = append.Item;
if (!_behavior.Checkboxes)
{
ImageList.Add(_behavior.GetImage(append.Item, ImageSize).ToSD());
}
var listViewItem = Items.Add(GetLabel(append.Item));
listViewItem.Tag = append.Item;
ImageList.Append(append.Item, listViewItem);
}
foreach (var replace in diffs.ReplaceOperations)
{
if (!_behavior.Checkboxes)
{
ImageList[replace.Index].Dispose();
ImageList[replace.Index] = _behavior.GetImage(replace.Item, ImageSize).ToSD();
}
Items[replace.Index].Tag = replace.Item;
ImageList.Replace(replace.Item, replace.Index);
}
foreach (var trim in diffs.TrimOperations)
{
for (int i = 0; i < trim.Count; i++)
{
if (!_behavior.Checkboxes)
{
ImageList[ImageList.Count - 1].Dispose();
ImageList.RemoveAt(ImageList.Count - 1);
}
Items.RemoveAt(Items.Count - 1);
ImageList.DeleteFromEnd();
}
}
}

View File

@ -398,6 +398,7 @@ public abstract class DesktopForm : EtoFormBase
Commands.ReorderMenu.Enabled =
Commands.EmailPdf.Enabled = Commands.Print.Enabled = ImageList.Images.Any();
// TODO: Changing the text on the command doesn't actually propagate to the widget
// "All" dropdown items
Commands.SaveAllPdf.MenuText = Commands.SaveAllImages.MenuText = Commands.EmailAllPdf.MenuText =
Commands.ReverseAll.MenuText = string.Format(MiscResources.AllCount, ImageList.Images.Count);