简体   繁体   中英

C# WPF - ListBox with DataTemplate - add extra “header” row

I have ListBox with DataTemplate, like this:

 <ListBox ItemsSource="{Binding}" BorderBrush="Transparent" 
         Grid.IsSharedSizeScope="True"
         HorizontalContentAlignment="Stretch"
         Grid.Row="1"
         Grid.Column="0" Grid.ColumnSpan="4"         
         Name="playerList">          
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Black" BorderThickness="2">
                        <Grid Margin="4">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="3*" />
                                <ColumnDefinition Width="3*" />
                                <ColumnDefinition Width="3*" />
                                <ColumnDefinition Width="3*" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="16"  />
                            <TextBlock Grid.Column="1" Text="{Binding Drinked }" FontWeight="Bold" FontSize="16" />
                            <TextBlock Grid.Column="2" Text="{Binding Remaining }" FontWeight="Bold" FontSize="16" />
                            <Button Grid.Column="3" Name="addButton" Click="addButton_Click" FontWeight="Bold" FontSize="16">+</Button>
                            <Button Grid.Column="4" Name="substractButton" Click="substractButton_Click" FontSize="16">-</Button>
                        </Grid>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

I have class that has the properties Name, Drinked, etc. It's in List<> that is set as DataSource. This part works fine.

But I need to add extra entry to this ListBox, that will be displayed before the DataTemplate. It will not have bindings or the same structure - it will serve as header and will have different layout than the DataTemplate. Is there any way to do it? If I add it like in a normal ListBox, I then got an error that the ListBox must be empty before using binding.

Now the ListBox looks like this: 在此处输入图片说明

But I need to make it looks like this: 在此处输入图片说明

Is it possible to do so? If there is way to do it using other element than ListBox, I'm fine with it, as long as I can use Binding and DataTemplate.

If scrollign doesn't matter to you can put simply a static border just above your listbox.. For the scenario where you want to include that extra row in the scrolling you have to modify ListBox's template. Here is an example of an implicit style that would add a TextBlock just before your items and that would participate of the scrolling:

        <Style TargetType="{x:Type ListBox}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBox}">
                        <ScrollViewer x:Name="ScrollViewer">
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <TextBlock Text="I am an extra row" Grid.Row="0"/>
                                <ItemsPresenter Grid.Row="1"/>
                            </Grid>
                        </ScrollViewer>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

In fact by overriding the control's template you can give it all the customizations that you need... You can change the textblock for whatever any other control you want..

You could achieve this goal by using a Stackpanel or Grid. However, if you insist on using an extra row in your ListBox, you can make use of a Content Template Selector. In the following solution, I did use a trigger to set the template of the current item according to its type.

    <Window.Resources>
    <local:ObjectToTypeConverter x:Key="ObjectToTypeConverter" />

    <ControlTemplate x:Key="emptyRow">
        <Grid HorizontalAlignment="Stretch">
            <Border Height="50" BorderBrush="Black" BorderThickness="2">
                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="Extra row that is not part of the binding or DataTemplate" />
            </Border>
        </Grid>
    </ControlTemplate>
    <ControlTemplate x:Key="default">
            <Border BorderBrush="Black" BorderThickness="2">
                <Grid Margin="4">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="3*" />
                        <ColumnDefinition Width="3*" />
                        <ColumnDefinition Width="3*" />
                        <ColumnDefinition Width="3*" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="16"  />
                    <TextBlock Grid.Column="1" Text="{Binding Drinked }" FontWeight="Bold" FontSize="16" />
                    <TextBlock Grid.Column="2" Text="{Binding Remaining }" FontWeight="Bold" FontSize="16" />
                    <Button Grid.Column="3" Name="addButton" Click="addButton_Click" FontWeight="Bold" FontSize="16">+</Button>
                    <Button Grid.Column="4" Name="substractButton" Click="substractButton_Click" FontSize="16">-</Button>
                </Grid>
            </Border>
    </ControlTemplate>

    <Style x:Key="ItemStyle" TargetType="{x:Type ListBoxItem}">
        <Setter Property="Template" Value="{DynamicResource emptyRow}" />
    <Style.Triggers>
            <DataTrigger Binding="{Binding ., Converter={StaticResource ObjectToTypeConverter}}" Value="{x:Type local:Model}">
                <Setter Property="Template" Value="{DynamicResource default}" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<Grid>
    <ListBox ItemsSource="{Binding}" BorderBrush="Transparent"
     Grid.IsSharedSizeScope="True"
     HorizontalContentAlignment="Stretch"
     Grid.Row="1"
     ItemContainerStyle="{StaticResource ItemStyle}"
     Grid.Column="0" Grid.ColumnSpan="4"
     Name="playerList" />
</Grid>

The converter simply converts the object to its type

    public class ObjectToTypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value?.GetType();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

To add different objects to your collection, change the type to object or to a type according to your inheritance setup.

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM