Wpf 自定义面板内部子项顺序与绑定项源不同
我正在编写一个自定义ItemsControl和面板,它按照项目添加到绑定到ItemsControl的ItemsSource的顺序垂直排列项目请注意,这只是最终面板的原型,其布局将更复杂一些。因此,我对备选小组建议不感兴趣。 ItemsControl涓流为绑定集合中的面板项提供数据,因此集合中的项不会全部“同时”显示(面板会引发一个事件,表示它已准备就绪,ItemsControl会捕获该事件以释放下一个项)。问题是,由于某些原因,面板上的排列重写有时会决定在已呈现的图像的中间添加项目,导致事物跳转。 目前,我只需单击测试视图上的Add按钮,将项目添加到绑定ItemsSource集合的末尾。因此,在进行涓流馈送时,可以从绑定集合中添加/删除项目。当面板渲染这些“新”项时,它们被添加到看似随机的位置Wpf 自定义面板内部子项顺序与绑定项源不同,wpf,panel,itemscontrol,itemssource,Wpf,Panel,Itemscontrol,Itemssource,我正在编写一个自定义ItemsControl和面板,它按照项目添加到绑定到ItemsControl的ItemsSource的顺序垂直排列项目请注意,这只是最终面板的原型,其布局将更复杂一些。因此,我对备选小组建议不感兴趣。 ItemsControl涓流为绑定集合中的面板项提供数据,因此集合中的项不会全部“同时”显示(面板会引发一个事件,表示它已准备就绪,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);
}
}
当它运行时,我点击“添加”按钮几次,然后点击“踢”按钮几次,以此类推。正如我之前所说的,这些收藏品正以正确的顺序源源不断地提供给人们。但是,在为什么面板上的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);
}
}