Disabling Individual GridView and ListView Items in a XAML Application

One of the most significant aspects of the XAML-based applications is the concept of lookless controls. This means that controls can be restyled without changing the behaviour. The Windows App SDK and Uno Platform provide out-of-the-box styles for GridView and ListView controls, offering a great starting point for working with these controls. In this post, Nick Randolph digs into  being able to disable individual items.

The default style for these controls includes a Disabled state, which is applied to each item when the IsEnabled property on the control is set to true. However, in some scenarios, it may be necessary to disable individual items. Unfortunately, unlike many other aspects of the GridView and ListView controls, there’s no easy way to disable individual items. Let’s dig into this and look at why it’s difficult and a workaround that can be used. For the rest of this post, we will focus on the GridView, but everything we cover can be applied equally to the ListView control.

Disabling Items via IsEnabled on GridViewItem

The GridView control has several templates that control how the control is styled. A ControlTemplate for the GridView defines things like the position and style of scrollbars. The arrangement of items is governed by the ItemsPanelTemplate (the ItemsPanel property). The ItemTemplate controls the layout of each item. Each item is wrapped in a GridViewItem, and the ControlTemplate for the GridViewItem defines the various visual states for the item, such as Selected, Pressed and Disabled.

When the GridView is disabled, the each of the GridViewItem transitions to the Disabled visual state. In order to disable an individual item, the challenge is to switch the respective GridViewItem into the Disabled state. This requires accessing the GridViewItem in order to set the IsEnabled property to false.

Accessing the GridViewItem isn’t straightforward since the lifecycle of each GridViewItem is managed by the GridView. However, there is a point where we can access the GridViewItem, which is at the point where it’s applying the ItemTemplate for the corresponding item that’s to appear in the GridView. At this point the GridViewItem is already created and it’s template has been applied. Normally, an instance of the ItemTemplate is created (or recycled) and is used to layout the item. However, if we use an ItemTemplateSelector, we have an opportunity to interact with the GridViewItem before it applies the ItemTemplate.

In the following code, the CustomTemplateSelector is used to setup a Binding between the IsEnabled property on the GridViewItem (actually on SelectorItem which is the base for GridViewItem and ListViewItem) and the IsEnabled property on the item being displayed.

				
					public class CustomTemplateSelector : DataTemplateSelector
{       
    public DataTemplate ItemTemplate { get; set; }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if(container is SelectorItem selector &&
            item is SimpleItem si)
        {
            var myBinding = new Binding
            {
                Source = si,
                Path = new PropertyPath(nameof(si.IsEnabled)),
                Mode = BindingMode.TwoWay,
                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
            };
            BindingOperations.SetBinding(selector, SelectorItem.IsEnabledProperty, myBinding);
        }
        return ItemTemplate;
    }
}
				
			

In XAML we need to create an instance of the CustomTemplateSelector and set the ItemTemplate to be the layout for the item to be displayed.

				
					<local:CustomTemplateSelector x:Key="CustomTemplateSelector">
	<local:CustomTemplateSelector.ItemTemplate>
		<DataTemplate>
			<Grid Height="200"
				Width="200">
				<TextBlock Text="{Binding Display}"
						HorizontalAlignment="Center"
						VerticalAlignment="Center" />
			</Grid>
		</DataTemplate>
	</local:CustomTemplateSelector.ItemTemplate>
</local:CustomTemplateSelector>
				
			

And then, we apply the CustomTemplateSelector to the GridView.

				
					<GridView ItemsSource="{x:Bind Items}"
			ItemTemplateSelector="{StaticResource CustomTemplateSelector}">
				
			

Now items in the GridView can be disabled by adjusting the IsEnabled property on the item. For example, the following image shows Item 2 in the disabled state.

Disabling Items using IsHitTestEnabled

An alternative to using the existing Disabled state in the Template for the GridViewItem, is to set the IsHitTestEnabled property to false for the relevant GridViewItem. This effectively disables the item by preventing input on the disabled item from being processed by the GridView

This can be done in XAML by data binding the IsHitTestEnabled property on the root element of the Template for the GridViewItem to the IsEnabled property on the relevant item.

				
					<Style x:Key="CustomDefaultGridViewItemStyle"
		TargetType="GridViewItem">
	.... setters ... 
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="GridViewItem">
				<ListViewItemPresenter x:Name="Root"
				    IsHitTestVisible="{Binding IsEnabled}"
				    .. properties ... 
				    />
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>
				
			

Note that because this option doesn’t use the Disabled state, you’ll need to update the layout for the item in the ItemTemplate to reflect that the item is disabled. For example, in the following XAML, a gray Border is displayed when the IsEnabled property is false (note using an inverted bool to visibility converter).

				
					<DataTemplate x:Key="CustomItemTemplate">
	<Grid Height="200"
			Width="200">
		<Border	Background="Gray"
				Visibility="{Binding IsEnabled, Converter={StaticResource BoolVisibilityConverter}, ConverterParameter=invert}" />
		<TextBlock Text="{Binding Display}"
					HorizontalAlignment="Center"
					VerticalAlignment="Center" />
	</Grid>
</DataTemplate>
				
			

In this post, we’ve covered two ways to disable individual items in the GridView. The benefits of using the template selector approach are that it makes use of the existing Disabled visual state, and the template selector can be reused across any GridView or ListView.

A side note: If you plan to take this strategy cross-platform with Uno Platform, you’ll notice that the container parameter in the SelectTemplateCore method of the DataTemplateSelector isn’t a SelectItem. Instead, it’s a ContentPresenter, and you then need to access the TemplatedParent property to get the GridViewItem.

About Uno Platform

For those new to the Uno Platform, it allows for creating pixel-perfect, single-source C# and XAML apps that run natively on Windows, iOS, Android, macOS, Linux and Web via WebAssembly. In addition, it offers Figma integration for design-development handoff and a set of extensions to bootstrap your projects. Uno Platform is free, open-source (Apache 2.0), and available on GitHub.

The original version of this post was published on Nick Randolph’s blog. For additional Uno Platform and XAML articles by Nick, visit Nick’s .NET Travels.

Next Steps

To upgrade to the latest release of Uno Platform, please update your packages to 4.8 via your Visual Studio NuGet package manager! If you are new to Uno Platform, following our official getting started guide is the best way to get started. (5 min to complete)

Tags:

Share this post:

Uno Platform 5.2 – True Single Project, enhanced Skia renderers, Multi Window and .NET 9 support , and more! See what’s new