Change ListView CellTemplate based on state of ite

2019-06-12 16:27发布

问题:

I have a ListView that is has an ObservableCollection as its ItemsSource, and it has several columns. One of these is a State column which, depending on the current state of the item, shows a different message. Currently this is implemented as a basic string, and while it works it is far from pretty or userfriendly. I want to be able to vary the sort of output to more properly suit the state of the item.

I did do some research and know that I need to use a CellTemplate to affect the display, but all the different sorts of templates simply overwhelm me to the point where I can't figure out where to go next.

My code (excluding lots of other listview fluff) is as follows:

<ListView Name="itemsListView" ItemsSource="{Binding Source={StaticResource listingDataView}}" IsSynchronizedWithCurrentItem="True">
    ...
    <ListView.View>
         <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Item Information">
             ...
             <GridViewColumn DisplayMemberBinding="{Binding Path=StatusMessage}" Width="283" Header="Status" HeaderContainerStyle="{StaticResource GVHeaderLeftAlignedStyle}" />
         </GridView>
    </ListView.View>
</ListView>

Yes, the items have hardcoded 'Status Message' that get updated alongside other properties that are actually relevant for the code, causing ugly duplication elsewhere in my code. (And yes, I know this is far from pretty, but I want to improve this too.) That property would be called ItemState as I am not all that creative.

So, my question is: how can I vary this column to have the most suitable display for the given state? Textual descriptions will do for many states, but some are rather lengthy and might profit from a text with a progress bar besides it, and maybe some sort of time remaining. Another state would profit from having a clickable hyperlink. In other words, I think I need at least 3 different CellTemplates.

I realize it is a rather open-ended question that largely suffers from the design mistakes of someone (=me) who has rather little experience with WPF, but that is exactly why I'm hoping someone experienced can set me straight with some basic code before I make an even worse mess of things than I have already. :)

回答1:

You can use triggers to change the content of the cell, e.g.

<GridViewColumn Header="Status">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <ContentControl>
                <ContentControl.Style>
                    <Style TargetType="{x:Type ContentControl}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding StateItem.HasError}" Value="True">
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <!-- Possibly create another contentcontrol which differentiates between errors -->
                                        <DataTemplate>
                                             <TextBlock Text="{Binding StateItem.Error}"
                                                        Foreground="Red"/>
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>

                            <DataTrigger Binding="{Binding StateItem.HasError}" Value="False">
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <Image Source="Images/Default.ico"/>
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ContentControl.Style>
            </ContentControl>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>

Code gets a bit crazy that way though if you branch it further but it's a way to do it.

Edit: The setters should set the ContentTemplate instead of the Content, apparently otherwise no new controls may be created and only one row shows the proper content since the content can only have one parent.