Search code examples
c#wpfdrop-down-menudatagriditemsource

How to display a DataGrid property with combo box/drop down list that varies according to input of another text box property


I have a DataGrid which is bind to a list like below that has various properties. I have 2 requirements that i am struggling with.

<DataGrid  ItemsSource="{Binding Path=VariedObjectList}" 
AutoGeneratingColumn="OnAutoGeneratingColumn" x:Name="dataGridView"
                          SelectionMode="Single" AutoGenerateColumns="true" CanUserAddRows="true"  CanUserDeleteRows="true />
  1. For a specific value in the text box property1, the list of items displayed in property2 should dynamically change. So, for ex, here if 'Value' has value say 2, then the 'ValueRange' should display a dropdown with items say "2-5" and "5.81-6.6" as list items and if Value is 8 then list changes to => 7.9-8.8 ; 8.81-11.2 and so on like in below image.

How can i achieve this ? I would like to keep the binding to the entire collection itself as in above xaml.

enter image description here

 public class VariedObjectList
{
    public Modes ModeType { get; set; }
    public int Value{ get; set; }
    public <dataType??> ValueRange { get; set; } // not sure what should be the type for ValueRange

}
public enum Mode
    {
        ModeSlow = 0,
        ModeFast = 1,
        ModeNeutral = 2,
        ModeNone = 3
    }


  1. If my 'ModeType' property is 'ModeSlow' or 'ModeFast' then 'ValueRange' will need to be a dropDownlist. If ModeType is "ModeNeutral" then ValueRange has to accept a textBox. How do i achieve this?

I modified the code as suggested by Keith and although most of my changes are working, i have problem in the below areas

(i)i could not get to have the combo box editable working. It takes value when i type in but once my focus leaves the field, the text disappears and binding never happens with 'SelectedValueRange' for text entered. But when there is a list and if i select any value from list, that gets binded to 'SelectedValueRange' but not for text input.

(ii)ModeType being enum, when new row is entered by default i want it to display 'ModeSlow' but its empty. Also, if I update one row, all the previous rows are also updated with the latest value.its behaviour is wierd but i know i am missing something.

public class VariedObjectList : INotifyPropertyChanged
{
    public Mode ModeType { get; set; } = Mode.ModeSlow;
    private int val;
    public int Value {
            get { return val; }
            set
            {
                val = value;

                OnPropertyChanged("Value");
                SetValueRangeList();
            }
        }
    public List<strings> ValueRange { get; set; } 
    public string SelectedValueRange {get; set;}

}

below is updated xaml

I am setting the ObjectDataProvider for the Enum

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

<DataGrid x:Name="dataGridView" Grid.Row="0" DataGridCell.Selected="DataGridGotFocus" ItemsSource="{Binding VariedObjectList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False" 
                               CanUserDeleteRows="true"  SelectionMode="Single" CanUserAddRows="True" VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True">

                    <DataGrid.Columns>
                        <DataGridTemplateColumn  Header=" ModeType " >
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <ComboBox ItemsSource="{Binding Source={StaticResource EnumProvider}}"
                                              SelectedItem="{Binding Path= ModeType 
, UpdateSourceTrigger=LostFocus, Mode=TwoWay}" />
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="Value" Binding="{Binding Value, UpdateSourceTrigger=LostFocus}"/>
                        <DataGridTemplateColumn  Header="ValueRange" >
                            <DataGridTemplateColumn.CellEditingTemplate>
                                <DataTemplate>
                                    <ComboBox IsEditable="True" SelectedValue="{Binding SelectedValueRange, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Path= ValueRange }" /> 
                                </DataTemplate>
                            </DataGridTemplateColumn.CellEditingTemplate>
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding SelectedValueRange, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"/>

                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>


Can someone please point out what am i missing with sample code? Thanks.


Solution

  • Let's start with the VariedObjectList class:

    • Since ValueRange is going to be changing nd you want the UI to update with it, VariedObjectList is going to have to implement the INotifyPropertyChanged Interface.

    • The type for ValueRange can be any generic collection of int. I would recoomend declaring the property as IList<int>, because it gives you lots of room to use different collections if you need to.

    • In ModeType.set, along with raising PropertyChanged, you'll need to create or retrieve the list of values you want to display for that Mode and then assign that list to ValueRange.

    Now for the DataGrid:

    If you want to change the behavior of the ValueRange column depending on ModeType, you're going to have to give up AutoGenerateColumns="true" Instead, you'll explicitly define the columns between <DataGrid.Columns> tags.

    I would recommend DataGridTemplateColumn for the ValueRange column. The CellEditingTemplate would contain a ComboBox with ItemsSource={Binding ValueRange}. You can apply a Style to the ComboBox that changes IsEditable depending on the value of ModeType. This will let you set the ability of the user to enter text which doesn't match any items in the list.

    For the editable ComboBox, you will have to use the Text property. SelectedValue and SelectedItem only work when selecting an item from the drop down, not when entering custom text.