Arvind Chourasiya
Arvind Chourasiya

Reputation: 17422

Modify property from ViewModel before binding to ListView

I am binding ItemSource to a ListView using Prism MVVM. This is list Cell

<ListView x:Name="list1" ItemsSource="{Binding StudentList}">
<ListView.ItemTemplate >
    <DataTemplate>
        <ViewCell>
            <StackLayout>
                <Label VerticalTextAlignment="Center"  
                       Text="{Binding StudentName}" />
                <Button x:Name="mybtn"         
                    BindingContext="{Binding Source={x:Reference list1}, Path=BindingContext}" 
                    Command="{Binding BtnTextCommand}" 
                    CommandParameter="{Binding Source={x:Reference mybtn}}"  
                    Text="{Binding BtnText}" />
            </StackLayout>                  
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>
</ListView>

This is Source in ViewModel

private List<Student> _studentList;
public List<Student> StudentList
{
    get { return _studentList; }
    set { SetProperty(ref _studentList, value); }
}

I want to modify Button Text every time for each list item before binding. How can I do it from ViewModel Class to get updated value for BtnText?

Modifying values means if Text has ID value then show 'Present' if null then show 'Absent' etc.

Student Class

public class Student
{
    public Mychildren MyKid { get; set; }
    public string LocalStudentId { get; set; }
    public int? StudentId { get; set; }
    public string StudentName { get; set; }
    public int? AttendanceTypeStatusId { get; set; }
    public int? StudentDailyAttendanceId { get; set; }
    public int? AbsentReasonId { get; set; }
    public string AttendanceTypeStatusCD { get; set; }}
}

Edit:

I have written this logic in Converter, throwing exception in first else part at value

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    string attStatusId = string.Empty;
    //AttendanceTypeStatusId
    if (value != null)
    {
        attStatusId = "Present";
    }
    else
    {
        attStatusId = SISConst.AttendanceResults.FirstOrDefault(i => i.AttendanceTypeStatusID == System.Convert.ToInt32(value)).AttendanceTypeStatusCD;
    }
    if (attStatusId == "Absent")
    {
        return "A";
    }
    else if (attStatusId == "Present")
    {
        return "P";
    }
    else
    {
        return "L";
    }
}

ExcpetionScreenshot

enter image description here

Upvotes: 2

Views: 1284

Answers (3)

OliveJuice
OliveJuice

Reputation: 378

A Converter would be a good solution for this. Easy to create and modify.

Try something like -

<ListView x:Name="list1" ItemsSource="{Binding StudentList}">
<ListView.ItemTemplate >
    <DataTemplate>
        <ViewCell>
            <StackLayout>
                <Label VerticalTextAlignment="Center"  
                       Text="{Binding StudentName}" />
                <Button x:Name="mybtn"         
                    BindingContext="{Binding Source={x:Reference list1}, Path=BindingContext}" 
                    Command="{Binding BtnTextCommand}" 
                    CommandParameter="{Binding Source={x:Reference mybtn}}"  
                    Text="{Binding PresentBool, Converter={StaticResource PresentToString}}" />
            </StackLayout>                  
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>
</ListView>

Then create a new class that implements IValueConverter -

public class PresentToString : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? "Present" : "Absent";
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

To use it as a StaticResource as I have above, you'll need to include it in a relevant resource dictionary. You can put it on the Page/ContentView that this ListView is from -

<ContentView>
    <ContentView.Resources>
        <PresentToString x:Key="PresentToString" />
    </ContentView.Resources>

    <ListView x:Name="list1" ... >
...

And that should be all! If your converter is in a difference namespace, you will have to add the xaml include to the Page/ContentView.

EDIT

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var typedValue = (int)value;
    // Since ints are not nullable, we don't need to do a null check

    switch (typedValue)
    {
        case 1:
            return "P";
        case 2:
            return "L";
        // case 0 handled in default. Removed to avoid redundancy.
        default:
            return "A";
    }
}

Upvotes: 2

Ax1le
Ax1le

Reputation: 6643

I guess your Student model must contain an ID property. Do you mean you want to set the button's text to Present when ID's value isn't null and set it to Absent on the other hand?

If so, you should not change the Button's Binding Context. You could only change the command's source when you want to fire this command in your view model instead of the Student model. Here is my XAML for you referring to:

<ContentPage.Resources>
    <local:IDToStringConverter x:Key="IDToStringConverter"/>
</ContentPage.Resources>
<ContentPage.Content>
    <StackLayout>
        <ListView x:Name="list1" ItemsSource="{Binding StudentList}" HasUnevenRows="True">
            <ListView.ItemTemplate >
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label VerticalTextAlignment="Center" 
                                Text="{Binding StudentName}" />
                            <Button x:Name="mybtn"         
                                Command="{Binding BindingContext.BtnTextCommand, Source={x:Reference list1}}" 
                                CommandParameter="{Binding Source={x:Reference mybtn}}"  
                                Text="{Binding ID, Converter={x:StaticResource IDToStringConverter}}"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage.Content>

The converter implement the effect that returns different strings depending on ID's value:

public class IDToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null)
        {
            return "Present";
        }
        return "Absent";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Updated:

If you want to store some data at runtime, you could create a static class:

public static class UtiClass
{
    public static List<AttendanceStatusModel> AttendanceStatus { set; get; }
}

Set its value before you push to page 2: UtiClass.AttendanceStatus = //.... Then you could access it anywhere else after that setting. i.e. in your converter.

Upvotes: 2

Sid Go
Sid Go

Reputation: 2151

Assuming I understand your question properly, the standard way to do that is to create another get only property which returns Present or Absent conditionally, and then raise property changed event for that custom property when the actual property is changed.

Upvotes: 1

Related Questions