Home > front end >  Edit item in DataGrid not updated using MVVM with Caliburn Micro
Edit item in DataGrid not updated using MVVM with Caliburn Micro

Time:12-29

I have a GridViewModel with a DataGrid which is bound to a BindableCollection of CalibrationPoints. The grid contains 4 columns: No, Start, Stop and EstimatedCalibrationTime. The user is able to edit the values directly in the DataGrid. When value in column Start is edited, the value in column EstimatedCalibrationTime needs to be recalculated. When value in column Stop is edited, the value in column Start needs to be recalculated. I am facing 2 problems that i really do not know how to solve:

  1. how to know what column is edited?
  2. how to update the DataGrid with the new calculated values?
public class CalibrationPoint
    {
        public int No { get; set; }
        public int Start { get; set; }
        public int Stop { get; set; }
        public double EstimatedCalibrationTime { get; set; }
    }

    public class GridViewModel : Screen
    {
        public BindableCollection<CalibrationPoint> CalibrationPoints { get; }

        private CalibrationPoint selectedCalibrationPoint;
        public CalibrationPoint SelectedCalibrationPoint
        {
            get => selectedCalibrationPoint;
            set
            {
                selectedCalibrationPoint = value;
                NotifyOfPropertyChange(() => SelectedCalibrationPoint);
            }
        }

        public GridViewModel()
        {
            CalibrationPoints = new BindableCollection<CalibrationPoint>();

            CalibrationPoints.Add(new CalibrationPoint() { No = 1, Start = 1, Stop = 2, EstimatedCalibrationTime = CalculateCalibrationTime(1, 2) });
            CalibrationPoints.Add(new CalibrationPoint() { No = 2, Start = 3, Stop = 5, EstimatedCalibrationTime = CalculateCalibrationTime(3, 5) });
            CalibrationPoints.Add(new CalibrationPoint() { No = 3, Start = 6, Stop = 8, EstimatedCalibrationTime = CalculateCalibrationTime(6, 8) });
        }

        private double CalculateCalibrationTime(int start, int stop)
        {
            var time = (start * 0.7)   (stop * 1.2);
            return time;
        }
    }
Window x:Class="Wpf_DataGridTest.Views.GridView"
        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:Wpf_DataGridTest.Views"
        mc:Ignorable="d"
        Title="GridView" Height="300" Width="400">
    <Grid>
        <DataGrid Grid.Column="1" 
                ItemsSource="{Binding CalibrationPoints, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                AutoGenerateColumns="False"
                SelectedItem="{Binding Path=SelectedCalibrationPoint, Mode=TwoWay}"
                IsSynchronizedWithCurrentItem="True"
                CanUserAddRows="False"
                CanUserDeleteRows="False"
                CanUserReorderColumns="False"
                CanUserSortColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="no" Width="*" Binding="{Binding No}" IsReadOnly="True"/>
                <DataGridTextColumn Header="start" 
                                        Width="2*" 
                                        Binding="{Binding Start}">
                </DataGridTextColumn>
                <DataGridTextColumn Header="stop" 
                                        Width="2*" 
                                        Binding="{Binding Stop}"/>
                
                <DataGridTextColumn Header="estimated&#x0a;calibration&#x0a;time(s)" 
                                        Width="3*" 
                                        Binding="{Binding EstimatedCalibrationTime}"
                                        IsReadOnly="True"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

CodePudding user response:

When you tab out a field you edited in the datagrid, that value will transfer via the magic of binding to your viewmodel property. You have a CalibrationPoint viewmodel per row.

Therefore an edited value will transfer to the matching CalibrationPoint instance.

That value will be set on your property. You have default empty setters there but you're not forced to work that way.

One option would be to call logic in your setter(s). Write a method does your calculation. Call it from the setter.

Like is described here:

https://social.technet.microsoft.com/wiki/contents/articles/30564.wpf-uneventful-mvvm.aspx#Change_of_Variable

That covers 1)

Moving on from there to 2) you need to implement inotifypropertychanged on your viewmodel and raise property changed with the name of any field which is changing as a parameter. The view then re gets the value from the viewmodel for that property.

That seems to be covered here:

https://doc.postsharp.net/inotifypropertychanged-caliburnmicro

CodePudding user response:

I wrapped the CalibrationPoint model into a CalibrationPointViewModel to be able to perform calculations in the setters as Andy suggested. Now the DataGrid is updated and everything works! No changes to xaml necessary. Final viewmodel code:

public class CalibrationPoint
{
    public int No { get; set; }
    public int Start { get; set; }
    public int Stop { get; set; }
    public double EstimatedCalibrationTime { get; set; }
}

public class CalibrationPointViewModel : PropertyChangedBase
{
    private CalibrationCalculator calibrationCalculator;

    public CalibrationPointViewModel()
    {
        calibrationCalculator = new CalibrationCalculator();
    }

    public int No { get; set; }

    private int start;

    public int Start
    {
        get { return start; }
        set 
        { 
            start = value;
            NotifyOfPropertyChange(() => Start);
            NotifyOfPropertyChange(() => EstimatedCalibrationTime);
        }
    }

    private int stop;

    public int Stop
    {
        get { return stop; }
        set 
        { 
            stop = value;
            Start = calibrationCalculator.CalculateStart(value);
            NotifyOfPropertyChange(() => Stop);
            NotifyOfPropertyChange(() => EstimatedCalibrationTime);
        }
    }

    private double estimatedCalibrationTime;

    public double EstimatedCalibrationTime
    {
        get 
        {
            estimatedCalibrationTime = calibrationCalculator.CalculateCalibrationTime(Start, Stop);
                return estimatedCalibrationTime; 
        }
        set 
        {
            estimatedCalibrationTime = value;
            NotifyOfPropertyChange(() => EstimatedCalibrationTime);
        }
    }
}

public class CalibrationCalculator
{
    public double CalculateCalibrationTime(int start, int stop)
    {
        var time = (start * 0.7)   (stop * 1.2);
        return time;
    }

    public int CalculateStart(int stop)
    {
        return stop - 1;
    }
}

public class GridViewModel : Screen
{
    public BindableCollection<CalibrationPointViewModel> CalibrationPoints { get; }

    private CalibrationPointViewModel selectedCalibrationPoint;
    public CalibrationPointViewModel SelectedCalibrationPoint
    {
        get => selectedCalibrationPoint;
        set
        {
            selectedCalibrationPoint = value;
            NotifyOfPropertyChange(() => SelectedCalibrationPoint);
        }
    }

    public GridViewModel()
    {
        CalibrationPoints = new BindableCollection<CalibrationPointViewModel>();
            
        CalibrationPoints.Add(new CalibrationPointViewModel() { No = 1, Start = 1, Stop = 2 });
        CalibrationPoints.Add(new CalibrationPointViewModel() { No = 2, Start = 3, Stop = 5 });
        CalibrationPoints.Add(new CalibrationPointViewModel() { No = 3, Start = 6, Stop = 8 });
    }
}
  • Related