Właściowść klasy a wywoływanie settera

0

Witam. Jestem początkujący zarówno w c# jak i w xamarin. W ramach nauki przerabiam sobie pewien kurs i natrafiłem na użycie "Application Properties". O ile działanie rozumiem to zastanowiła mnie obsługa tego przy wykorzystaniu właściwości klasy wraz seterem i geterem.
Więc na początek kod w głównej klasie aplikacji:
(pominięta ta część kodu co nie istotna dla pytania)

public partial class App : Application
    {
        private const string TitleKey = "Name";
        private const string NotificationEnabled = "NotificationEnabled";
        public App()
        {
            InitializeComponent();

            MainPage = new AppProperties();
           
        }
        public string Title
        {
            get
            {
                if (Properties.ContainsKey(TitleKey))
                    return Properties[TitleKey].ToString();
                return "";
            }
            set
            {
                Properties[TitleKey] = value;
            }
        }

        public bool NotificationsEnabled
        {
            get
            {
                if (Properties.ContainsKey(NotificationEnabled))
                    return (bool)Properties[NotificationEnabled];
                return false;
            }

            set
            {
                Properties[NotificationEnabled] = value;
            }
        }
    }

Code-behind klasy "AppProperties":

[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class AppProperties : ContentPage
    {
        public AppProperties()
        {
            InitializeComponent();
            BindingContext = Application.Current;
        }
    }

I kod XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="link"
             xmlns:x="link"
             x:Class="X_kurs.AppProperties">
    <TableView Intent="Form">
        <TableRoot>
            <TableSection>
                <EntryCell Text="{Binding Title}" Label="Title" Placeholder="Add Title"/>
                <SwitchCell Text="Notification" On="{Binding NotificationsEnabled}"/>
            </TableSection>
        </TableRoot>
    </TableView>
</ContentPage>

I kod działa tak jak trzeba.
A moje pytanie jest odnośnie setterów dla właściwości w głównej klasie, czyli "NotificationsEnabled" i "Title".

Kiedy jest wywoływany ten setter? O ile z getterem sprawa wydaje mi się prosta (wywoływany jest w momencie gdy parser w xaml natrafia na odpowiednią bindowaną właściwość) to jak to się ma z setterem? Czy "value" zmienia swoją wartość za każdym razem gdy zmienia się wartość Bindowana w xaml? Czy wartość value jest pobierana dopiero w momencie zapisu do "Application Properties".
Tak jak pisałem jestem nowy w c# a wcześniej pisałem w JS i PHP i tam setterów i getterów bezpośrednio we właściwościach klasy nie było. Dlatego pytam się jak to tu działa.

Z góry dziękuję za odpowiedź.

Pozdrawiam.

0

setter wywoływany jest wtedy, kiedy coś próbujesz zapisać do właściwości.
Nie wiem, czy w Xamarin SwitchCell i EntryCell działają "normalnie", ale pewnie tak - więc w zasadzie ta wartość w obiekcie nigdy się nie zmieni po zmianach w widoku - w przypadku kodu, który pokazujesz. Dlaczego? Bo domyślnie Binding jest OneWay, znaczy tylko z właściwości do kontrolki - zmiana właściwości wpływa na kontrolkę, ale w drugą stronę już nie. Do tego potrzeba trybu TwoWay:

<SwitchCell Text="Notification" On="{Binding NotificationsEnabled, Mode=TwoWay}"/>
0

No ale właśnie się zmienia. Po zmianach w widoku kiedy wpiszę nowy tekst i zamknę aplikację, to nowa wartość jest zapisywana do pamięci. Bo ta nowa wartość pojawia się po ponownym włączeniu aplikacji. Więc chyba bindowanie jest "TwoWay". Czy się mylę?

0

Na to wygląda, że domyślnie jest TwoWay, bo i tak jest w innym przykładzie w internecie. Więc działa tak jak - setter wywoływany po zmianie wartości powiązanej.

Było późno i nie myślałem chyba trzeźwo, bo TwoWay jest domyślnym dla wielu właściwości wielu kontrolek przecież, a EntryCell wygląda na coś, co miało by sens w takim trybie ;)

0

Ok, dzięki za odpowiedź.
Dalej tylko jednej rzeczy nie rozumiem.
Dla testów dodałem kolejną kontrolkę, która wskazuje na tą samą właściwość w klasie.

<Label Text="{Binding Title}"/>

Myślałem że tekst w tej kontrolce będzie automatycznie uaktualniany gdy w kontolce

<EntryCell Text="{Binding Title}" Label="Title" Placeholder="Add Title"/>

Będą wprowadzane zmiany. Niestety tak się nie dzieje. Dodałem też modyfikacje "Mode=TwoWay", ale nic to nie zmieniło.
Więc dlaczego wartość w "Label" nie jest uaktualniana?
Czy może ja dalej źle rozumuję?

Dziękuję.

0

Wynika to z tego, że niestety języki programowania to nie magia. Skąd Label ma wiedzieć, że ma się zaktualizować, bo "Title" się zmieniło? Trzeba kontrolkę o tym powiadomić. Potrzebujesz do tego interfejsu INotifyPropertyChanged, który wymaga, że klasa go implementująca posiada event informujący o tym, że property o pewnej nazwie się zmieniło. I potem kontrolki powiązane nasłuchują, czy aby się ich wartość powiązania nie zmieniła i w razie potrzeby się aktualizują.

Klasa Application w Xamarin.Forms na szczęście implementuje już INotifyPropertyChanged (bo dziedziczy po bodaj Element), poprzez logicznie nazwaną metodę OnPropertyChanged wywołując ten event, stąd wystarczy, że zrobisz w swoim kodzie jedną prostą zmianę:

public string Title
{
    get
    {
        if (Properties.ContainsKey(TitleKey))
            return Properties[TitleKey].ToString();
        return "";
    }
    set
    {
        Properties[TitleKey] = value;
        OnPropertyChanged("Title"); // chyba można też bez jawnego podawania nazwy zmienianej właściwości, bo sobie weźmie z CallerMemberName
    }
}

I będzie działać :)

0

Dla mnie programowanie to magia:). Tak jak magią dla mnie jest że robię "pstryk" na ścianie a w pokoju robi się jaśniej :P. No magia Panie.
A tak na poważnie to dzięki za wyjaśnienie. Może głupie to było co zapytałem ale dopiero zaczynam i nie wszystko jest jasne.

Pozdrawiam.

0

Nie ma problemu.
Musisz pamiętać, że jeżeli będziesz robił własne klasy i do nich bindował, to one muszą implementować INotifyPropertyChanged, aby to działało w taki sposób.

Ale tak, programowanie na pewnym etapie to nadal magia ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1