Wpf 自定义面板内部子项顺序与绑定项源不同

Wpf 自定义面板内部子项顺序与绑定项源不同,wpf,panel,itemscontrol,itemssource,Wpf,Panel,Itemscontrol,Itemssource,我正在编写一个自定义ItemsControl和面板,它按照项目添加到绑定到ItemsControl的ItemsSource的顺序垂直排列项目请注意,这只是最终面板的原型,其布局将更复杂一些。因此,我对备选小组建议不感兴趣。 ItemsControl涓流为绑定集合中的面板项提供数据,因此集合中的项不会全部“同时”显示(面板会引发一个事件,表示它已准备就绪,ItemsControl会捕获该事件以释放下一个项)。问题是,由于某些原因,面板上的排列重写有时会决定在已呈现的图像的中间添加项目,导致事物跳转

我正在编写一个自定义ItemsControl和面板,它按照项目添加到绑定到ItemsControl的ItemsSource的顺序垂直排列项目请注意,这只是最终面板的原型,其布局将更复杂一些。因此,我对备选小组建议不感兴趣。

ItemsControl涓流为绑定集合中的面板项提供数据,因此集合中的项不会全部“同时”显示(面板会引发一个事件,表示它已准备就绪,ItemsControl会捕获该事件以释放下一个项)。问题是,由于某些原因,面板上的排列重写有时会决定在已呈现的图像的中间添加项目,导致事物跳转。

目前,我只需单击测试视图上的Add按钮,将项目添加到绑定ItemsSource集合的末尾。因此,在进行涓流馈送时,可以从绑定集合中添加/删除项目。当面板渲染这些“新”项时,它们被添加到看似随机的位置

我得到了 Trace.在代码中写“< /代码> s”,这样我就可以看到项目被成功地添加到集合的末尾,并确认内部的孩子正在随机插入中间。我甚至实现了一个CollectionViewSource来强制执行项目的顺序。即使在那时,内部子项也为基础itemsource提供了不同的顺序

我能想到的唯一一件事是,在滴流馈送过程中不知何故添加项目会导致某种竞争状况,但这一切都在UI线程上,我仍然无法理解为什么ItemsControl上的顺序正确,而面板上的顺序不正确

如何将面板上内部子项的顺序与绑定项控件同步,以便以正确的顺序显示视觉效果

更新

根据要求,这里有一些代码。在完整的解决方案中有很多,所以我将尝试只在这里发布相关的部分。因此,这段代码不会运行,但它应该会给您一个想法。我已经删除了所有的
Trace.WriteLine
代码,还有很多我认为对解决当前问题不重要的额外代码

我有一个
交错删除集合
,它扩展了
可观察集合
。添加到集合中的项目保存在单独的“HeldItems”集合中,直到它们准备好通过“Kick”方法移动到继承的“Items”集合中(在
IFlushableCollection
上)

VerticalStackFlushPanel
所基于的抽象
FlushPanel
处理第1阶段动画事件的引发。出于某种原因,OnVisualChildrenChanged不会在Kick()方法期间激发
StaggedReleaseCollection
,除非我自己明确地引发OnCollectionChanged事件(可能是红旗?)

}

Generic.xaml
文件将标准模板放在一起

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:si="clr-namespace:AnimatedQueueTest2010.StaggeredItemControlTest.Controls"
>
    <!--StaggeredReleaseItemControl Style-->
    <Style TargetType="{x:Type si:StaggeredReleaseItemsControl}" BasedOn="{StaticResource {x:Type ItemsControl}}">
        <Setter Property="FontSize" Value="20" />
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <si:VerticalStackFlushPanel/>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

我的测试视图相当简单

<Window 
    x:Class="AnimatedQueueTest2010.StaggeredItemControlTest.Views.StaggeredItemControlTestView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:AnimatedQueueTest2010.StaggeredItemControlTest.Controls"
    xmlns:cm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    Title="StaggeredItemControlTestView" 
    Width="640" Height="480" 
>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <local:StaggeredReleaseItemsControl x:Name="ic" ItemsSource="{Binding ViewModel.Names}" />

        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <StackPanel.Resources>
                <Style TargetType="Button">
                    <Setter Property="MinWidth" Value="80"/>
                    <Setter Property="MinHeight" Value="20"/>
                </Style>
            </StackPanel.Resources>
            <Button x:Name="btnKick" Content="Kick" Click="btnKick_Click"/>
            <Button x:Name="btnAdd" Content="Add" Click="btnAdd_Click"/>
        </StackPanel>

    </Grid>

</Window>

My ViewModel定义初始状态

public class StaggeredItemControlTestViewModel : INotifyPropertyChanged
{
    public StaggeredReleaseCollection<string> Names { get; set; }

    public StaggeredItemControlTestViewModel()
    {
        Names = new StaggeredReleaseCollection<string>() { "Carl", "Chris", "Sam", "Erin" };
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
公共类StaggedEditemControlTestViewModel:INotifyPropertyChanged
{
公共交错删除集合名称{get;set;}
public EditemControlTestViewModel()
{
Names=新的交错删除集合(){“Carl”、“Chris”、“Sam”、“Erin”};
}
公共事件属性更改事件处理程序属性更改;
}
而背后的代码是让我与之互动的

public partial class StaggeredItemControlTestView : Window
{
    List<string> GenesisPeople = new List<string>() { "Rob", "Mike", "Cate", "Andrew", "Dave", "Janet", "Julie" };
    Random random = new Random((int)(DateTime.Now.Ticks % int.MaxValue));

    public StaggeredItemControlTestViewModel ViewModel { get; set; }

    public StaggeredItemControlTestView()
    {
        InitializeComponent();
        ViewModel = new StaggeredItemControlTestViewModel();
        DataContext = this;
    }

    private void btnKick_Click(object sender, RoutedEventArgs e)
    {
        ViewModel.Names.Kick();
    }

    private void btnAdd_Click(object sender, RoutedEventArgs e)
    {
        //Get a random name
        //NOTE: Use a new string here to ensure it's not reusing the same object pointer
        string nextName = new string(GenesisPeople[random.Next(GenesisPeople.Count)].ToCharArray());

        //Add to ViewModel
        ViewModel.Names.Add(nextName);
    }
}
public分部类StaggeredItemControlTestView:窗口
{
List GenesisPeople=新列表(){“Rob”、“Mike”、“Cate”、“Andrew”、“Dave”、“Janet”、“Julie”};
Random Random=新随机((int)(DateTime.Now.Ticks%int.MaxValue));
public EditItemControlTestViewModel视图模型{get;set;}
public EditemControlTestView()
{
初始化组件();
ViewModel=新的EditEmControlTestViewModel();
DataContext=this;
}
私有void b单击(对象发送方,路由目标)
{
ViewModel.Names.Kick();
}
私有无效btnAdd_单击(对象发送者,路由目标e)
{
//随机取一个名字
//注意:在这里使用一个新字符串以确保它不会重用同一个对象指针
string nextName=新字符串(GenesisPeople[random.Next(GenesisPeople.Count)].tocharray();
//添加到ViewModel
ViewModel.Names.Add(nextName);
}
}
当它运行时,我点击“添加”按钮几次,然后点击“踢”按钮几次,以此类推。正如我之前所说的,这些收藏品正以正确的顺序源源不断地提供给人们。但是,在 SuffeReIDE < /C>中,IntualCudio集合偶尔会将新添加的项报告在集合的中间,而不是结束。考虑到通常一次只添加一个项目,我无法理解为什么会出现这种情况


为什么面板上的InternalChildren显示的顺序与绑定的
StaggeredReleaseCollection
不同?

Eureka!多亏了克莱门斯的调查,我发现了这个问题

这个问题与我为什么必须自己提出
OnCollectionChanged
有关。在
StaggeredReleaseCollection
上,我在此集合上定义了一个新的Add()定义,以便将项添加到我的保留集合(而不是ObservableCollection上的基础项集合)。在Kick()期间,我使用了
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:si="clr-namespace:AnimatedQueueTest2010.StaggeredItemControlTest.Controls"
>
    <!--StaggeredReleaseItemControl Style-->
    <Style TargetType="{x:Type si:StaggeredReleaseItemsControl}" BasedOn="{StaticResource {x:Type ItemsControl}}">
        <Setter Property="FontSize" Value="20" />
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <si:VerticalStackFlushPanel/>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
<Window 
    x:Class="AnimatedQueueTest2010.StaggeredItemControlTest.Views.StaggeredItemControlTestView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:AnimatedQueueTest2010.StaggeredItemControlTest.Controls"
    xmlns:cm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    Title="StaggeredItemControlTestView" 
    Width="640" Height="480" 
>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <local:StaggeredReleaseItemsControl x:Name="ic" ItemsSource="{Binding ViewModel.Names}" />

        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <StackPanel.Resources>
                <Style TargetType="Button">
                    <Setter Property="MinWidth" Value="80"/>
                    <Setter Property="MinHeight" Value="20"/>
                </Style>
            </StackPanel.Resources>
            <Button x:Name="btnKick" Content="Kick" Click="btnKick_Click"/>
            <Button x:Name="btnAdd" Content="Add" Click="btnAdd_Click"/>
        </StackPanel>

    </Grid>

</Window>
public class StaggeredItemControlTestViewModel : INotifyPropertyChanged
{
    public StaggeredReleaseCollection<string> Names { get; set; }

    public StaggeredItemControlTestViewModel()
    {
        Names = new StaggeredReleaseCollection<string>() { "Carl", "Chris", "Sam", "Erin" };
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
public partial class StaggeredItemControlTestView : Window
{
    List<string> GenesisPeople = new List<string>() { "Rob", "Mike", "Cate", "Andrew", "Dave", "Janet", "Julie" };
    Random random = new Random((int)(DateTime.Now.Ticks % int.MaxValue));

    public StaggeredItemControlTestViewModel ViewModel { get; set; }

    public StaggeredItemControlTestView()
    {
        InitializeComponent();
        ViewModel = new StaggeredItemControlTestViewModel();
        DataContext = this;
    }

    private void btnKick_Click(object sender, RoutedEventArgs e)
    {
        ViewModel.Names.Kick();
    }

    private void btnAdd_Click(object sender, RoutedEventArgs e)
    {
        //Get a random name
        //NOTE: Use a new string here to ensure it's not reusing the same object pointer
        string nextName = new string(GenesisPeople[random.Next(GenesisPeople.Count)].ToCharArray());

        //Add to ViewModel
        ViewModel.Names.Add(nextName);
    }
}