For my current project I am working on a cross-platform application. We are building this using Visual studio, cross platform-app. In this project we use questionaires which we set in an SQL Database.
Using MVVM I can load this dynamically into the mobile app. (We do this because there are many platforms where we show this, so we choose to set it in the back-end, the questionaire with the answers to each question).
But with saving there seems to be an issue, I can't seem to send data back to my back end -> I want to send either the value or the string showed from the picker back to the back end (preferably using MVVM). If I can get the data in the viewmodel (or code behind if MVVM would not be possible), i would be helped enough with my question.
I build my questionaire using a collectionview which contains a DataTemplate with a picker inside.
QuestionaireView.xaml:
<ContentPage.BindingContext>
<viewModels:Questionaire_viewmodel/>
</ContentPage.BindingContext>
<CollectionView
x:Name="CollectionQuestionsView"
SelectionMode="None"
Margin="0,0,0,0" RemainingItemsThreshold="5"
BackgroundColor="Transparent"
SelectedItems="{Binding selectedPickers}"
ItemsSource="{Binding Questionaire.Vraag,Mode=TwoWay}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Style="{StaticResource RegularLabelStyleSmallLogin}" Text="{Binding vraagstelling}" HorizontalTextAlignment="Start" VerticalTextAlignment="Start" LineBreakMode="WordWrap"/>
<Picker Style="{StaticResource PickRegularSmall}" SelectedItem="{Binding vraag_id}" x:Name="{Binding vraag_id}" ItemsSource="{Binding Antwoorden}" ItemDisplayBinding="{Binding antwoord_vraagstelling}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Frame Margin="0,0,0,20" Style="{StaticResource OrangeLoginFrame}">
<Grid Margin="-15" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Style="{StaticResource SemiBoldLabelStyleMedium}" Grid.Row="0" Grid.Column="0" Text="Opslaan"/>
<Button Style="{StaticResource LoginButton}" Command="{Binding SaveVragenlijstCompetentiesCommand}" Grid.Row="0" Grid.Column="0" />
</Grid>
</Frame>
<Button x:Name="questionaireButton" Command="{Binding getQuestionaireLoopbaancompetenties}" IsVisible="False"/>
So to explain the bindings (since some names are not English):
QuestionaireViewModel.cs
public string vraag_127 { get; set; }
public string vraag_128 { get; set; }
... and so on
public string vraag_161 { get; set; }
public string categorie_vraag { get; set; }
public string SelectedAnswer { get; set; }
public string vraagstelling { get; set; }
public string vraag_id { get; set; }
public string score_antwoord { get; set; }
public string antwoord_vraagstelling { get; set; }
public string default_question { get; set; }
public string description { get; set; }
private Questionaire_model _questionaire;
public Questionaire_model Questionaire
{
get { return _questionaire; }
set
{
_questionaire = value;
OnPropertyChanged();
}
}
private List<Questionaire_model> _vraag;
public List<Questionaire_model> Vraag
{
get { return _vraag; }
set
{
_vraag = value;
OnPropertyChanged();
}
}
private List<Questionaire_model> _antwoorden;
public List<Questionaire_model> Antwoorden
{
get { return _antwoorden; }
set
{
_antwoorden = value;
OnPropertyChanged();
}
}
public ICommand getQuestionaireLoopbaancompetenties
{
get
{
return new Command(async (boolean) =>
{
Questionaire = await _apiServices.GetQuestionaireQuestions("Competenties");
});
}
}
private ObservableCollection<vragenModel> _selectedPicker;
public ObservableCollection<vragenModel> SelectedPicker
{
get
{
return _selectedPicker;
}
set
{
_selectedPicker = value;
OnPropertyChanged();
}
}
public ICommand SaveVragenlijstCompetentiesCommand
{
get
{
return new Command(async () =>
{
var responseBool = await _apiServices.SaveCultuur(vraag_127,vraag_128, ... vraag_161)});
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
And my QuestionaireModel.cs:
public class Questionaire_model
{
public int id { get; set; }
public string default_question { get; set; }
public string description { get; set; }
public IList<Vragen> Vraag{ get; set; }
public class Vragen
{
public string categorie_vraag { get; set; }
public string SelectedAnswer { get; set; }
public string vraagstelling { get; set; }
public string vraag_id { get; set; }
public IList<antwoorden> Antwoorden { get; set; }
}
public class antwoorden
{
public string score_antwoord { get; set; }
public string antwoord_vraagstelling { get; set; }
}
}
The reason we use an collectionView, is because we use the template for various questionaires with various amount of questions and answer possibilities.
So at this moment, when I execute my save action, it gives "null" in my ViewModel - action
I have defined public class Questionaire_viewmodel : INotifyPropertyChanged
above my viewmodel
So I hope someone can either help me with my code, that I missed something here Or if I have to change something because it is simply impossible well then I would like to have an direction where to go to.
As I understand, you want your Picker.SelectedItem
actually bind to QuestionaireViewModel.vrag_xxx
properties. That won't work like this - what now happens is that your Picker.SelectedItem
is bound to Vragen.vraag_id
field and is not able to use it, as long as it expects antwoorden
type there.
However, I see that you have SelectedAnswer
property in Vragen
, you can change it to be of antwoorden
type and then fetch user choice from that field.
So, if your Vragen
class is:
public class Vragen
{
public string categorie_vraag { get; set; }
public antwoorden SelectedAnswer { get; set; }
public string vraagstelling { get; set; }
public string vraag_id { get; set; }
public IList<antwoorden> Antwoorden { get; set; }
}
Then, you can change your binding for Picker
to:
<Picker Style="{StaticResource PickRegularSmall}" SelectedItem="{Binding SelectedAnswer}" x:Name="{Binding vraag_id}" ItemsSource="{Binding Antwoorden}" ItemDisplayBinding="{Binding antwoord_vraagstelling}"/>
And, at last in SaveVragenlijstCompetentiesCommand
you'll be able to get array of answers (sorted by question id):
Questionaire.Vraag.OrderBy(x => x.vraag_id).Select(x => x.SelectedAnswer.antwoord_vraagstelling)
That will give you array of answer texts. Still it doesn't conform to your apiServices.SaveCultuur
call. I would strongly recommend to change that method - method that contains a lot of parameters is certainly a problem.
If you can change it to accept string[]
, then you will be able to pass Questionaire.Vraag.OrderBy(x => x.vraag_id).Select(x => x.SelectedAnswer.antwoord_vraagstelling)
to it.
If you can't change apiServices
, there's still possibility to call the method through reflection using array, but it's a bit tricky.