1
votes

I have a ComboBox that holds a list of StrategyViewModels. The StrategyViewModel has an ObservableCollection of StrategyParameterViewModels inside of it. I have a StrategyViewModel called SelectedStrategy that I bound to the SelectedItem property on the combobox. When the user selects a Strategy from the ComboBox I would like to set the itemsource of a datagrid to the StrategyParameters inside that Strategy. I've tried all different ways, but nothing seems to work.

Here's the XAML:

<ComboBox Height="23" Margin="0,12,0,0" Name="cbxStrats" VerticalAlignment="Top" ItemsSource="{Binding Strategies}" DisplayMemberPath="Name" SelectedItem="{Binding Path=SelectedStrategy,Mode=TwoWay}" />

 <DataGrid AutoGenerateColumns="False" Margin="12,97,14,35" Name="dgSettings" ItemsSource="{Binding SelectedStrategy.Parameters, Mode=TwoWay}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name"  Binding="{Binding Path=Name}" IsReadOnly="True"/>
            <DataGridTextColumn Header="Value" Binding="{Binding Path=Value}" IsReadOnly="False"/>
        </DataGrid.Columns>
    </DataGrid>

Here is the Strategy ViewModel:

public class StrategyViewModel : ViewModelBase
{
    public StrategyObject Strategy { get; set; }

    public int Id
    {
        get { return Strategy.Id; }
        set 
        {
            if (value == Strategy.Id)
                return;
            Strategy.Id = value;
            OnPropertyChanged("Id");
        }
    }

    public string Name
    {
        get { return Strategy.Name; }
        set 
        {
            if (value == Strategy.Name)
                return;
            Strategy.Name = value;
            OnPropertyChanged("Name");
        }
    }

    public ObservableCollection<StrategyParameterViewModel> Parameters { get { return _parameters; } }
    public ObservableCollection<StrategyParameterViewModel> _parameters;

    public StrategyViewModel()
    {
        Strategy = new StrategyObject();
        _parameters = new ObservableCollection<StrategyParameterViewModel>();
    }

    public StrategyViewModel(StrategyObject o, IEnumerable<StrategyParameterObject> pms)
    {
        Strategy = o;
        _parameters = new ObservableCollection<StrategyParameterViewModel>();
        foreach (StrategyParameterObject s in pms)
        {
            _parameters.Add(new StrategyParameterViewModel(s));
        }
    }   
}

And here is the StrategyParameter ViewModel:

public class StrategyParameterViewModel : ViewModelBase
{
    public StrategyParameterObject StrategyParameter { get; set; }

    public int Id
    {
        get { return StrategyParameter.Id; }
        set 
        {
            if (value == StrategyParameter.Id)
                return;
            StrategyParameter.Id = value;
            OnPropertyChanged("Id");
        }
    }

    public int StrategyId
    {
        get { return StrategyParameter.StrategyId; }
        set 
        {
            if (value == StrategyParameter.StrategyId)
                return;
            StrategyParameter.StrategyId = value;
            OnPropertyChanged("StrategyId");
        }
    }

    public string Name
    {
        get { return StrategyParameter.Name; }
        set 
        {
            if (value == StrategyParameter.Name)
                return;
            StrategyParameter.Name = value;
            OnPropertyChanged("Name");
        }
    }

    public string Value
    {
        get { return StrategyParameter.Value; }
        set 
        {
            if (value == StrategyParameter.Value)
                return;
            StrategyParameter.Value = value;
            OnPropertyChanged("Value");
        }
    }

            public StrategyParameterViewModel() 
    {
        StrategyParameter = new StrategyParameterObject();
    }

    public StrategyParameterViewModel(StrategyParameterObject o) 
    {
        StrategyParameter = o;
    }   
}
2

2 Answers

1
votes

The problem is that you are trying to set up a two way binding with a read-only property. You need to either add a setter for SelectedStrategy.Parameters, or use a OneWay binding on the datagrid itemssource.

1
votes

Off the top of my head, I don't think you're doing anything wrong with regards to your XAML.

Data Binding is notoriously tricky. I suggest adding PresentationTraceSources to get more debug information as to what is being found. This will ship several lines of data about binding results to your Output window.

    <ComboBox Height="23" Margin="0,12,0,0" Name="cbxStrats" VerticalAlignment="Top" DisplayMemberPath="Name">
        <ComboBox.ItemsSource>
            <Binding Path="Strategies" PresentationTraceSources.TraceLevel="High"/>
        </ComboBox.ItemsSource>
        <ComboBox.SelectedItem>
            <Binding Path="SelectedStrategy" Mode="TwoWay" PresentationTraceSources.TraceLevel="High"/>
        </ComboBox.SelectedItem>
    </ComboBox>

    <DataGrid AutoGenerateColumns="False" Margin="12,97,14,35" Name="dgSettings">
        <DataGrid.ItemsSource>
            <Binding Path="SelectedStrategy.Parameters" Mode="TwoWay" PresentationTraceSources.TraceLevel="High"/>
        </DataGrid.ItemsSource>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name"  Binding="{Binding Path=Name}" IsReadOnly="True"/>
            <DataGridTextColumn Header="Value" Binding="{Binding Path=Value}" IsReadOnly="False"/>
        </DataGrid.Columns>
    </DataGrid>

In addition, WPF Inspector can help, as you can tweak Data Binding expressions in real time when running your actual app. http://wpfinspector.codeplex.com/