【问题标题】:WPF: Is it possible to show one item on an ItemsControl more than once?WPF:是否可以在 ItemsControl 上多次显示一个项目?
【发布时间】:2018-01-13 17:09:13
【问题描述】:

我想创建一个控件,但我真的不知道如何用英文命名它,这就是为什么我也不知道用谷歌搜索什么。

所以,如果已经有它的名称和/或已经有一个具有以下功能的控件,请告诉我。

我的控制是某种ItemsControl,但有一个区别。如果它包含的项目太少而无法完全填满视口,则应重新从第一个项目开始,直到视口完全填满。

因此,如果视口有空间容纳五个项目,而我的 ItemsSource 仅包含两个项目,则控件应显示项目 1,然后是 2,然后是 1,然后是 2,然后是 1。

将其视为电视新闻频道底部新闻波段的 WPF 等效项。它在可用项目中循环,如果到达末尾,它会再次从第一个项目开始,中间没有间隙。

我已经尝试为此创建自己的面板,但我找不到在此面板中多次显示一个项目的方法。这甚至可能吗?

您将如何创建这样的控件?我可以基于默认的ItemsControl 还是必须从Control 继承并从头开始?

感谢您的想法和推动正确方向的努力。


编辑 1:

我尝试了以下方法以遵循答案 1 中的建议。

我创建了一个RecurringEnumerator(Of T)

Imports System.Collections.ObjectModel

Public Class RecurringEnumerator(Of T)
    Implements IEnumerator(Of T)

    Private _internal As ICollection(Of T)
    Private _currentIndex As Integer

    Public Sub New(ByVal internal As ICollection(Of T))
        If (internal Is Nothing) Then
            Throw New ArgumentNullException("internal")
        End If

        _internal = internal
        _currentIndex = -1
    End Sub

    Public ReadOnly Property Current As T Implements IEnumerator(Of T).Current
        Get
            Return _internal(_currentIndex)
        End Get
    End Property

    Public ReadOnly Property CurrentObject As Object Implements IEnumerator.Current
        Get
            Return _internal(_currentIndex)
        End Get
    End Property

    Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
        If (_internal.Count = 0) Then
            Return False
        End If

        _currentIndex += 1

        If (_currentIndex >= _internal.Count) Then
            _currentIndex -= _internal.Count
        End If

        Return True
    End Function

    Public Sub Reset() Implements IEnumerator.Reset
        _currentIndex = -1
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
    End Sub

End Class

然后我在RecurringCollection(Of T) 中使用了这个RecurringEnumerator(Of T): 公共类 RecurringCollection(Of T) 实现 ICollection(Of T)

    Private _enumerator As RecurringEnumerator(Of T)
    Private _internal As ObjectModel.Collection(Of T)

    Public Sub New()
        _enumerator = New RecurringEnumerator(Of T)(Me)
        _internal = New ObjectModel.Collection(Of T)
    End Sub

    Public Function GetEnumerator() As IEnumerator(Of T) Implements IEnumerable(Of T).GetEnumerator
        Return _enumerator
    End Function

    Public Function GetObjectEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return _enumerator
    End Function

    Public Sub Add(ByVal item As T) Implements ICollection(Of T).Add
        _internal.Add(item)
    End Sub

    Public Sub Clear() Implements ICollection(Of T).Clear
        _internal.Clear()
    End Sub

    Public Function Contains(ByVal item As T) As Boolean Implements ICollection(Of T).Contains
        Return _internal.Contains(item)
    End Function

    Public Sub CopyTo(ByVal array() As T, ByVal arrayIndex As Integer) Implements ICollection(Of T).CopyTo
        _internal.CopyTo(array, arrayIndex)
    End Sub

    Public ReadOnly Property Count As Integer Implements ICollection(Of T).Count
        Get
            Return _internal.Count
        End Get
    End Property

    Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of T).IsReadOnly
        Get
            Return False
        End Get
    End Property

    Public Function Remove(ByVal item As T) As Boolean Implements ICollection(Of T).Remove
        Return _internal.Remove(item)
    End Function

End Class

但是在这个RecurringCollection(Of T) 中放置三个短字符串并遍历集合并为每个字符串显示一个消息框会导致StackOverflowException 至少没有显示一个消息框。

我之前没有创建自定义集合,所以我不确定我是否做对了。

【问题讨论】:

    标签: wpf itemscontrol


    【解决方案1】:

    您可以创建一个专门的集合,而不是尝试操纵ItemsControl。我在下面写了一个模板。为简洁起见,我省略了错误检查和其他 ICollection<T> 方法。您可以轻松地将方法委托给内部列表。

    public class RecurringList<T> : ICollection<T>
    {
        private readonly List<T> _items = new List<T>();
    
        public int DesiredCount { get; set; }
        public int Count => _items.Count > 0 ? DesiredCount : 0;
    
        public bool IsReadOnly => false;
    
        public IEnumerator<T> GetEnumerator()
        {
            if (_items.Count == 0)
            {
                yield break;
            }
    
            int count = Math.Max(DesiredCount, _items.Count);
    
            for (int i = 0; i < count; i++)
            {
                yield return _items[i % _items.Count];
            }
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        public void Add(T item)
        {
            _items.Add(item);
        }
    
        public void Clear()
        {
            _items.Clear();
        }
    
        public bool Contains(T item)
        {
            return _items.Contains(item);
        }
    
        public void CopyTo(T[] array, int arrayIndex)
        {
            _items.CopyTo(array, arrayIndex);
        }
    
        public bool Remove(T item)
        {
            return _items.Remove(item);
        }
    }
    

    【讨论】:

    • 我不是反对者!我尝试了您的解决方案,但您的代码不起作用,例如您的函数 GetEnumerator 返回 T 类型的值,但应该返回 IEnumerator&lt;T&gt;。请参阅我对问题的编辑,了解我尝试过的内容。
    • 我试过这个解决方案,效果很好。 GetEnumerator 函数 yield 返回 T。这是 C# 的 IEnumerable 模式。可以看解释here
    • 那我用C#再试一次。
    • 您能否将此类的完整代码粘贴到某处,我不知道在Current 属性中返回什么或在MoveNext 函数中执行什么操作,因为您似乎这样做GetEnumerator 函数中的所有内容。
    • 我已经添加了完整的代码。您不需要创建 Enumerator 类。您应该能够按原样使用此代码。转换成VB应该是一个简单的语法转换。
    猜你喜欢
    • 2011-10-08
    • 1970-01-01
    • 1970-01-01
    • 2016-01-03
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多