我希望能够在后台维护一个列表,使新项目加入到列表的末尾(以避免在更新时使用Insert()将项目推开),但是可以按相反的顺序显示它而不需要“排序”。
我只希望它按照与列表中相反的顺序显示在列表视图中。我是否可以使用模板或类似的东西来实现这个功能?
我只希望它按照与列表中相反的顺序显示在列表视图中。我是否可以使用模板或类似的东西来实现这个功能?
您可以将ListView的ItemsPanel更改为具有LastChildFill设置为false的DockPanel。然后在ItemContainerStyle中,将DockPanel.Dock属性设置为bottom。这将从底部开始填充并向上工作。我将ListView放在一个具有2行的网格中,第一行的Height =“ Auto”,第二行的Height =“ *”,它就像普通的ListView一样,但项目反转了。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}"
BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="DockPanel.Dock"
Value="Bottom" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel LastChildFill="False" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
更新
这里提供了一个附加行为,可以将任何ItemsControl
反转。使用方法如下:
<ListBox behaviors:ReverseItemsControlBehavior.ReverseItemsControl="True"
...>
ReverseItemsControlBehavior
public class ReverseItemsControlBehavior
{
public static DependencyProperty ReverseItemsControlProperty =
DependencyProperty.RegisterAttached("ReverseItemsControl",
typeof(bool),
typeof(ReverseItemsControlBehavior),
new FrameworkPropertyMetadata(false, OnReverseItemsControlChanged));
public static bool GetReverseItemsControl(DependencyObject obj)
{
return (bool)obj.GetValue(ReverseItemsControlProperty);
}
public static void SetReverseItemsControl(DependencyObject obj, object value)
{
obj.SetValue(ReverseItemsControlProperty, value);
}
private static void OnReverseItemsControlChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
ItemsControl itemsControl = sender as ItemsControl;
if (itemsControl.IsLoaded == true)
{
DoReverseItemsControl(itemsControl);
}
else
{
RoutedEventHandler loadedEventHandler = null;
loadedEventHandler = (object sender2, RoutedEventArgs e2) =>
{
itemsControl.Loaded -= loadedEventHandler;
DoReverseItemsControl(itemsControl);
};
itemsControl.Loaded += loadedEventHandler;
}
}
}
private static void DoReverseItemsControl(ItemsControl itemsControl)
{
Panel itemPanel = GetItemsPanel(itemsControl);
itemPanel.LayoutTransform = new ScaleTransform(1, -1);
Style itemContainerStyle;
if (itemsControl.ItemContainerStyle == null)
{
itemContainerStyle = new Style();
}
else
{
itemContainerStyle = CopyStyle(itemsControl.ItemContainerStyle);
}
Setter setter = new Setter();
setter.Property = ItemsControl.LayoutTransformProperty;
setter.Value = new ScaleTransform(1, -1);
itemContainerStyle.Setters.Add(setter);
itemsControl.ItemContainerStyle = itemContainerStyle;
}
private static Panel GetItemsPanel(ItemsControl itemsControl)
{
ItemsPresenter itemsPresenter = GetVisualChild<ItemsPresenter>(itemsControl);
if (itemsPresenter == null)
return null;
return GetVisualChild<Panel>(itemsControl);
}
private static Style CopyStyle(Style style)
{
Style styleCopy = new Style();
foreach (SetterBase currentSetter in style.Setters)
{
styleCopy.Setters.Add(currentSetter);
}
foreach (TriggerBase currentTrigger in style.Triggers)
{
styleCopy.Triggers.Add(currentTrigger);
}
return styleCopy;
}
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
}
否则,您可以按照以下链接中概述的步骤操作:WPF 反向 ListView
<ListBox ...>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VerticalAlignment="Top" Orientation="Vertical">
<VirtualizingStackPanel.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="-1" />
</VirtualizingStackPanel.LayoutTransform>
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform ScaleX="1" ScaleY="-1" />
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
假设ItemsSource是一个ObservableCollection,我的解决方案是实现一个ReverseObservableCollection:
public class ReverseObservableCollection<T> : IReadOnlyList<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
#region Private fields
private readonly ObservableCollection<T> _observableCollection;
#endregion Private fields
#region Constructor
public ReverseObservableCollection(ObservableCollection<T> observableCollection)
{
_observableCollection = observableCollection;
observableCollection.CollectionChanged += ObservableCollection_CollectionChanged;
}
#endregion
#region Event handlers
private void ObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (new[] { NotifyCollectionChangedAction.Add, NotifyCollectionChangedAction.Remove, NotifyCollectionChangedAction.Reset }.Contains(e.Action))
{
OnPropertyChanged(nameof(Count));
}
OnPropertyChanged(Binding.IndexerName); // ObservableCollection does this to improve WPF performance.
var newItems = Reverse(e.NewItems);
var oldItems = Reverse(e.OldItems);
int newStartingIndex = e.NewItems != null ? _observableCollection.Count - e.NewStartingIndex - e.NewItems.Count : -1;
//int oldCount = _observableCollection.Count - (e.NewItems?.Count ?? 0) + (e.OldItems?.Count ?? 0);
//int oldStartingIndex = e.OldItems != null ? oldCount - e.OldStartingIndex - e.OldItems.Count : -1;
int oldStartingIndex = e.OldItems != null ? _observableCollection.Count - e.OldStartingIndex - (e.NewItems?.Count ?? 0) : -1;
var eventArgs = e.Action switch
{
NotifyCollectionChangedAction.Add => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItems, newStartingIndex),
NotifyCollectionChangedAction.Remove => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, oldStartingIndex),
NotifyCollectionChangedAction.Replace => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItems, oldItems, oldStartingIndex),
NotifyCollectionChangedAction.Move => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, oldItems, newStartingIndex, oldStartingIndex),
NotifyCollectionChangedAction.Reset => new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset),
_ => throw new ArgumentException("Unexpected Action", nameof(e)),
};
OnCollectionChanged(eventArgs);
}
#endregion
#region IReadOnlyList<T> implementation
public T this[int index] => _observableCollection[_observableCollection.Count - index - 1];
public int Count => _observableCollection.Count;
public IEnumerator<T> GetEnumerator()
{
for (int i = _observableCollection.Count - 1; i >= 0; --i)
{
yield return _observableCollection[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region INotifyCollectionChanged implementation
public event NotifyCollectionChangedEventHandler? CollectionChanged;
private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
CollectionChanged?.Invoke(this, args);
}
#endregion
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string? propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Private methods
private IList? Reverse(IList? list)
{
if (list == null) return null;
object[] result = new object[list.Count];
for (int i = 0; i < list.Count; ++i)
{
result[i] = list[list.Count - i - 1];
}
return result;
}
#endregion
}
然后,您只需向ViewModel添加一个新属性并绑定它:
public class ViewModel
{
// Your old ItemsSource:
public ObservableCollection<string> Collection { get; } = new ObservableCollection<string>();
// New ItemsSource:
private ReverseObservableCollection<string>? _reverseCollection = null;
public ReverseObservableCollection<string> ReverseCollection => _reverseCollection ??= new ReverseObservableCollection<string>(Collection);
}
<ScrollViewer>
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
VerticalAlignment="Top"
ItemsSource="{Binding MyList, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Image DockPanel.Dock="Left"
Source="MyIcon.png"
Width="16" />
<Label DockPanel.Dock="Left"
Content="{Binding MyName, Mode=TwoWay}"/>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}"
BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="DockPanel.Dock"
Value="Bottom" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel LastChildFill="False" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</ScrollViewer>