简体   繁体   中英

Xamarin.Forms, XAML, Bindings and isVisible

I am (obviously, regarding the question) new to this, so thanks in advance for any help! As I am trying my current best on this for hours now, I hope, someone can accelerate my learning curve :)

Addendum: I also posted on the Xamarin Forum, however this seems to be rather dead regarding the amount of answers to questions. Therefore I hope no one takes offense.

So, I have my XAML MainPage:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="cant post links"
             xmlns:x="cant post links"
             xmlns:local="clr-namespace:App.ViewModels"
             x:Class="App.ViewModels.MainPage">

    <ContentPage.BindingContext>
        <local:User/>
    </ContentPage.BindingContext>

    <StackLayout>
        <Label Text="Test"/>
        <ListView ItemsSource="{Binding Changes}"
                  x:Name="ChangesList"
                  HasUnevenRows="True"
                  Margin="40,80"
                  ItemTapped="ListView_ItemTapped">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label Text="{Binding Name}"/>
                            <Button Text="Details"
                                    IsVisible="{Binding IsVisible}"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Button Text="Press to add" x:Name="addItem" Clicked="addItem_Clicked"/>
    </StackLayout>

</ContentPage>

I have a datamodel with the correponding attributes behind. When I leave the MainPage class constructor like the following:

public MainPage()
{
    InitializeComponent();
}

I see no list items.

So, first Question: I thought, based on the bindings, the app would automatically generate a User instance and assign it to the list?

As I can see with the debugger, a User instance actually is generated and also assigned to the BindingContext, but the ChangesList.ItemsSource remains null.

Therefore, I changed the code of the constructor:

public MainPage()
{
    InitializeComponent();
    var bc = BindingContext;
    var list = ChangesList.ItemsSource;
    ChangesList.ItemsSource = (bc as User).Changes;
}

This lets me see a list with items when I compile and run the app. Also, I can add new items to the list via the code behind the 'addItem' Button. However: Nothing happens, when I tap an item. The code looks like follows:

private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
    var change = e.Item as Change;
    change.IsVisible = !change.IsVisible;
}

and the Data methods look as follows:

 public class Change : INotifyPropertyChanged
{
    public string Name { get; set; }
    bool isVisible = false;

    public event PropertyChangedEventHandler PropertyChanged;

    public bool IsVisible
    {
        get
        {
            return isVisible;
        }
        set
        {
            isVisible = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Change-IsVisible"));
        }
    }
}

Therefore the second question: Why does the Button not change its visibility status and become visible?

Thank you very much in advance!

Wolf

Edit: Added the complete "Change" Class Code.

Edit 2: The User Code.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.ComponentModel;
using Xamarin.Forms;
using System.Diagnostics;

namespace App.ViewModels
{
    public class User : INotifyPropertyChanged
    {
        public string Username { get; set; }
        public ObservableCollection<Change> Changes = null;   
        public event PropertyChangedEventHandler PropertyChanged;

        public User()
        {
            Changes = new ObservableCollection<Change>();
            addChange("Test 1");
            addChange("Test 2");

        }

        public void addChange(string text)
        {
            Change c = new Change
            {
                Name = text,
                IsVisible = false
            };
            Changes.Add(c);
            refresh();
        }

        public void refresh()
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Changes"));
        }
    }
}

For the 2nd question - try:

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsVisible"));

For the first question - can you please share your User source code

Try the following:

Change the getters/setters in your User class to:

public string Username
{
    get { return username;}
    set { username = value; OnPropertyChanged("Username");
}
private string username = string.Empty;

public ObservableCollection<Change> Changes
{
    get { return changes; }
    set { changes = value; OnPropertyChanged("Changes");
}
private ObservableCollection<Change> changes = null;

Add a new method to the class: public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }

Then make sure that you implement the modified code above accordingly in your Changes class.

Finally in your MainPage class constructor set the binding context of the page:

public MainPage(User user)
{
    InitializeComponent();
    BindingContext = user;
}

Some explanation: The binding context has to contain your datamodel classes, in your case User.

What you are doing with

var bc = BindingContext;
ChangesList.ItemsSource = (bc as User).Changes;

is trying to cast the unset BindingContext of your MainPage to User, which will be empty.

So in order to use the BindingContext in your Xaml, it MUST be set to an instance of your user class or else there is no data to be shown on the screen.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM