CPF/CPF/Controls/VirtualizationPresenter.cs
2023-11-21 23:05:03 +08:00

1417 lines
56 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;
using CPF.Drawing;
using System.Linq;
using System.Runtime;
using System.Threading;
using System.ComponentModel;
using System.Diagnostics;
namespace CPF.Controls
{
/// <summary>
/// 虚拟化容器,采用第一个元素来计算元素尺寸,自定义模板最好用固定尺寸的
/// </summary>
/// <typeparam name="T"></typeparam>
[Description("虚拟化容器")]
public class VirtualizationPresenter<T> : Decorator, IScrollInfo where T : UIElement, ISelectableItem
{
[NotCpfProperty]
internal protected IList Items { get; set; }
public MultiSelector<T> MultiSelector { get { return GetValue<MultiSelector<T>>(); } set { SetValue(value); } }
public event EventHandler ScrollChanged
{
add { AddHandler(value); }
remove { RemoveHandler(value); }
}
StackPanel stackPanel;
float widthOrHeight = -1;
int count;
int scrollValue = 0;
bool end = false;
int startId = 0;
int _count;
bool collectChanged;
List<(object obj, float size)> customData;
public void OnCollectionSorted()
{
if (CustomScrollData != null && CustomScrollData.Custom != null)
{
List<(int index, float size)> list = new List<(int index, float size)>();
foreach (var item in customData)
{
var index = -1;
for (int i = 0; i < Items.Count; i++)
{
if (Items[i] == item.obj)
{
index = i;
break;
}
}
if (index > -1)
{
list.Add((index, item.size));
}
}
CustomScrollData.Custom = list.OrderBy(a => a.index);
}
Scroll(true);
}
public void OnCollectionStartSort()
{
if (CustomScrollData != null && CustomScrollData.Custom != null)
{
if (customData == null)
{
customData = new List<(object obj, float size)>();
}
customData.Clear();
foreach (var item in CustomScrollData.Custom.OrderBy(a => a.index))
{
var _item = Items[item.index];
customData.Add((_item, item.size));
}
}
}
public void OnDataCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (stackPanel)
{
if (stackPanel.Children.Count == 0 && Items.Count > 0)
{
var item = multiSelector.CreateItemElement();
item.DataContext = Items[0];
if (item is ContentControl c)
{
var diss = multiSelector.DisplayMemberPath;
if (!string.IsNullOrWhiteSpace(diss))
{
c.Content = Items[0].GetPropretyValue(diss);
}
else
{
c.Content = Items[0];
}
}
item.Index = 0;
stackPanel.Children.Add(item);
end = false;
}
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
//if (e.NewStartingIndex >= startId && e.NewStartingIndex <= startId + count)
//{
if (Items is ItemCollection collection && collection.comparison != null)
{
multiSelector.SelectedIndex = -1;
}
else
{
if (e.NewStartingIndex >= 0 && multiSelector.SelectedIndexs.Count > 0)
{
for (int j = 0; j < multiSelector.SelectedIndexs.Count; j++)
{
if (multiSelector.SelectedIndexs[j] >= e.NewStartingIndex)
{
multiSelector.SelectedIndexs[j]++;
}
}
}
}
if (!collectChanged)
{
collectChanged = true;
BeginInvoke(() =>
{
collectChanged = false;
oldCount = 0;
Scroll();
});
}
//}
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Remove:
//if (e.OldStartingIndex >= startId && e.OldStartingIndex <= startId + count)
//{
if (multiSelector.SelectedIndex == e.OldStartingIndex)
{
multiSelector.SelectedIndex = -1;
}
if (!collectChanged)
{
collectChanged = true;
BeginInvoke(() =>
{
collectChanged = false;
oldCount = 0;
Scroll();
});
}
//}
break;
case NotifyCollectionChangedAction.Replace:
var i = e.NewStartingIndex - startId;
if (e.NewStartingIndex >= startId && e.NewStartingIndex <= startId + count && stackPanel.Children.Count > i)
{
var s = e.NewStartingIndex;
T item = null;
if (multiSelector.HasProperty(nameof(ListBox.VirtualizationMode)) && multiSelector.GetValue<VirtualizationMode>(nameof(ListBox.VirtualizationMode)) == VirtualizationMode.Recycling)
{
item = stackPanel.Children[i] as T;
}
else
{
stackPanel.Children.RemoveAt(i);
item = multiSelector.CreateItemElement();
stackPanel.Children.Insert(i, item);
}
item.DataContext = Items[s];
if (item is ContentControl c)
{
var diss = multiSelector.DisplayMemberPath;
if (!string.IsNullOrWhiteSpace(diss))
{
c.Content = Items[s].GetPropretyValue(diss);
}
else
{
c.Content = Items[s];
}
}
var ac = multiSelector.AlternationCount;
if (ac != 0)
{
multiSelector.SetAlternationIndex((CpfObject)item, (int)(s % ac));
}
else
{
multiSelector.SetAlternationIndex((CpfObject)item, s);
}
item.Index = s;
item.IsSetOnOwner = true;
item.IsSelected = multiSelector.SelectedIndexs.Any(a => a == s);
item.IsSetOnOwner = false;
OnSetItem(item);
}
break;
case NotifyCollectionChangedAction.Reset:
ComputeItems();
break;
}
}
}
protected override void InitializeComponent()
{
Children.Add(new Panel
{
Name = "contentPresenter",
//Height = "100%",
//Width = "100%",
MarginTop = 0,
MarginLeft = 0,
PresenterFor = this
});
}
private void StackPanel_LayoutUpdated(object sender, RoutedEventArgs e)
{
ComputeItems();
}
[PropertyChanged(nameof(MultiSelector))]
void RegisterMultiSelector(object newValue, object oldValue, PropertyMetadataAttribute attribute)
{
multiSelector = newValue as MultiSelector<T>;
if (newValue != null)
{
multiSelector.PropertyChanged += MultiSelector_PropertyChanged;
multiSelector.SelectedIndexs.CollectionChanged += SelectedIndexs_CollectionChanged;
}
if (oldValue != null)
{
(oldValue as MultiSelector<T>).PropertyChanged -= MultiSelector_PropertyChanged;
(oldValue as MultiSelector<T>).SelectedIndexs.CollectionChanged -= SelectedIndexs_CollectionChanged;
}
}
[PropertyChanged(nameof(Child))]
void RegisterChild(object newValue, object oldValue, PropertyMetadataAttribute attribute)
{
if (stackPanel != null)
{
stackPanel.LayoutUpdated -= StackPanel_LayoutUpdated;
}
stackPanel = newValue as StackPanel;
if (stackPanel)
{
stackPanel.LayoutUpdated += StackPanel_LayoutUpdated;
}
if (newValue != null && stackPanel == null)
{
throw new Exception("虚拟模式必须是StackPanel");
}
}
//protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue, PropertyMetadataAttribute propertyMetadata)
//{
// base.OnPropertyChanged(propertyName, oldValue, newValue, propertyMetadata);
// if (propertyName == nameof(MultiSelector))
// {
// multiSelector = newValue as MultiSelector<T>;
// if (newValue != null)
// {
// multiSelector.PropertyChanged += MultiSelector_PropertyChanged;
// multiSelector.SelectedIndexs.CollectionChanged += SelectedIndexs_CollectionChanged;
// }
// if (oldValue != null)
// {
// (oldValue as MultiSelector<T>).PropertyChanged -= MultiSelector_PropertyChanged;
// (oldValue as MultiSelector<T>).SelectedIndexs.CollectionChanged -= SelectedIndexs_CollectionChanged;
// }
// }
// else if (propertyName == nameof(Child))
// {
// if (stackPanel != null)
// {
// stackPanel.LayoutUpdated -= StackPanel_LayoutUpdated;
// }
// stackPanel = newValue as StackPanel;
// if (stackPanel)
// {
// stackPanel.LayoutUpdated += StackPanel_LayoutUpdated;
// }
// }
//}
MultiSelector<T> multiSelector;
private void SelectedIndexs_CollectionChanged(object sender, CollectionChangedEventArgs<int> e)
{
switch (e.Action)
{
case CollectionChangedAction.Add:
if (e.NewItem >= startId && e.NewItem <= startId + count)
{
var i = multiSelector.ItemsHost.Children.Select(a => a as ISelectableItem).FirstOrDefault(a => a.Index == e.NewItem);
if (i != null)
{
i.IsSetOnOwner = true;
i.IsSelected = true;
i.IsSetOnOwner = false;
}
}
break;
case CollectionChangedAction.Remove:
if (e.OldItem >= startId && e.OldItem <= startId + count)
{
var i = multiSelector.ItemsHost.Children.Select(a => a as ISelectableItem).FirstOrDefault(a => a.Index == e.OldItem);
if (i != null)
{
i.IsSetOnOwner = true;
i.IsSelected = false;
i.IsSetOnOwner = false;
}
}
break;
case CollectionChangedAction.Replace:
if (e.NewItem >= startId && e.NewItem <= startId + count)
{
var i = multiSelector.ItemsHost.Children.Select(a => a as ISelectableItem).FirstOrDefault(a => a.Index == e.NewItem);
if (i != null)
{
i.IsSetOnOwner = true;
i.IsSelected = true;
i.IsSetOnOwner = false;
}
}
if (e.OldItem >= startId && e.OldItem <= startId + count)
{
var i = multiSelector.ItemsHost.Children.Select(a => a as ISelectableItem).FirstOrDefault(a => a.Index == e.OldItem);
if (i != null)
{
i.IsSetOnOwner = true;
i.IsSelected = false;
i.IsSetOnOwner = false;
}
}
break;
default:
break;
}
}
private void MultiSelector_PropertyChanged(object sender, CPFPropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ItemsControl<T>.ItemTemplate))
{
widthOrHeight = -1;
if (Items != null && Items.Count > 0)
{
stackPanel.Children.DisposeChildren();
var item = multiSelector.CreateItemElement();
item.DataContext = Items[0];
//item.Content = Items[0];
if (item is ContentControl c)
{
var diss = multiSelector.DisplayMemberPath;
if (!string.IsNullOrWhiteSpace(diss))
{
c.Content = Items[0].GetPropretyValue(diss);
}
else
{
c.Content = Items[0];
}
}
item.Index = 0;
stackPanel.Children.Add(item);
}
}
}
protected override void OnLayoutUpdated()
{
base.OnLayoutUpdated();
BeginInvoke(ComputeItems);
}
bool scrollChanged = true;
//protected override void InitializeComponent()
//{
// base.InitializeComponent();
//}
private void ComputeItems()
{
if (stackPanel && Items != null)
{
bool changed = false;
var size = ActualSize;
if (CustomScrollData == null)
{
if (stackPanel.Children.Count > 0 && size.Width > 0 && size.Height > 0)
{
//end = false;
var s = stackPanel.Children[0].ActualSize;
var w = s.Height;
int _count = 0;
if (stackPanel.Orientation == Orientation.Horizontal)
{
w = s.Width;
if (widthOrHeight != -1)
{
w = widthOrHeight;
}
if (w == 0)
{
return;
}
_count = (int)Math.Ceiling(size.Width / w);
ExtentWidth = Items.Count * w;
if (stackPanel.Children.Count > 0)
{
var ww = 0f;
for (int i = 0; i < Math.Min(stackPanel.Children.Count, _count); i++)
{
ww += stackPanel.Children[i].ActualSize.Width;
}
ExtentWidth = Math.Max(ExtentWidth, ww);
}
//ExtentHeight = stackPanel.ActualSize.Height;
var extentHeight = stackPanel.ActualSize.Height;
if (extentHeight != ExtentHeight)
{
ExtentHeight = extentHeight;
changed = true;
}
}
else
{
if (widthOrHeight != -1)
{
w = widthOrHeight;
}
if (w == 0)
{
return;
}
_count = (int)Math.Ceiling(size.Height / w);
ExtentHeight = Items.Count * w;
if (stackPanel.Children.Count > 0)
{
var h = 0f;
for (int i = 0; i < Math.Min(stackPanel.Children.Count, _count); i++)
{
h += stackPanel.Children[i].ActualSize.Height;
}
ExtentHeight = Math.Max(ExtentHeight, h);
}
//ExtentWidth = stackPanel.ActualSize.Width;
var extentWidth = stackPanel.ActualSize.Width;
if (extentWidth != ExtentWidth)
{
ExtentWidth = extentWidth;
changed = true;
}
}
_count = Math.Min(_count, Items.Count);
if (widthOrHeight != w || this.count != _count)
{
widthOrHeight = w;
this.count = _count;
Scroll();
}
}
}
else
{
int _count = 0;
var r = CustomScrollData;
var w = r.DefaultSize;
if (w <= 0)
{
throw new Exception("自定义虚拟滚动的默认尺寸必须大于0");
}
if (r.Custom != null)
{
var cc = r.Custom.Count();
if (stackPanel.Orientation == Orientation.Horizontal)
{
_count = (int)Math.Ceiling(size.Width / w);
ExtentWidth = r.DefaultSize * (Items.Count - cc) + r.Custom.Sum(a => a.size);
var extentHeight = stackPanel.ActualSize.Height;
if (extentHeight != ExtentHeight)
{
ExtentHeight = extentHeight;
changed = true;
}
}
else
{
_count = (int)Math.Ceiling(size.Height / w);
ExtentHeight = r.DefaultSize * (Items.Count - cc) + r.Custom.Sum(a => a.size);
var extentWidth = stackPanel.ActualSize.Width;
if (extentWidth != ExtentWidth)
{
ExtentWidth = extentWidth;
changed = true;
}
}
}
else
{
if (stackPanel.Orientation == Orientation.Horizontal)
{
_count = (int)Math.Ceiling(size.Width / w);
ExtentWidth = r.DefaultSize * Items.Count;
var extentHeight = stackPanel.ActualSize.Height;
if (extentHeight != ExtentHeight)
{
ExtentHeight = extentHeight;
changed = true;
}
}
else
{
_count = (int)Math.Ceiling(size.Height / w);
ExtentHeight = r.DefaultSize * Items.Count;
var extentWidth = stackPanel.ActualSize.Width;
if (extentWidth != ExtentWidth)
{
ExtentWidth = extentWidth;
changed = true;
}
}
}
_count = Math.Min(_count + 1, Items.Count);
if (widthOrHeight != w || this.count != _count)
{
widthOrHeight = w;
this.count = _count;
Scroll();
}
}
if (Items == null || Items.Count == 0)
{
ExtentHeight = 0;
ExtentWidth = 0;
}
if (ViewportHeight != size.Height)
{
ViewportHeight = size.Height;
changed = true;
}
if (ViewportWidth != size.Width)
{
ViewportWidth = size.Width;
changed = true;
}
if (CanVerticallyScroll != (ExtentHeight - ViewportHeight) > 1)
{
CanVerticallyScroll = (ExtentHeight - ViewportHeight) > 1;
changed = true;
}
if (CanHorizontallyScroll != (ExtentWidth - ViewportWidth) > 1)
{
CanHorizontallyScroll = (ExtentWidth - ViewportWidth) > 1;
changed = true;
}
if (changed)
{
if (scrollChanged)
{
scrollChanged = false;
this.BeginInvoke(() =>
{
RaiseEvent(EventArgs.Empty, nameof(ScrollChanged));
scrollChanged = true;
});
}
}
}
}
protected override void OnOverrideMetadata(OverrideMetadata overridePropertys)
{
base.OnOverrideMetadata(overridePropertys);
overridePropertys.Override(nameof(Width), new UIPropertyMetadataAttribute((FloatField)"100%", UIPropertyOptions.AffectsMeasure));
overridePropertys.Override(nameof(Height), new UIPropertyMetadataAttribute((FloatField)"100%", UIPropertyOptions.AffectsMeasure));
}
protected override Size ArrangeOverride(in Size finalSize)
{
var rect = new Rect(0, 0, finalSize.Width, finalSize.Height);
foreach (UIElement child in Children)
{
if (stackPanel.Orientation == Orientation.Vertical)
{
if (end && CustomScrollData == null)
{
rect.Location = new Point(-HorizontalOffset, rect.Height - child.DesiredSize.Height);
//rect.Size = child.DesiredSize;
child.Arrange(rect);
}
else
{
//rect.Size = child.DesiredSize;
rect.X = -HorizontalOffset;
rect.Y = offset;
child.Arrange(rect);
}
}
else
{
if (end && CustomScrollData == null)
{
rect.Location = new Point(rect.Width - child.DesiredSize.Width, -VerticalOffset);
child.Arrange(rect);
}
else
{
rect.Y = -VerticalOffset;
rect.X = offset;
child.Arrange(rect);
}
}
}
return finalSize;
}
protected override Size MeasureOverride(in Size availableSize)
{
var size = base.MeasureOverride(availableSize);
var items = Items;
if (stackPanel && stackPanel.Children.Count > 0 && items != null && items.Count > 0)
{
var s = stackPanel.Children[0].ActualSize;
if (stackPanel.Orientation == Orientation.Horizontal)
{
size = new Size(Math.Max(s.Width * items.Count, size.Width), Math.Max(size.Height, s.Height));
}
else
{
size = new Size(Math.Max(s.Width, size.Width), Math.Max(size.Height, s.Height * items.Count));
}
}
return size;
}
//protected override Size MeasureOverride(in Size availableSize)
//{
// var s = base.MeasureOverride(availableSize);
// return new Size(ExtentWidth, ExtentHeight);
//}
/// <summary>
/// 用来自定义虚拟模式,调整自定义模板里的尺寸,实现正常的虚拟化呈现。模板里要根据数据来修改尺寸,否则可能会对应不上。
/// </summary>
/// <returns>返回默认尺寸和自定义尺寸index数据里的索引不能有重复indexsize呈现尺寸。 自定义尺寸可以为null默认尺寸不能小于等于0没有在自定义尺寸里的数据使用默认尺寸</returns>
[NotCpfProperty]
public CustomScrollData CustomScrollData
{
get { return scrollData; }
set
{
if (scrollData != value)
{
if (scrollData != null)
{
scrollData.owner = null;
}
if (value.owner != null && value.owner != this)
{
throw new Exception("CustomScrollData不能共享");
}
value.owner = this;
scrollData = value;
InvalidateArrange();
}
}
}
CustomScrollData scrollData;
float offset;
int oldScrollValue = -1;
int oldCount = 0;
void Scroll(bool refresh = false)
{
if (stackPanel && widthOrHeight != -1)
{
if (CustomScrollData == null)
{
if (stackPanel.Orientation == Orientation.Horizontal)
{
if (ExtentWidth > ViewportWidth && widthOrHeight != 0)
{
scrollValue = (int)((widthOrHeight / 2 + HorizontalOffset) / widthOrHeight);
if (ViewportWidth + HorizontalOffset == ExtentWidth)
{
scrollValue += 1;
}
}
else
{
scrollValue = 0;
}
offset = 0;
}
else
{
if (ExtentHeight > ViewportHeight && widthOrHeight != 0)
{
scrollValue = (int)((widthOrHeight / 2 + VerticalOffset) / widthOrHeight);
if (ViewportHeight + VerticalOffset == ExtentHeight)
{
scrollValue += 1;
}
//scrollValue = (int)(VerticalOffset / widthOrHeight);
//offset = -(VerticalOffset - scrollValue * widthOrHeight);
}
else
{
scrollValue = 0;
}
offset = 0;
}
}
else
{
var r = CustomScrollData;
if (r.DefaultSize <= 0)
{
throw new Exception("自定义虚拟滚动的默认尺寸必须大于0");
}
if (r.DefaultSize != widthOrHeight)
{
throw new Exception("默认尺寸不能每次都不同");
}
if (r.Custom != null)
{
var list = r.Custom.OrderBy(a => a.index).ToArray();
if (list.Length > 0)
{
if (stackPanel.Orientation == Orientation.Horizontal)
{
scrollValue = (int)(HorizontalOffset / widthOrHeight);
if (list[0].index >= scrollValue)
{
offset = -(HorizontalOffset - scrollValue * widthOrHeight);
}
else
{
var len = list[0].index * widthOrHeight;
for (int i = 0; i < list.Length; i++)
{
len += list[i].size;
if (len > HorizontalOffset)
{
scrollValue = list[i].index;
offset = len - list[i].size - HorizontalOffset;
break;
}
else
{
var c = (int)((HorizontalOffset - len) / widthOrHeight) + list[i].index + 1;
if (i == list.Length - 1 || c < list[i + 1].index)
{
len += (int)((HorizontalOffset - len) / widthOrHeight) * widthOrHeight;
scrollValue = c;
offset = len - HorizontalOffset;
break;
}
else
{
len += widthOrHeight * (list[i + 1].index - list[i].index - 1);
}
}
}
}
}
else
{
scrollValue = (int)(VerticalOffset / widthOrHeight);
if (list[0].index >= scrollValue)
{
offset = -(VerticalOffset - scrollValue * widthOrHeight);
}
else
{
var len = list[0].index * widthOrHeight;
for (int i = 0; i < list.Length; i++)
{
len += list[i].size;
if (len > VerticalOffset)
{
scrollValue = list[i].index;
offset = len - list[i].size - VerticalOffset;
break;
}
else
{
var c = (int)((VerticalOffset - len) / widthOrHeight) + list[i].index + 1;
if (i == list.Length - 1 || c < list[i + 1].index)
{
len += (int)((VerticalOffset - len) / widthOrHeight) * widthOrHeight;
scrollValue = c;
offset = len - VerticalOffset;
break;
}
else
{
len += widthOrHeight * (list[i + 1].index - list[i].index - 1);
}
}
}
}
//Debug.WriteLine(scrollValue);
//Debug.WriteLine(offset);
}
}
}
else
{
if (stackPanel.Orientation == Orientation.Horizontal)
{
if (ExtentWidth > ViewportWidth && widthOrHeight != 0)
{
scrollValue = (int)(HorizontalOffset / widthOrHeight);
offset = -(HorizontalOffset - scrollValue * widthOrHeight);
}
else
{
scrollValue = 0;
offset = 0;
}
}
else
{
if (ExtentHeight > ViewportHeight && widthOrHeight != 0)
{
scrollValue = (int)(VerticalOffset / widthOrHeight);
offset = -(VerticalOffset - scrollValue * widthOrHeight);
}
else
{
scrollValue = 0;
offset = 0;
}
}
}
}
if (oldScrollValue != scrollValue || oldCount != count || refresh)
{
oldCount = count;
oldScrollValue = scrollValue;
var start = scrollValue;
if (CustomScrollData == null && start + count > Items.Count)
{
end = true;
start = Items.Count - count;
if (start <= 0)
{
start = 0;
//end = false;
}
}
else
{
end = false;
}
startId = start;
_count = 0;
var diss = multiSelector.DisplayMemberPath;
if (multiSelector.HasProperty(nameof(ListBox.VirtualizationMode)) && multiSelector.GetValue<VirtualizationMode>(nameof(ListBox.VirtualizationMode)) == VirtualizationMode.Recycling)
{
var ac = multiSelector.AlternationCount;
for (int i = 0; i < count; i++)
{
var s = start + i;
if (s < Items.Count)
{
_count++;
T item = null;
if (stackPanel.Children.Count > i)
{
item = stackPanel.Children[i] as T;
item.DataContext = Items[s];
}
else
{
item = multiSelector.CreateItemElement();
item.DataContext = Items[s];
stackPanel.Children.Add(item);
}
//item.Content = Items[s];
if (item is ContentControl c)
{
if (!string.IsNullOrWhiteSpace(diss))
{
c.Content = Items[s].GetPropretyValue(diss);
}
else
{
c.Content = Items[s];
}
}
if (ac != 0)
{
multiSelector.SetAlternationIndex((CpfObject)item, (int)(s % ac));
}
else
{
multiSelector.SetAlternationIndex((CpfObject)item, s);
}
item.Index = s;
item.IsSetOnOwner = true;
item.IsSelected = multiSelector.SelectedIndexs.Any(a => a == s);
item.IsSetOnOwner = false;
OnSetItem(item);
}
}
var cc = stackPanel.Children.Count;
if (_count > 0 || widthOrHeight > 0)
{
for (int i = _count; i < cc; i++)
{//多余的释放
var item = stackPanel.Children[_count];
if (item is ContentControl c)
{
c.Content = null;
}
item.DataContext = null;
item.Dispose();
}
}
}
else
{
//stackPanel.Children.DisposeChildren();
//for (int i = 0; i < count; i++)
//{
// if (start + i < Items.Count)
// {
// _count++;
// var item = MultiSelector.CreateItemElement();
// item.DataContext = Items[start + i];
// //item.Content = Items[start + i];
// if (item is ContentControl c)
// {
// if (!string.IsNullOrWhiteSpace(diss))
// {
// c.Content = Items[start + i].GetPropretyValue(diss);
// }
// else
// {
// c.Content = Items[start + i];
// }
// }
// item.Index = start + i;
// item.IsSelected = MultiSelector.SelectedIndexs.Any(a => a == start + i);
// OnSetItem(item);
// stackPanel.Children.Add(item);
// }
//}
//GCLatencyMode oldMode = GCSettings.LatencyMode;
//GCSettings.LatencyMode = GCLatencyMode.LowLatency;
List<T> list = new List<T>();
foreach (T item in stackPanel.Children)
{
var index = item.Index;
if (index < start || index > start + count - 1)
{
list.Add(item);
}
}
if (_count > 0 || widthOrHeight > 0)
{
for (int i = list.Count - 1; i >= 0; i--)
{
//list[i].Dispose();
var item = list[i];
if (item is ContentControl c)
{
c.Content = null;
}
item.DataContext = null;
item.Dispose();
}
}
var s = -1;
var e = -1;
if (stackPanel.Children.Count > 0)
{
s = (stackPanel.Children[0] as T).Index;
e = (stackPanel.Children[stackPanel.Children.Count - 1] as T).Index;
}
var ac = multiSelector.AlternationCount;
for (int i = 0; i < count; i++)
{
var index = start + i;
if ((index < s || index > e) && index < Items.Count)
{
var item = multiSelector.CreateItemElement();
item.DataContext = Items[index];
stackPanel.Children.Insert(i, item);
if (item is ContentControl c)
{
if (!string.IsNullOrWhiteSpace(diss))
{
c.Content = Items[index].GetPropretyValue(diss);
}
else
{
c.Content = Items[index];
}
}
if (ac != 0)
{
multiSelector.SetAlternationIndex((CpfObject)item, (int)(index % ac));
}
else
{
multiSelector.SetAlternationIndex((CpfObject)item, index);
}
item.Index = index;
item.IsSetOnOwner = true;
item.IsSelected = multiSelector.SelectedIndexs.Any(a => a == index);
item.IsSetOnOwner = false;
OnSetItem(item);
}
else
{
var item = stackPanel.Children[i] as T;
item.DataContext = Items[index];
if (item is ContentControl c)
{
if (!string.IsNullOrWhiteSpace(diss))
{
c.Content = Items[index].GetPropretyValue(diss);
}
else
{
c.Content = Items[index];
}
}
if (ac != 0)
{
multiSelector.SetAlternationIndex((CpfObject)item, (int)(index % ac));
}
else
{
multiSelector.SetAlternationIndex((CpfObject)item, index);
}
item.Index = index;
item.IsSetOnOwner = true;
item.IsSelected = multiSelector.SelectedIndexs.Any(a => a == index);
item.IsSetOnOwner = false;
OnSetItem(item);
}
}
//GC.Collect();
//GCSettings.LatencyMode = oldMode;
}
}
var size = ActualSize;
//bool changed = false;
//if (extentHeight != ExtentHeight)
//{
// ExtentHeight = extentHeight;
// changed = true;
//}
//if (extentWidth != ExtentWidth)
//{
// ExtentWidth = extentWidth;
// changed = true;
//}
if (ViewportHeight != size.Height)
{
ViewportHeight = size.Height;
//changed = true;
}
if (ViewportWidth != size.Width)
{
ViewportWidth = size.Width;
//changed = true;
}
if (CanVerticallyScroll != (ExtentHeight - ViewportHeight) > 1)
{
CanVerticallyScroll = (ExtentHeight - ViewportHeight) > 1;
//changed = true;
}
if (CanHorizontallyScroll != (ExtentWidth - ViewportWidth) > 1)
{
CanHorizontallyScroll = (ExtentWidth - ViewportWidth) > 1;
//changed = true;
}
InvalidateArrange();
RaiseEvent(EventArgs.Empty, nameof(ScrollChanged));
}
}
/// <summary>
/// 对元素设置数据之后
/// </summary>
/// <param name="item"></param>
protected virtual void OnSetItem(T item)
{
if (SetItem != null)
{
SetItem(this, item);
}
}
/// <summary>
/// 对元素设置数据之后
/// </summary>
public event EventHandler<T> SetItem;
//protected override void OnLayoutUpdated()
//{
// var size = ActualSize;
// bool changed = false;
// if (extentHeight != ExtentHeight)
// {
// ExtentHeight = extentHeight;
// changed = true;
// }
// if (extentWidth != ExtentWidth)
// {
// ExtentWidth = extentWidth;
// changed = true;
// }
// if (ViewportHeight != size.Height)
// {
// ViewportHeight = size.Height;
// changed = true;
// }
// if (ViewportWidth != size.Width)
// {
// ViewportWidth = size.Width;
// changed = true;
// }
// if (CanVerticallyScroll != ExtentHeight > ViewportHeight)
// {
// CanVerticallyScroll = ExtentHeight > ViewportHeight;
// changed = true;
// }
// if (CanHorizontallyScroll != ExtentWidth > ViewportWidth)
// {
// CanHorizontallyScroll = ExtentWidth > ViewportWidth;
// changed = true;
// }
// if (changed)
// {
// RaiseEvent(EventArgs.Empty, nameof(ScrollChanged));
// }
// base.OnLayoutUpdated();
//}
public void LineUp()
{
SetVerticalOffset(VerticalOffset - widthOrHeight);
}
public void LineDown()
{
SetVerticalOffset(VerticalOffset + widthOrHeight);
}
public void LineLeft()
{
SetHorizontalOffset(HorizontalOffset - widthOrHeight);
}
public void LineRight()
{
SetHorizontalOffset(HorizontalOffset + widthOrHeight);
}
public void PageUp()
{
SetVerticalOffset(VerticalOffset - ViewportHeight);
}
public void PageDown()
{
SetVerticalOffset(VerticalOffset + ViewportHeight);
}
public void PageLeft()
{
SetHorizontalOffset(HorizontalOffset - ViewportWidth);
}
public void PageRight()
{
SetHorizontalOffset(HorizontalOffset + ViewportWidth);
}
public void MouseWheelUp()
{
SetVerticalOffset(VerticalOffset + widthOrHeight * 2);
}
public void MouseWheelDown()
{
SetVerticalOffset(VerticalOffset - widthOrHeight * 2);
}
public void MouseWheelLeft()
{
SetHorizontalOffset(HorizontalOffset - widthOrHeight * 2);
}
public void MouseWheelRight()
{
SetHorizontalOffset(HorizontalOffset + widthOrHeight * 2);
}
CPF.Threading.DispatcherTimer timer;
void SetScroll()
{
if (timer == null && !IsDisposed && !IsDisposing)
{
timer = new Threading.DispatcherTimer { Interval = TimeSpan.FromMilliseconds(10), IsEnabled = true };
timer.Tick += delegate
{
Scroll();
timer.Dispose();
timer = null;
};
}
}
public void SetHorizontalOffset(float offset)
{
HorizontalOffset = offset;
if (HorizontalOffset < 0 || ExtentWidth < ViewportWidth)
{
HorizontalOffset = 0;
}
else if (HorizontalOffset > ExtentWidth - ViewportWidth)
{
HorizontalOffset = ExtentWidth - ViewportWidth;
}
SetScroll();
//Scroll();
//RaiseEvent(EventArgs.Empty, nameof(ScrollChanged));
if (stackPanel && stackPanel.Orientation == Orientation.Vertical)
{
InvalidateArrange();
}
}
public void SetVerticalOffset(float offset)
{
VerticalOffset = offset;
if (VerticalOffset < 0 || ExtentHeight < ViewportHeight)
{
VerticalOffset = 0;
}
else if (VerticalOffset > ExtentHeight - ViewportHeight)
{
VerticalOffset = ExtentHeight - ViewportHeight;
}
SetScroll();
//Scroll();
//RaiseEvent(EventArgs.Empty, nameof(ScrollChanged));
if (stackPanel && stackPanel.Orientation == Orientation.Horizontal)
{
InvalidateArrange();
}
}
protected override void Dispose(bool disposing)
{
if (timer != null)
{
timer.Dispose();
timer = null;
}
base.Dispose(disposing);
}
[NotCpfProperty]
public bool CanVerticallyScroll
{
get;
set;
}
[NotCpfProperty]
public bool CanHorizontallyScroll
{
get;
set;
}
[NotCpfProperty]
public float ExtentWidth
{
get; private set;
}
[NotCpfProperty]
public float ExtentHeight
{
get; private set;
}
[NotCpfProperty]
public float ViewportWidth
{
get; private set;
}
[NotCpfProperty]
public float ViewportHeight
{
get; private set;
}
[NotCpfProperty]
public float HorizontalOffset
{
get; private set;
}
[NotCpfProperty]
public float VerticalOffset
{
get;
private set;
}
[NotCpfProperty]
public ScrollViewer ScrollOwner
{
get;
set;
}
/// <summary>
/// 开始ID
/// </summary>
[NotCpfProperty]
public int StartId
{
get
{
return startId;
}
}
/// <summary>
/// 当前显示的数量
/// </summary>
public int Count
{
get
{
return _count;
}
}
}
/// <summary>
/// 用来自定义虚拟模式,调整自定义模板里的尺寸,实现正常的虚拟化呈现
/// </summary>
public class CustomScrollData
{
/// <summary>
/// 用来自定义虚拟模式调整自定义模板里的尺寸实现正常的虚拟化呈现。模板里要根据数据来修改尺寸否则可能会对应不上。返回默认尺寸和自定义尺寸index数据里的索引不能有重复indexsize呈现尺寸必须大于默认值。 自定义尺寸可以为null默认尺寸不能小于等于0没有在自定义尺寸里的数据使用默认尺寸
/// </summary>
public CustomScrollData()
{ }
float defaultSize;
internal UIElement owner;
IEnumerable<(int index, float size)> custom;
/// <summary>
/// 默认尺寸必须大于0
/// </summary>
public float DefaultSize
{
get => defaultSize;
set
{
defaultSize = value;
if (value <= 0)
{
throw new Exception("默认尺寸必须大于0");
}
if (owner != null)
{
owner.InvalidateArrange();
}
}
}
/// <summary>
/// index数据里的索引不能有重复indexsize呈现尺寸。 自定义尺寸可以为null没有在自定义尺寸里的数据使用默认尺寸
/// </summary>
public IEnumerable<(int index, float size)> Custom
{
get => custom;
set
{
custom = value;
if (owner != null)
{
owner.InvalidateArrange();
}
}
}
}
}