如何优化Surface ListBox项目的样式?
我在 SurfaceListBox 中仅包含 15 个项目时遇到性能问题。
ListBox 是通过源绑定到我的视图模型创建的,我使用 ItemContainerStyleSelector 来选择要使用的三种样式之一。 然而,我仍然有 8 个 ControlTemplate,因为我有 4 个数据项可以呈现的状态,加上选择该项目时的 4 个状态。
StyleSelector 非常简单:
public class TaskStyleSelector : StyleSelector
{
public Style DefaultStyle { get; set; }
public Style OverdueStyle { get; set; }
public Style PresentStyle { get; set; }
public Style CompletedStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
var t = item as Sekoia.Karuna.Public.Models.Tasks.Task;
if (t == null)
return base.SelectStyle(item, container);
if (t.Completed)
return CompletedStyle;
else if (t.IntervalStartTime <= DateTime.Now && t.IntervalEndTime >= DateTime.Now)
return PresentStyle;
else if (t.IntervalEndTime < DateTime.Now)
return OverdueStyle;
return DefaultStyle;
}
}
我的 ControlTemplates 看起来像这样:
<ControlTemplate x:Key="defaultTaskTemplate">
<Border BorderThickness="0, 0, 0, 1" Height="56" BorderBrush="{StaticResource SideBarShadowBrush}" Background="Transparent">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="56"/>
<ColumnDefinition Width="130"/>
<ColumnDefinition Width="214"/>
<ColumnDefinition Width="56"/>
<ColumnDefinition Width="56"/>
</Grid.ColumnDefinitions>
<TextBlock Padding="13,16,0,0" Grid.Column="1" Foreground="{StaticResource TextGreyBrush}"><Run Text="{Binding IntervalStartTime, StringFormat=\{0:hh:mm\}}"/><Run Text=" - "/><Run Text="{Binding IntervalEndTime, StringFormat=\{0:hh:mm\}}"/></TextBlock>
<TextBlock Padding="13,16,0,0" Grid.Column="2" Foreground="{StaticResource TextGreyBrush}" Text="{Binding Title}"/>
<Views:ArrowControl HorizontalAlignment="Center" Width="20" Grid.Column="4" Height="13" VerticalAlignment="Center" ArrowBrushColor="{StaticResource TextGrey}"/>
<Views:CheckMarkControl Width="30" Height="30" />
</Grid>
</Border>
</ControlTemplate>
对于我来说,这看起来非常简单。 然而我的样式看起来像这样:
<Style TargetType="{x:Type ListBoxItem}" x:Key="normalTaskItemStyle" BasedOn="{StaticResource baseTaskStyle}">
<Setter Property="Template" Value="{StaticResource defaultTaskTemplate}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Template" Value="{StaticResource defaultSelectedTaskTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
如您所见,当选择更改时我正在更改模板。
我面临的是当实际绑定到源时,窗口挂起,等待列表框呈现项目。当窗口打开时,我会显示一个“旋转器”元素,以指示我正在下载内容。下载完成后,我进行数据绑定,并看到微调器在被删除之前停止了。旋转器停止向我表明,窗口线程挂起,因为绑定正在进行中。初始绑定结束后,我可以很好地滚动/平移,并且没有明显的性能影响。
我在网上搜索了一些有关优化此场景的信息,但似乎找不到任何相关内容。 我是不是找错地方了?
我还想知道,这是否是一种可以接受的生成列表的方式?有没有更简单的方法来做到这一点?即避免大量的控件模板和样式?
有人可以帮忙吗?
I'm having trouble with performance with just 15 items in a SurfaceListBox.
The ListBox is created with source binding to my viewmodel and I'm using a ItemContainerStyleSelector, to select one of the three styles to use.
However I still have 8 ControlTemplates, since I have 4 states a dataitem can present, plus the 4, when the item is selected.
The StyleSelector is pretty simple:
public class TaskStyleSelector : StyleSelector
{
public Style DefaultStyle { get; set; }
public Style OverdueStyle { get; set; }
public Style PresentStyle { get; set; }
public Style CompletedStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
var t = item as Sekoia.Karuna.Public.Models.Tasks.Task;
if (t == null)
return base.SelectStyle(item, container);
if (t.Completed)
return CompletedStyle;
else if (t.IntervalStartTime <= DateTime.Now && t.IntervalEndTime >= DateTime.Now)
return PresentStyle;
else if (t.IntervalEndTime < DateTime.Now)
return OverdueStyle;
return DefaultStyle;
}
}
My ControlTemplates look like this:
<ControlTemplate x:Key="defaultTaskTemplate">
<Border BorderThickness="0, 0, 0, 1" Height="56" BorderBrush="{StaticResource SideBarShadowBrush}" Background="Transparent">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="56"/>
<ColumnDefinition Width="130"/>
<ColumnDefinition Width="214"/>
<ColumnDefinition Width="56"/>
<ColumnDefinition Width="56"/>
</Grid.ColumnDefinitions>
<TextBlock Padding="13,16,0,0" Grid.Column="1" Foreground="{StaticResource TextGreyBrush}"><Run Text="{Binding IntervalStartTime, StringFormat=\{0:hh:mm\}}"/><Run Text=" - "/><Run Text="{Binding IntervalEndTime, StringFormat=\{0:hh:mm\}}"/></TextBlock>
<TextBlock Padding="13,16,0,0" Grid.Column="2" Foreground="{StaticResource TextGreyBrush}" Text="{Binding Title}"/>
<Views:ArrowControl HorizontalAlignment="Center" Width="20" Grid.Column="4" Height="13" VerticalAlignment="Center" ArrowBrushColor="{StaticResource TextGrey}"/>
<Views:CheckMarkControl Width="30" Height="30" />
</Grid>
</Border>
</ControlTemplate>
For me, this looks pretty simple.
However my styles look like this:
<Style TargetType="{x:Type ListBoxItem}" x:Key="normalTaskItemStyle" BasedOn="{StaticResource baseTaskStyle}">
<Setter Property="Template" Value="{StaticResource defaultTaskTemplate}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Template" Value="{StaticResource defaultSelectedTaskTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
As you can see, I'm changing the template when the selection changes.
What I'm facing is when the actual bind to the source occurs, the window hangs, waiting for the listbox to render the items. When the window opens, I display a "spinner" element, to indicate I'm downloading content. One the download finishes, I databind and I see the spinner stop before it is removed. The spinner stopping indicates to me, that the window thread hangs because the binding is in process. Once the initial binding is over, I can scroll/pan just fine and no performance hit is notable.
I've searched the web for some info on optimizing this scenario, but I cannot seem to find anything relevant.
Am I looking in the wrong places?
What I would also like to know, if this is an acceptable way of producing a list? Is there any easier way to do it? I.e. avoiding the large number of controltemplates and styles?
Can anybody help?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我通常不会在选择项目时费心更改模板,最好在同一模板中使用视觉状态,这可能会避免重新创建一些您本来会在两种状态下重用的元素。不过,这个选定的模板不应导致加载挂起。
我很惊讶您发现只有 15 个项目出现了明显的延迟。我没有看到你的模板有任何明显的错误,看起来很简单,但我不知道那些箭头和复选标记控件中有什么。您可能需要检查这些控件的 xaml(或可视化树和运行时),以确保它们不是不必要的复杂。
您可以尝试以下几种方法来减少初始渲染时间:
在每个项目中使用视觉状态来逐步显示内部元素。因此,如果这些箭头或复选标记控件是问题所在,则可能您的每个元素都以这些元素折叠开始,然后在一段时间后使它们可见。
不要一次性绑定整个列表,而是绑定到最初为空的列表,然后将每个项目添加到调度程序循环中,以允许 UI 在创建每个容器之间做出响应。
I usually wouldn't bother with changing the template when selecting an item, better to use a visual state within the same template, which probably would avoid recreating some elements that you'd otherwise reuse in both states. This selected template shouldn't be causing the loading hang though.
I'm surprised you're seeing a noticeable delay with only 15 items. I don't see anything glaringly wrong with your template, is seems pretty simple, but I don't know what is in those arrow and checkmark controls. You may want to inspect the xaml (or visual tree an runtime) of those controls to make sure they aren't needlessly complex.
There are a couple things you can try to reduce the initial render time:
Use visual states in each item to progressively show internal elements. So, if those arrow or checkmark controls are the problem, maybe you have each element start with those elements collapsed and then make them visible after some time has passed.
Don't bind the whole list at one, but bind to an initially empty list and then add each item in a dispatcher loop that allows the UI to be responsive in between creating each container.