RaUI/Source/ryControls/ObjectListView/Implementation/VirtualGroups.cs
鑫Intel 523add43be ### 2021-01-12 dev更新
------
#### ryControls    V2.1.2101.1201
- *.[更新]内置的ObjectListView从1.13更新到2.9.1版本,并对主要属性进行汉化。
- *.[修复]修复新版ObjectListView选中项有筛选结果时,筛选结果白色字体看不清的BUG。
- *.[改进]TextBoxEx2默认事件改为TextChanged2。
2021-01-12 16:32:29 +08:00

356 lines
14 KiB
C#

/*
* Virtual groups - Classes and interfaces needed to implement virtual groups
*
* Author: Phillip Piper
* Date: 28/08/2009 11:10am
*
* Change log:
* 2011-02-21 JPP - Correctly honor group comparer and collapsible groups settings
* v2.3
* 2009-08-28 JPP - Initial version
*
* To do:
*
* Copyright (C) 2009-2014 Phillip Piper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
*/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace BrightIdeasSoftware
{
/// <summary>
/// A IVirtualGroups is the interface that a virtual list must implement to support virtual groups
/// </summary>
public interface IVirtualGroups
{
/// <summary>
/// Return the list of groups that should be shown according to the given parameters
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
IList<OLVGroup> GetGroups(GroupingParameters parameters);
/// <summary>
/// Return the index of the item that appears at the given position within the given group.
/// </summary>
/// <param name="group"></param>
/// <param name="indexWithinGroup"></param>
/// <returns></returns>
int GetGroupMember(OLVGroup group, int indexWithinGroup);
/// <summary>
/// Return the index of the group to which the given item belongs
/// </summary>
/// <param name="itemIndex"></param>
/// <returns></returns>
int GetGroup(int itemIndex);
/// <summary>
/// Return the index at which the given item is shown in the given group
/// </summary>
/// <param name="group"></param>
/// <param name="itemIndex"></param>
/// <returns></returns>
int GetIndexWithinGroup(OLVGroup group, int itemIndex);
/// <summary>
/// A hint that the given range of items are going to be required
/// </summary>
/// <param name="fromGroupIndex"></param>
/// <param name="fromIndex"></param>
/// <param name="toGroupIndex"></param>
/// <param name="toIndex"></param>
void CacheHint(int fromGroupIndex, int fromIndex, int toGroupIndex, int toIndex);
}
/// <summary>
/// This is a safe, do nothing implementation of a grouping strategy
/// </summary>
public class AbstractVirtualGroups : IVirtualGroups
{
/// <summary>
/// Return the list of groups that should be shown according to the given parameters
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
public virtual IList<OLVGroup> GetGroups(GroupingParameters parameters) {
return new List<OLVGroup>();
}
/// <summary>
/// Return the index of the item that appears at the given position within the given group.
/// </summary>
/// <param name="group"></param>
/// <param name="indexWithinGroup"></param>
/// <returns></returns>
public virtual int GetGroupMember(OLVGroup group, int indexWithinGroup) {
return -1;
}
/// <summary>
/// Return the index of the group to which the given item belongs
/// </summary>
/// <param name="itemIndex"></param>
/// <returns></returns>
public virtual int GetGroup(int itemIndex) {
return -1;
}
/// <summary>
/// Return the index at which the given item is shown in the given group
/// </summary>
/// <param name="group"></param>
/// <param name="itemIndex"></param>
/// <returns></returns>
public virtual int GetIndexWithinGroup(OLVGroup group, int itemIndex) {
return -1;
}
/// <summary>
/// A hint that the given range of items are going to be required
/// </summary>
/// <param name="fromGroupIndex"></param>
/// <param name="fromIndex"></param>
/// <param name="toGroupIndex"></param>
/// <param name="toIndex"></param>
public virtual void CacheHint(int fromGroupIndex, int fromIndex, int toGroupIndex, int toIndex) {
}
}
/// <summary>
/// Provides grouping functionality to a FastObjectListView
/// </summary>
public class FastListGroupingStrategy : AbstractVirtualGroups
{
/// <summary>
/// Create groups for FastListView
/// </summary>
/// <param name="parmameters"></param>
/// <returns></returns>
public override IList<OLVGroup> GetGroups(GroupingParameters parmameters) {
// There is a lot of overlap between this method and ObjectListView.MakeGroups()
// Any changes made here may need to be reflected there
// This strategy can only be used on FastObjectListViews
FastObjectListView folv = (FastObjectListView)parmameters.ListView;
// Separate the list view items into groups, using the group key as the descrimanent
int objectCount = 0;
NullableDictionary<object, List<object>> map = new NullableDictionary<object, List<object>>();
foreach (object model in folv.FilteredObjects) {
object key = parmameters.GroupByColumn.GetGroupKey(model);
if (!map.ContainsKey(key))
map[key] = new List<object>();
map[key].Add(model);
objectCount++;
}
// Sort the items within each group
// TODO: Give parameters a ModelComparer property
OLVColumn primarySortColumn = parmameters.SortItemsByPrimaryColumn ? parmameters.ListView.GetColumn(0) : parmameters.PrimarySort;
ModelObjectComparer sorter = new ModelObjectComparer(primarySortColumn, parmameters.PrimarySortOrder,
parmameters.SecondarySort, parmameters.SecondarySortOrder);
foreach (object key in map.Keys) {
map[key].Sort(sorter);
}
// Make a list of the required groups
List<OLVGroup> groups = new List<OLVGroup>();
foreach (object key in map.Keys) {
string title = parmameters.GroupByColumn.ConvertGroupKeyToTitle(key);
if (!String.IsNullOrEmpty(parmameters.TitleFormat)) {
int count = map[key].Count;
string format = (count == 1 ? parmameters.TitleSingularFormat : parmameters.TitleFormat);
try {
title = String.Format(format, title, count);
} catch (FormatException) {
title = "Invalid group format: " + format;
}
}
OLVGroup lvg = new OLVGroup(title);
lvg.Collapsible = folv.HasCollapsibleGroups;
lvg.Key = key;
lvg.SortValue = key as IComparable;
lvg.Contents = map[key].ConvertAll<int>(delegate(object x) { return folv.IndexOf(x); });
lvg.VirtualItemCount = map[key].Count;
if (parmameters.GroupByColumn.GroupFormatter != null)
parmameters.GroupByColumn.GroupFormatter(lvg, parmameters);
groups.Add(lvg);
}
// Sort the groups
if (parmameters.GroupByOrder != SortOrder.None)
groups.Sort(parmameters.GroupComparer ?? new OLVGroupComparer(parmameters.GroupByOrder));
// Build an array that remembers which group each item belongs to.
this.indexToGroupMap = new List<int>(objectCount);
this.indexToGroupMap.AddRange(new int[objectCount]);
for (int i = 0; i < groups.Count; i++) {
OLVGroup group = groups[i];
List<int> members = (List<int>)group.Contents;
foreach (int j in members)
this.indexToGroupMap[j] = i;
}
return groups;
}
private List<int> indexToGroupMap;
/// <summary>
///
/// </summary>
/// <param name="group"></param>
/// <param name="indexWithinGroup"></param>
/// <returns></returns>
public override int GetGroupMember(OLVGroup group, int indexWithinGroup) {
return (int)group.Contents[indexWithinGroup];
}
/// <summary>
///
/// </summary>
/// <param name="itemIndex"></param>
/// <returns></returns>
public override int GetGroup(int itemIndex) {
return this.indexToGroupMap[itemIndex];
}
/// <summary>
///
/// </summary>
/// <param name="group"></param>
/// <param name="itemIndex"></param>
/// <returns></returns>
public override int GetIndexWithinGroup(OLVGroup group, int itemIndex) {
return group.Contents.IndexOf(itemIndex);
}
}
/// <summary>
/// This is the COM interface that a ListView must be given in order for groups in virtual lists to work.
/// </summary>
/// <remarks>
/// This interface is NOT documented by MS. It was found on Greg Chapell's site. This means that there is
/// no guarantee that it will work on future versions of Windows, nor continue to work on current ones.
/// </remarks>
[ComImport(),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("44C09D56-8D3B-419D-A462-7B956B105B47")]
internal interface IOwnerDataCallback
{
/// <summary>
/// Not sure what this does
/// </summary>
/// <param name="i"></param>
/// <param name="pt"></param>
void GetItemPosition(int i, out NativeMethods.POINT pt);
/// <summary>
/// Not sure what this does
/// </summary>
/// <param name="t"></param>
/// <param name="pt"></param>
void SetItemPosition(int t, NativeMethods.POINT pt);
/// <summary>
/// Get the index of the item that occurs at the n'th position of the indicated group.
/// </summary>
/// <param name="groupIndex">Index of the group</param>
/// <param name="n">Index within the group</param>
/// <param name="itemIndex">Index of the item within the whole list</param>
void GetItemInGroup(int groupIndex, int n, out int itemIndex);
/// <summary>
/// Get the index of the group to which the given item belongs
/// </summary>
/// <param name="itemIndex">Index of the item within the whole list</param>
/// <param name="occurrenceCount">Which occurences of the item is wanted</param>
/// <param name="groupIndex">Index of the group</param>
void GetItemGroup(int itemIndex, int occurrenceCount, out int groupIndex);
/// <summary>
/// Get the number of groups that contain the given item
/// </summary>
/// <param name="itemIndex">Index of the item within the whole list</param>
/// <param name="occurrenceCount">How many groups does it occur within</param>
void GetItemGroupCount(int itemIndex, out int occurrenceCount);
/// <summary>
/// A hint to prepare any cache for the given range of requests
/// </summary>
/// <param name="i"></param>
/// <param name="j"></param>
void OnCacheHint(NativeMethods.LVITEMINDEX i, NativeMethods.LVITEMINDEX j);
}
/// <summary>
/// A default implementation of the IOwnerDataCallback interface
/// </summary>
[Guid("6FC61F50-80E8-49b4-B200-3F38D3865ABD")]
internal class OwnerDataCallbackImpl : IOwnerDataCallback
{
public OwnerDataCallbackImpl(VirtualObjectListView olv) {
this.olv = olv;
}
VirtualObjectListView olv;
#region IOwnerDataCallback Members
public void GetItemPosition(int i, out NativeMethods.POINT pt) {
//System.Diagnostics.Debug.WriteLine("GetItemPosition");
throw new NotSupportedException();
}
public void SetItemPosition(int t, NativeMethods.POINT pt) {
//System.Diagnostics.Debug.WriteLine("SetItemPosition");
throw new NotSupportedException();
}
public void GetItemInGroup(int groupIndex, int n, out int itemIndex) {
//System.Diagnostics.Debug.WriteLine(String.Format("-> GetItemInGroup({0}, {1})", groupIndex, n));
itemIndex = this.olv.GroupingStrategy.GetGroupMember(this.olv.OLVGroups[groupIndex], n);
//System.Diagnostics.Debug.WriteLine(String.Format("<- {0}", itemIndex));
}
public void GetItemGroup(int itemIndex, int occurrenceCount, out int groupIndex) {
//System.Diagnostics.Debug.WriteLine(String.Format("GetItemGroup({0}, {1})", itemIndex, occurrenceCount));
groupIndex = this.olv.GroupingStrategy.GetGroup(itemIndex);
//System.Diagnostics.Debug.WriteLine(String.Format("<- {0}", groupIndex));
}
public void GetItemGroupCount(int itemIndex, out int occurrenceCount) {
//System.Diagnostics.Debug.WriteLine(String.Format("GetItemGroupCount({0})", itemIndex));
occurrenceCount = 1;
}
public void OnCacheHint(NativeMethods.LVITEMINDEX from, NativeMethods.LVITEMINDEX to) {
//System.Diagnostics.Debug.WriteLine(String.Format("OnCacheHint({0}, {1}, {2}, {3})", from.iGroup, from.iItem, to.iGroup, to.iItem));
this.olv.GroupingStrategy.CacheHint(from.iGroup, from.iItem, to.iGroup, to.iItem);
}
#endregion
}
}