How to Bind DataGrid to dynamically changeable dat

2019-08-30 23:12发布

问题:

I´m quite new to WPF and I want to show in my UserControl something like this: Sample Image

I get data from different sources:

Columns:

ID: always present

Problem 1 ...n: I have an XML-file like this one

from which I want to get the Columns for the Problems. There can be a variable number of Problems.

Grade: always present

Rows:

Each Row should only contain Textboxes (some are read-only for the user).

The Data in the ID Column comes from a file.

The Values for the Problems are input by the user.

The Data under Grade will be calculated.

The input values for the Problems and the calculated value for Grade are related to the ID.

I tried to implement this by using a DataGrid and setting up DataGridTemplateColumn for the Problem Columns but was not really successful. I think this would be the right control in this case, but I can´t really figure out how to set this up.

I thought about building this "Table" manually, by adding Labels and Textboxes for the "Problems" depending on the Data. But then my ViewModel should know something about the View which violates MVVM.

Thanks and best regards.

EDIT: Thanks, @Ivan Furdek for your help so far! I got to show now each textbox and column for the Problems and can edit them. But my Collection won´t be updated -.- so my List<double> PointsPerProblems stays on the intialized values

Here is my XAML:

<ItemsControl ItemsSource="{Binding PointsPerProblems, Mode=TwoWay, 
             UpdateSourceTrigger=PropertyChanged}">                                            
         <ItemsControl.ItemsPanel>
             <ItemsPanelTemplate>
                 <StackPanel Orientation="Horizontal"/>
             </ItemsPanelTemplate>
         </ItemsControl.ItemsPanel>
         <ItemsControl.ItemTemplate>
             <DataTemplate>
                 <Border Width="70">
                     <TextBox Text="{Binding Path=., Mode=TwoWay,
                      UpdateSourceTrigger=PropertyChanged}"
                      TextAlignment="Center"/>
                 </Border>
             </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

My Model:

public class GradingModel
{
    public string MatriculationNumber { get; set; }

    public List<double> PointsPerProblems { get; set; }

    public double Grade { get; set; }

    public double TotalScore { get; set; }
}

And the specific parts of my ViewModel:

public ObservableCollection<GradingModel> Gradings
    {
        get
        {
            return this.gradings;
        }

        set
        {
            this.gradings = value;
            this.OnPropertyChanged(nameof(this.Gradings));
        }
    }

public List<string> ProblemList
    {
        get
        {
            return this.problemList;
        }

        set
        {
            this.problemList = value;
            this.OnPropertyChanged(nameof(this.ProblemList));
        }
    }
private void GetGradingForm()
    {
        ExamRatingDTO examRatingModel = new ExamRatingDTO();
        List<StudentDTO> students = new List<StudentDTO>();

        this.messageBoxService.ShowInfoMessage(
            "Please Select a xml-File containing the Exam Ratings.",
            "Select a Exam Rating File.");
        try
        {
           examRatingModel = this.fileDialogService.OpenLoadFileDialog<ExamRatingDTO>();
        }
        catch (InvalidOperationException ex)
        {
            this.messageBoxService.ShowErrorMessage("Please select a correct Exam Rating File.", ex);
        }

        this.messageBoxService.ShowInfoMessage(
            "Please Select a xml-File containing the Students Information.",
            "Select a Student Information File.");
        try
        {
            students = this.fileDialogService.OpenLoadFileDialog<List<StudentDTO>>();
        }
        catch (InvalidOperationException ex)
        {
            this.messageBoxService.ShowErrorMessage("Please select a correct Students File.", ex);
        }

        foreach (var student in students)
        {
            this.Gradings.Add(new GradingModel()
            {
                MatriculationNumber = student.MatriculationNumber.ToString(),
                PointsPerProblems = new List<double>(),
                Grade = 0.0,
                TotalScore = 0.0
            });
        }

        List<string> tmpProblemList = new List<string>();

        foreach (var problem in examRatingModel.PointsPerProblems)
        {
            tmpProblemList.Add(problem.ProblemName);
        }

        foreach (var grading in this.Gradings)
        {
            for (int i = 0; i < tmpProblemList.Count; i++)
            {
                grading.PointsPerProblems.Add(0.0);
            }
        }

        this.ProblemList = tmpProblemList;
    }

EDIT

Ok found the Solution for the last problem here in 2nd edit of the answer

WPF: How to make DataGrid binding with dynamic columns editable?

回答1:

You'll need to parse the xml into a list of objects like

    public class Student 
    { 
        public int Id { get; set; } 
        public List<decimal> ProblemScores { get; set; } 
        public DecimalGrade
        { 
            get
            {
               return ProblemScores.Average();
            }
        } 

After that I suggest you follow this method to get the required display: https://blogs.msmvps.com/deborahk/populating-a-datagrid-with-dynamic-columns-in-a-silverlight-application-using-mvvm/

Make sure to use two way binding, and UpdateSourceTrigger = PropertyChanged so the changes get propagated back to the list.

The columns for id and score should be have their IsReadOnly property set to true.