1
votes

I have met following problem in WPF binding. I need to load objects from XML file and create list of loaded items in listbox, and when listbox item is selected then display suitable set of objects.

I can do it in 'code behind' style, but I really want to do it in proper MVVM way. My Matrixes class is generated by xsd2code from which contains:

List<CorrectionMatrixType> correctionMatrixField;

and follows

 public partial class CorrectionMatrixType {   
   public MatrixType A {get; set;}
   public MatrixType B {get; set;}
   public MatrixType C {get; set;}
... }

How can I create 'dynamically'something like Grid with three DataGrids by Viewmodel and bind each matrix (A,B,C) to them which content will change depends of value selected in listbox? I know that to bind my MatrixType to DataGrid i have to use ValueConverter to convert my object to two-dimensional array.

Maybe I have to admit I am using MVVM Light.

Please, any suggestions?

2
Can you show the MatrixType class as well?Mighty Badaboom
Its generated code: pastebin.com/vhSqkUxJuser5784314

2 Answers

1
votes

I would use the INotifyPropertyChanged Interface. Here is a small example (not exactly your case, but enough to show the principle, I think):

MatrixType class:

public class MatrixType
{
    public string Name { get; set; }

    public string Width { get; set; }

    public string Height { get; set; }

}

Xaml:

 <Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"></ColumnDefinition>
        <ColumnDefinition Width="*"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <ListBox Grid.Column="0" ItemsSource="{Binding Items}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedItem}"></ListBox>
    <Grid Grid.Column="1">
        <StackPanel Orientation="Vertical">
            <TextBox Text="{Binding SelectedItem.Name}" Height="30"/>
            <TextBox Text="{Binding SelectedItem.Height}" Height="30"/>
            <TextBox Text="{Binding SelectedItem.Width}" Height="30"/>
        </StackPanel>
    </Grid>
</Grid>

MainViewModel.cs:

public class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        var list = new List<MatrixType>
        {
            new MatrixType {Height = "233", Name = "A", Width = "133"},
            new MatrixType {Height = "333", Name = "B", Width = "233"},
            new MatrixType {Height = "433", Name = "C", Width = "333"}
        };
        Items = new ObservableCollection<MatrixType>(list);
    }

    private MatrixType _selectedItem;
    public MatrixType SelectedItem
    {
        get => _selectedItem;
        set { _selectedItem = value; OnPropertyChanged(); }
    }

    public ObservableCollection<MatrixType> Items { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

MainViewModel.cs (when using MVVM Light):

public class MainViewModel : ObservableObject
{
    public MainViewModel()
    {
        var list = new List<MatrixType>
        {
            new MatrixType {Height = "233", Name = "A", Width = "133"},
            new MatrixType {Height = "333", Name = "B", Width = "233"},
            new MatrixType {Height = "433", Name = "C", Width = "333"}
        };
        Items = new ObservableCollection<MatrixType>(list);
    }

    private MatrixType _selectedItem;
    public MatrixType SelectedItem
    {
        get => _selectedItem;
        set { _selectedItem = value; RaisePropertyChanged(); }
    }

    public ObservableCollection<MatrixType> Items { get; set; }
}
0
votes

I wrote solution by myself, I don't know if it is good MVVM solution. I re-write my XSD so MatrixType becomes SimpleMatrix, and now:

XAML:

        <ListBox Margin="5,20,0,5" ItemsSource="{Binding CorrectionMatrixes}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding name}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <command:EventToCommand Command="{Binding SelectionChangedCommand}" PassEventArgsToCommand="True"/>
            </i:EventTrigger>
            <i:EventTrigger EventName="Loaded">
                <command:EventToCommand Command="{Binding ListBoxLoadedCommand}" PassEventArgsToCommand="True"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ListBox>

<DataGrid CanUserAddRows="False"  HeadersVisibility="None" ItemsSource="{Binding CorrectionMatrixA, Converter={StaticResource MatrixToArray }}"/>

And in my viewmodel:

    public RelayCommand<SelectionChangedEventArgs> SelectionChangedCommand => new RelayCommand<SelectionChangedEventArgs>(SelectionChanged);
    public RelayCommand<RoutedEventArgs> ListBoxLoadedCommand => new RelayCommand<RoutedEventArgs>(ListBoxLoaded);

    private string CorrectionMatrixName { get; set; }

    private void ListBoxLoaded(RoutedEventArgs obj)
    {
        if (obj.Source is ListBox listBox)
        {
            listBox.SelectedIndex = 0;
        }
    }

    private void SelectionChanged(SelectionChangedEventArgs obj)
    {
        if (obj.AddedItems.Count <= 0) return;
        if (obj.AddedItems[0] is CorrectionMatrix matrix)
        {
            CorrectionMatrixName = matrix.name;
        }
        RaisePropertyChanged(() => CorrectionMatrixA);
    }

    public SimpleMatrix CorrectionMatrixA
    {
        get
        {
            try
            {
                var x = Matrixes.Correction.Where(a => a.name == CorrectionMatrixName)
                            .Select(a => a.A).Single();
                return x;
            }
            catch (InvalidOperationException)
            {
                return null;
            }
        }
    }

Matrixes are loaded by:

Matrixes = settingsLoader.LoadMatrixes(Properties.Resources.MatrixesSettings);

How does it all works:

  1. when user control is loaded => selected index on listbox is setting to zero

  2. When selected item on listbox is changed it fires event that changes CorrectionMatrixName

  3. Binding properties returns suitable matrix finding it in array by name

I don't post Converter code - it doesn't matter here. Thats full, my own solution that worked for me. I hope it will helps other people