Wpf using an items control instead of listbox.



  • I'm trying to replace our dependency on listbox to handle styling a list of items.

    I'm using an itemscontrol, but I had to use some codebehind to tell it to provide a content control so I can bind against it.

        class ItemsView : ItemsControl
        {
            protected override DependencyObject GetContainerForItemOverride()
            {
                return new ContentControl();
            }
            protected override bool IsItemItsOwnContainerOverride(object item)
            {
                return item is ContentControl;
            }
        }
    
        <Style
            x:Key="{x:Static themes:Styles.ItemsViewStyleKey}"
            TargetType="controls:ItemsView">
            <Setter Property="AlternationCount" Value="2"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Margin" Value="0"/>
            <Setter Property="Padding" Value="-1"/>
            <Setter Property="IsHitTestVisible" Value="False"/>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
    
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="ContentControl">
                        <Setter Property="Foreground" Value="some resource"/>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="ContentControl">
                                    <Border Background="{TemplateBinding Background}" Padding="4"
                                        BorderBrush="some resource"
                                        BorderThickness="1,0,1,1">
                                        <ContentPresenter/>
                                    </Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
    
                        <Style.Triggers>
                            <Trigger Property="ItemsControl.AlternationIndex"  Value="0">
                                <Setter Property="Background" Value="some recourse" />
                            </Trigger>
                            <Trigger Property="ItemsControl.AlternationIndex"  Value="1">
                                <Setter Property="Background" Value="some resource" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Setter.Value>
            </Setter>
        </Style>
    

    However, the guy here that everyone refers to for creating the styles isn't satisfied with what I did and wants me to do the same thing without code behind.

    I'm asking him for help but he can't seem to communicate his ideas to me and he's getting very frustrated. I'm new here and I don't want to piss him off, and he's leaving soon.

    Here are some of the things I've asked and his responses.

    Me: It doesn't work without the ItemView codebehind. That code basically supplies a contentcontrol as the item container, whereas the basic ItemsControl doesn't supply the contentcontrol, only a contentpresenter, which means I can't bind to background and other properties for the item containerstyle.

    Him: I don't necessarily hold your first assertion to be correct. There is a tendency here for folks to fall back to codebehind, but it is the wrong instinct when it comes to WPF. That's why we have [list of problems]

    Me: Is there a control type that supplies a container that can bind ui properties? I want to avoid using listcontrol, because we aren't using the selection features, so it's overkill. by default, Items control only has the content presenter, which doesn't have the properties I need like background.

    Him: ItemsControl is the correct control. Use ItemsPresenter instead

    Me: confused Items presenter doesn't have a foreground either.

    Him: They inherit from the parent.



  • I don't know wpf, but it sounds to me like he was saying that it inherits its foreground and background colors from its parent element, so you should be able to either set them there, or if necessary, wrap it inside another element that exists just so that you can set those properties without changing them on the original parent.



  • @anotherusername

    Unfortunately the itemscontrol only produces a contentpresenter to hold items. Contentpresenter is holding the AlternationIndex.

    So, you have to do some ancestor binding to get it to work but you can only do it from the template.

    What I've done is told my new class to provide a contentcontrol, so I can bind to that. The listbox/view controls use this method to provide listbox/viewitem as the container for each element in the items.

    I can bind from the ItemTemplate, but that's not part of the style.



  • I think I'm going to just ram it through.

    I've checked the tree, and there's no way that items presenter accepts a foreground or background. Yes, you can inherit, but that has to be at the top control level. For foreground, this is fine, but for background it doesn't work because we need the alternation index.

    I've tried EVERYTHING, but itemscontainerstyle doesn't have it's own template, so you'd have to use the itemstemplate, and that's not acceptable since that could change from instance to instance and I want the style to handle alternating row colors.

    I've tried ItemPanelTemplate, and that doesn't work either. The ItemPanelTemplate just can't path up to the contentpresenter to read it's alternation index.

    The ONLY way is the itemstemplate or creating a new class to force it to provide a contentcontrol so I can bind to its properties from the style directly.



  • The problem is that I've butted heads with this guy before, and he seems to be very protective of the styles.

    Yeah, I get that I need to consider consistency, but sometimes not everything is a nail.



  • He's right, actually. I'm not sure why you're using a ContentControl for anything to do with an ItemsControl honestly, since the former is for holding one item and the latter is for multiple, but how it works is this:

    1. You bind your items to the ItemsControl's ItemsSource
    2. You supply an ItemsPanelTemplate if you want it to go horizontally or something.
    3. You use the ItemTemplate or else just put DataTemplates in the Resources to apply them to individual items.


  • I don't know what special considerations you have, but:

    0_1527027199081_208ac7c5-dbea-4bc9-b78e-fe7b7165d148-image.png



  • @xaade said in Wpf using an items control instead of listbox.:

                <Style TargetType="ContentControl">
                    <Setter Property="Foreground" Value="some resource"/>
    

    Waitwaitwaitwaitwait... So you're binding to a list of ContentControls, or what?

    Honestly, just stay off the ItemContainerStyle if you can. You want ItemTemplate. The former is for doing weird things to a box that contains whatever the ItemTemplate happens to be. It's rare for it to be a benefit in any way.



  • @magus said in Wpf using an items control instead of listbox.:

    Waitwaitwaitwaitwait... So you're binding to a list of ContentControls, or what?

    No.

    List(Box/View) provides a List(Box/View)Item that you can bind alternation row colors to using item container style. IOW, you're setting the style of the List(Box/View)Item when you set the item container style. So if you want to control how List(Box/View) selection border looks, you'd set the item container style. (Or you could make a separate style for List(Box/View)Item).

    ItemTemplate for List(Box/View) sets the layout of the item INSIDE the List(Box/View)Item.

    However, ItemsControl by default only provides a ContentPresenter, which can't accept a style that sets UI properties like background and such.

    Therefore, I'm creating a control based on ItemsControl that provides a ContentControl instead of a ContentPresenter, and then I'm binding background color to the ContentControl.

    0_1527040080843_60d08b70-4576-4616-aad8-9782faf41d08-image.png

    Yes, you can do it this way. But I want to apply the alternating colors universally, rather than having to set them in every singe instance of ItemsControl out there. I'm going for reusability, and if I take up the ItemTemplate, I can't use that to bind with.



  • @xaade Why, though? You should be able to do exactly what I did with a style and not have any trouble at all. Honestly, WPF is all about doing the most simple thing you can. Setting the ItemTemplate with a style will make it reusable, and whatever you mean by 'I can't use that to bind with' isn't a thing.



  • @magus said in Wpf using an items control instead of listbox.:

    @xaade Why, though? You should be able to do exactly what I did with a style and not have any trouble at all. Honestly, WPF is all about doing the most simple thing you can. Setting the ItemTemplate with a style will make it reusable, and whatever you mean by 'I can't use that to bind with' isn't a thing.

    If I set the ItemTemplate, then I can't set it locally, it will override the style's item template.

    The ItemTemplate is being used locally as a datatemplate to map out three fields into columns.

    Psuedo code

    <ItemsView>
    <ItemsView.ItemTemplate>
    <DataTemplate>
    <UniformGrid Columns="3">
    <TextBlock Text={Binding Label}/>
    <TextBlock Text={Binding ValueA}/>
    <TextBlock Text={Binding ValueB}/>
    

    If I set the ItemTemplate in the style, then when I write this instance of ItemsView, it will lose the style's ItemTemplate.

    If I want a reusable ItemTemplate that takes in a dynamic amount of columns, then I'll have to refactor the entire thing.

    And even so, if I have another template layout that I want alternating row colors, I have to reuse the same boilerplate item template code.


    For a little bit of code behind, I gain the ability to reuse the style independent of the template. I think it's worth it.



  • @xaade So if you want columns, why aren't you using a DataGrid?



  • @magus said in Wpf using an items control instead of listbox.:

    @xaade So if you want columns, why aren't you using a DataGrid?

    I know.... I know....

    If I were doing things my way, then I would use the ItemTemplate, and I'd throw a contentcontrol, bind to . and I'd have a VM for the row data, and have a datatemplate for the layout of the actual item.

    But that's not how they chose to do things.

    They're using the ItemTemplate to control layout of the item itself, so I can't use ItemTemplate to do my alternate row color bindings.

    For some odd reason they use UserControls where I'd use DataTemplate.

    Maybe it would help if I include the view, so you can see why I need the style the way it is.

    <controls:ItemsView 
                    ItemsSource="{Binding Tag, ElementName=uiValueBoxView}" 
                    IsHitTestVisible="False"
                    ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                    Grid.IsSharedSizeScope="True"
                    Loaded="ListViewLoaded"
                    Style="{DynamicResource {x:Static themes:Styles.ItemsViewStyleKey}}"
                    >
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid Width="{Binding ElementName=uiValueBoxView, Path=ActualWidth}">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="2*"/>
                                    <ColumnDefinition Width="5*"/>
                                </Grid.ColumnDefinitions>
                                <Label 
                                    Grid.Column="0" 
                                    Content="{common:XlateBinding Value.Label}"
                                    FontWeight="Bold"
                                    VerticalAlignment="Center"/>
                                <ItemsControl
                                    Grid.Column="1"
                                    BorderThickness="0"
                                    IsHitTestVisible="False"
                                    ItemsSource="{Binding Value.Values}"
                                    VerticalAlignment="Center"
                                    >
                                </ItemsControl>
                            </Grid>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </controls:ItemsView>
    

    I need to reuse the ItemView style, because they use ItemTemplate to do all of their layout, instead of having a specific VM for the items, and using a data template to layout the items.


Log in to reply