Thursday, October 10, 2019

Disable items in Infragistics ComboBoxField

I didn't have all the facts when I showed how to disable items in a ComboBox. The actual problem my colleague had was to disable selected items in an Infragistics XamDataGrid ComboBox column that is bound to an enumeration. This is a whole lot more complex.

Lets start by looking at the correct way to bind to an enumeration. You use an ObjectDataProvider like this...

Start a new C#, WPF project called DisableComboBoxItem. Add references to Infragistics, Infragistics Editors, and Infragistics Data Presenters so your References tree looks like this...



The XAML contains an ObjectDataProvider and a XamDataGrid containing a single ComboBoxField column.


<Window x:Class="DisableComboBoxItem.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DisableComboBoxItem"
        xmlns:igDP="http://infragistics.com/DataPresenter"
        xmlns:igWPF="http://schemas.infragistics.com/xaml/wpf"
        xmlns:System="clr-namespace:System;assembly=mscorlib"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <ObjectDataProvider x:Key="enumItems" MethodName="GetValues" ObjectType="{x:Type System:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:eItems"/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>
    <igDP:XamDataGrid DataSource="{Binding Things}">
        <igDP:XamDataGrid.FieldLayouts>
            <igDP:FieldLayout>
                <igDP:ComboBoxField Label="Items" Name="Item" ItemsSource="{Binding Source={StaticResource enumItems}}">
                </igDP:ComboBoxField>
            </igDP:FieldLayout>
        </igDP:XamDataGrid.FieldLayouts>
    </igDP:XamDataGrid>
 </Window>

The code behind will simply define the enum and some records to bind the XamDataGrid to.


using System.Collections.Generic;
using System.Windows;

namespace DisableComboBoxItem
{
    public enum eItems
    {
        Locked,
        Unlocked,
        Extracted
    }

    public class cThing
    {
        public eItems Item { get; set; }
    }

    public partial class MainWindow : Window
    {


        private List<cThing> _Things = new List<cThing>
        {
            new cThing() {Item=eItems.Locked },
            new cThing() {Item=eItems.Unlocked}
        };

        public List<cThing> Things { get { return _Things; } }

        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

The result is a dropdown list that is bound to an enumeration.

ComboBoxField bound to enum

Now for the tricky part. We want to disable the Extracted option. The enum is already defined so all we have to work with is the text. It's not ideal, but it is what it is. The first thing is to realize that the ComboBoxField uses a XamComboEditor to drop the list. This contains a regular ComboBox which itself contains ComboBoxItems. We have to drill all the way down to the ComboBoxItems and change their style in order to disable one of them. We will set the style using a converter.

Add a class to the project called Converters and add this converter to it.


using System;
using System.Globalization;
using System.Windows.Data;

namespace DisableComboBoxItem
{
    public class EnabledConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (value.ToString() != "Extracted");
        }

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

Go back to MainWindow.xaml and add a reference to the converter in the window resources.


    <Window.Resources>
        <ObjectDataProvider x:Key="enumItems" MethodName="GetValues" ObjectType="{x:Type System:Enum}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:eItems"/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
        <local:EnabledConverter x:Key="EnabledConverter"/>
    </Window.Resources>


Now insert this styling into the ComboBoxField


<igDP:ComboBoxField Label="Items" Name="Item" ItemsSource="{Binding Source={StaticResource enumItems}}">
  <igDP:ComboBoxField.Settings>
    <igDP:FieldSettings EditorType="{x:Type igWPF:XamComboEditor}">
      <igDP:FieldSettings.EditorStyle>
        <Style TargetType="{x:Type igWPF:XamComboEditor}">
          <Setter Property="ComboBoxStyle">
            <Setter.Value>
              <Style TargetType="ComboBox">
                <Setter Property="ItemContainerStyle">
                  <Setter.Value>
                    <Style TargetType="ComboBoxItem">
                      <Setter Property="IsEnabled" Value="{Binding RelativeSource={RelativeSource Self}, Path=DataContext, Converter={StaticResource EnabledConverter}}"/>
                    </Style>
                  </Setter.Value>
                </Setter>
              </Style>
            </Setter.Value>
          </Setter>
        </Style>
      </igDP:FieldSettings.EditorStyle>
    </igDP:FieldSettings>
  </igDP:ComboBoxField.Settings>
</igDP:ComboBoxField>

And the result of all that is...

ComboBoxField with disabled item
Now here's another wrinkle. My colleague's Enum is in a different class in a different namespace. There's a special syntax for handling this.

Add a Class Library project to the solution using its default name of ClassLibrary1. It will have a single class called Class1. Move the enum into that class.


Because the enum is in a different namespace, we need a new xmlns declaration.


xmlns:cl1="clr-namespace:ClassLibrary1;assembly=ClassLibrary1"


You might think you would just change the TypeName to
<x:Type TypeName="cl1:Class1.eItems"/>

Try it, you will get an intellisense error about embedded types or a run time error message that it can't find Class1.eItems. Something about the XAML interpreter expects a property after a period. Classes, structures, and enums confuse it. There is a special syntax to get around this issue. Try this instead.

<x:Type TypeName="cl1:Class1+eItems"/>


No comments:

Post a Comment