谢谢你@anthonywjones,你的回答(几乎)正是我想要的。我决定在这里提供我自己的答案,以便其他读者知道我是如何使他的答案适应我的需要的。
首先,正如建议的那样,我从itemscontrol派生,并提供第二个“template”属性,称为
HeaderTemplate
:
#region HeaderTemplate PROPERTY
public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register(
"HeaderTemplate",
typeof( DataTemplate ),
typeof( ItemsControlWithHeaders ),
new PropertyMetadata( null, new PropertyChangedCallback( OnHeaderTemplateChanged ) ) );
public DataTemplate HeaderTemplate
{
get { return ( DataTemplate )this.GetValue( HeaderTemplateProperty ); }
set { this.SetValue( HeaderTemplateProperty, value ); }
}
private static void OnHeaderTemplateChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args )
{
ItemsControlWithHeaders control = obj as ItemsControlWithHeaders;
control.InvalidateArrange();
}
#endregion
第二,我要压倒一切
PrepareContainerForItemOverride
提供我自己的模板选择逻辑。我所做的只是将任何“字符串”项重定向到
页眉模板
以及其他日常用品
ItemTemplate
:
protected override void PrepareContainerForItemOverride( DependencyObject element, object item )
{
base.PrepareContainerForItemOverride( element, item );
ContentPresenter presenter = element as ContentPresenter;
if( presenter != null )
{
if( item is string )
{
presenter.ContentTemplate = this.HeaderTemplate;
}
else
{
presenter.ContentTemplate = this.ItemTemplate;
}
}
}
现在可以这样使用此控件:
<local:ItemsControlWithHeaders Grid.Row="1" ItemsSource="{Binding GroupedCities}" ScrollViewer.VerticalScrollBarVisibility="Auto">
<local:ItemsControlWithHeaders.Template>
<ControlTemplate TargetType="local:ItemsControlWithHeaders">
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</local:ItemsControlWithHeaders.Template>
<local:ItemsControlWithHeaders.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</local:ItemsControlWithHeaders.ItemsPanel>
<local:ItemsControlWithHeaders.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Style="{StaticResource PhoneTextSubtleStyle}" Foreground="{StaticResource PhoneAccentBrush}" Margin="12,12,12,8" />
</DataTemplate>
</local:ItemsControlWithHeaders.HeaderTemplate>
<local:ItemsControlWithHeaders.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource EmptyNonSelectButtonStyle}" BorderThickness="0" HorizontalContentAlignment="Left" Click="AnyCityButton_Click">
<TextBlock Text="{Binding Name, Mode=OneWay}" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeLarge}" />
</Button>
</DataTemplate>
</local:ItemsControlWithHeaders.ItemTemplate>
</local:ItemsControlWithHeaders>
要构建数据源,您必须传递给这个特殊的混合控制,Linq很好,但我选择了一个更明确的解决方案,在我的视图模型中实现:
public IEnumerable<object> GroupedCities
{
get
{
yield return new CurrentLocationCityViewModel();
yield return Localized.TopTenCitiesHeader; // string resource
foreach( CityViewModel city in this.TopTenCities )
{
yield return city;
}
yield return Localized.TopHundredCitiesHeader; // string resource
foreach( CityViewModel city in this.TopHundredCities )
{
yield return city;
}
}
}
我现在有一个通用的
ItemsControlWithHeaders
我可以在不止这个场景中重用。性能很好。对于像我这样的纯粹主义者来说,唯一剩下的问题是,基itemsControl在调试时会抱怨,因为“object”类型没有“name”属性。它产生了一个
System.Windows.Data Error: BindingExpression path error: 'Name' property not found
调试输出中的消息,可以忽略。