A problem in identifying an object that contains a list of another object after casting that is a base class in the Binding in UserControl

Elad Yehuda

Member
Joined
Jul 3, 2023
Messages
10
Programming Experience
3-5
I have a problem, that I am trying to access an object that is itself a list
of objects.
The structure is like this, I have some class called Client and that class has a property Matched_ which itself contains a list of questionnaires, each questionnaire has a list of questions.

Because of the requirements of the project, I split the Question class and decided to create a basic entity BaseQuestionEntity that will contain only one property of Id, and the Question class inherits from it and adds a property that saves the content of the question as a QuestionText string. Now I also have another QuestionAnswers class that inherits from BaseQuestionEntity and should keep a list of "answers" not as a string but only the Id of each answer.
At the beginning of the program I read an xml file which converts it into a list of Questionnaires according to the list of QuestionAnswers which saves me the answers for each question according to each questionnaire! The content is saved in a list property called AnswersQuestionnaires that I initially specified in the Matched_ object that exists in every client object (sounds a bit complicated but the full code continues), during the program in a certain window I transfer a client, in the exact same window to the constructor, in the xaml itself I make Bindings to the properties of that client for any fields, in addition I make a special Binding of the AnswersQuestionnaires property in twoway mode, the same UserControl
I have defined special functions of dependency properties for the binding, and when the function is activated that should receive the QuestionAnswers property as it is and I am trying to make a conversion from one element of the list of List<BaseQuestionEntity> to
QuestionAnswers I just get null! And I don't understand why this is happening?
In the window itself I do see that for a certain client the list is in the form of QuestionAnswers and not and I can access every element of it!
Here is the full code:

C#:
// all class
[AddINotifyPropertyChangedInterface]
public class Questionnaire
{
    public int Id { get; set; }
    public List<BaseQuestionEntity> Questions { get; set; }
}

public class BaseQuestionEntity
{
   public int Id { set; get; }
}

public class Question : BaseQuestionEntity
{
   public string QuestionText { get; set; }
}

public class QuestionAnswers: BaseQuestionEntity
{
  public HashSet<int> IdAnswers { get; set; }
}

[AddINotifyPropertyChangedInterface]
[Serializable]
public class Client
{
   private DateTime? selectedDateOfBirth;
   private string hebrewDateOfBirth;
   public string Id { get; set; }
  //.....
   public Matched Matched { get; set; }
}
[AddINotifyPropertyChangedInterface]
public class Matched
{
     public List<Questionnaire> AnswersQuestionnaires { get; set; }
}

//....
//...
public static async Task<Client> GetClient(string clientInfoFilePath)
{
    // DeserializeFromXml is read file xml of Client
    Client client = DeserializeFromXml<Client>(clientInfoFilePath);
    string folder = $"FolderClients/{client.PathFolder}";
    string matchedFilePath = Path.Combine(folder, "AnswerClientQuestionnaires.xml");
    if (client.Matched_ != null && File.Exists(matchedFilePath))
    {
        client.Matched_.AnswersQuestionnaires = await Task.Run(() => ReadClientAnswerQuestionnaireAsync(matchedFilePath));
    }
}

// function to read file xml
public static async Task<List<Questionnaire>> ReadClientAnswerQuestionnaireAsync(string filePath)
{
  try
  {
      if (!File.Exists(filePath))
      {
          return null;
      }

      XDocument xmlDoc = await Task.Run(() => XDocument.Load(filePath));

      var questionnaires = xmlDoc.Descendants("Questionnaire").Select(q =>
      {
            var questionnaireId = int.Parse(q.Attribute("Id").Value);
            var questions = q.Descendants("Question").Select(qs =>
            {
               var questionId = int.Parse(qs.Attribute("Id").Value);
               var answers = new QuestionAnswers
               {
                  Id = questionId,
                  IdAnswers = new HashSet<int>(qs.Descendants("AnswerImage").Select(a => int.Parse(a.Value)))
               };
               return answers;
            }).Cast<BaseQuestionEntity>().ToList();
              return new Questionnaire { Id = questionnaireId, Questions = questions };
                                                                      }).ToList();
      return questionnaires;
  }
    catch (Exception ex)
    {
        Console.WriteLine($"Error occurred while reading XML file: {ex.Message}");
        return null;
    }
}
// Window


public partial class CreatNewClient : CustomBaseWindow
{
    private Client cl, oldClient;
    public CreatNewClient()
    {
       InitializeComponent();
    }
    public CreatNewClient(Client oldClient)
    {
        //...
        //...
        // here te get list QuestionAnswers properly
          var listQuestionAnswers = oldClient.Matched_.AnswersQuestionnaires[0].Questions.OfType<QuestionAnswers>().ToList();
          InitializeComponent();
    }
    //...
    //..
}

//xaml of CreatNewClient
<custom:CustomBaseWindow x:Class="PMV.Views.CreatNewClient"
 xmlns:uc="clr-namespace:PMV.Components"
    //....
>
<Grid>
<!--...-->
<uc:QuestionnairePanel x:Name="ucQuestionnairePanel"  ParentWindow="{Binding RelativeSource={RelativeSource AncestorType=Window}}" ClientSerialNumber="{Binding SerialNumber, Mode=TwoWay}" ClientAnswers="{Binding Matched_.AnswersQuestionnaires, Mode=TwoWay}"  GenderClient="{Binding Gender, Mode=TwoWay}"/>
</Grid>
</custom:CustomBaseWindow>


// UserControl QuestionnairePanel
public partial class QuestionnairePanel : UserControl
{
    List<Questionnaire> questionnairesCurrent;
    int indexQuestionnaire, indexQustion;
    List<Questionnaire> clientAnswers;
//...

    public static readonly DependencyProperty ClientSerialNumberProperty = DependencyProperty.Register("ClientSerialNumber", typeof(string),
        typeof(QuestionnairePanel), new FrameworkPropertyMetadata(null, ClientSerialNumberPropertyChanged));

    public static readonly DependencyProperty GenderClientProperty = DependencyProperty.Register("GenderClient", typeof(bool?),
         typeof(QuestionnairePanel), new FrameworkPropertyMetadata(null, GenderClientPropertyChanged));

    public static readonly DependencyProperty ClientAnswersProperty = DependencyProperty.Register("ClientAnswers", typeof(List<Questionnaire>),
          typeof(QuestionnairePanel), new FrameworkPropertyMetadata(null, ClientAnswersPropertyChanged));

    public static readonly DependencyProperty ParentWindowProperty = DependencyProperty.Register("ParentWindow", typeof(Window), typeof(QuestionnairePanel), new PropertyMetadata(null));

    private static void ClientSerialNumberPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is QuestionnairePanel qustionPanel)
        {
             qustionPanel.ClientSerialNumber = (string)e.NewValue;
        }
     }

    private static void ClientAnswersPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
       if (d is QuestionnairePanel qustionPanel)
       {
          var temp = (List<Questionnaire>)e.NewValue;
          if (temp != null && temp != qustionPanel.clientAnswers)
          {
             qustionPanel.clientAnswers = temp;
             // here lisQuestionAnswers is list empty after casting!
             var lisQuestionAnswers = temp[0].Questions.OfType<QuestionAnswers>().ToList();
          }
             qustionPanel.buMatch.Visibility = Visibility.Visible;
        }
    }
    public Window ParentWindow
    {
       get { return (Window)GetValue(ParentWindowProperty); }
       set { SetValue(ParentWindowProperty, value); }
    }

    public string ClientSerialNumber
    {
       get { return (string)GetValue(ClientSerialNumberProperty); }
       set { SetValue(ClientSerialNumberProperty, value); }
    }


    public bool? GenderClient
    {
          get { return (bool?)GetValue(GenderClientProperty); }
        set { SetValue(GenderClientProperty, value); }
    }

    public List<Questionnaire> ClientAnswers
    {
        get { return (List<Questionnaire>)GetValue(ClientAnswersProperty); }
        set { SetValue(ClientAnswersProperty, value); }
    }
// The rest of the code
// ...

}
 
I would suggest temporarily changing this line:
C#:
var lisQuestionAnswers = temp[0].Questions.OfType<QuestionAnswers>().ToList();
to
C#:
var listQuestionAnswers = new List<QuestionAnswers>();
foreach(var item in temp[0].Questions)
{
    if (item is QuestionAnswers qa)
        listQuestionAnswers.Add(qa);
}

and then like previously set a breakpoint and then step through the code.

Is the foreach loop iterating over all the Questions? Or is that not even returning any items at all?

If the loop is iterating, are any items of type QuestionAnswers found?
 
Hey Skydiver
I tried what you suggested but even this way it simply returns null, I also see in the debug mode that the list itself is BaseQuestionEntity and not QuestionAnswers!
What's wrong is that I'm at a break point in the constructor of the window (and not in the UserControl) the list is QuestionAnswers! (I am attaching screenshots here in both places, in the window and in the UserControl)
This is an interesting problem, to understand why the UserControl does not know how to perform casting.
In any case, I have another way that worked for me before I decided to split the Question class into a base class and another class QuestionAnswers, only this solution is much less readable, and a bit cumbersome, but it is a solution that 100% does the job for me.

UserControl_.jpeg
Window_.jpeg

 
I wonder if temp[0].Questions is even the same instance as oldClient.Matched_.AnswersQuestionnaires[0].Questions.
 
This is indeed an instance of it, AnswersQuestionnaires is a list of Questionnaire
that the list of questions is the same list of BaseQuestionEntity.
Suppose I were to pass the list of the Questionnaire to the constructor of the UserControl instead of binding I would receive the list contained as QuestionAnswers and not BaseQuestionEntity and then the casting would work as expected
 
Last edited:
By same instance, I mean reference/pointer equality, not just type equality.
 
Yes, this is the same property by ref just as I do with other properties in such a binding, although the list of questionnaires is a relatively complex property than any other property.
However, I will look into it more deeply. Thank you
 
Back
Top Bottom