verybadcat / CSharpMath

LaTeX. in C#. (ported from the wonderful iosMath project).
MIT License
380 stars 62 forks source link

LaTex binding doesn't refresh #148

Open BenjaNapo opened 4 years ago

BenjaNapo commented 4 years ago

2020-07-23 (1) At start it shows well what's in DomandaAttuale.Testo but when DomandaAttuale change I need to modify LaTex in C# side because with binding it doesn't work. To resolved this I tried in this way: DomandaAttuale.Testo = DomandaAttuale.Testo.Clone() as string; but didn't worked

I noticed the same issue when I load ItemSource of a ListView that contains math:TextView, the first time works but the others shows empty cells

FoggyFinder commented 4 years ago

You have to set Mode = TwoWay explicitly:

LaTeX="{Binding DomandaAttuale.Testo, Mode=TwoWay, Converter={StaticResource UrlDecoder}}"
BenjaNapo commented 4 years ago

Thanks, now it works on the main text but I'm still having the issue on the ListView (using Label it didn't happen) Screenshot_1595516294

FoggyFinder commented 4 years ago

Can you show code that related to binding to the ListView ? (Both XAML and VM parts)

BenjaNapo commented 4 years ago
              <ListView
                    Margin="0,10,0,0"
                    ItemTapped="ListView_ItemTapped"
                    ItemsSource="{Binding DomandaAttuale.Risposte}"
                    SelectionMode="None"
                    VerticalOptions="Center">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <Grid VerticalOptions="CenterAndExpand">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="1*" />
                                        <ColumnDefinition Width="9*" />
                                    </Grid.ColumnDefinitions>
                                    <ia:Checkbox
                                        Padding="0,3"
                                        FillColor="{DynamicResource PrimaryColor}"
                                        IsChecked="{Binding Selezionata}"
                                        IsCheckedChanged="Checkbox_IsCheckedChanged"
                                        OutlineColor="{DynamicResource PrimaryColor}"
                                        Shape="Rectangle" />
                                    <!--<Label
                                        Grid.Column="1"
                                        Padding="0,3"
                                        Text="{Binding Testo, Converter={StaticResource UrlDecoder}}"
                                        VerticalOptions="Center" />-->
                                    <math:TextView
                                        Grid.Column="1"
                                        LaTeX="{Binding Testo, Converter={StaticResource UrlDecoder}, Mode=TwoWay}"
                                        TextAlignment="Left"
                                        VerticalOptions="Center" />
                                </Grid>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>

The VM is a little bit messed up, that's the part where i change quest to show

 private void AvviaDomanda(int index)
        {
            IndiceAttuale = index;
            DomandaAttuale = Micro.Domande[IndiceAttuale - 1];
            [...other code...]
         }
FoggyFinder commented 4 years ago

Does DomandaAttuale notify about its change?

BenjaNapo commented 4 years ago
public partial class Domanda : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        [JsonProperty("id")]
        public long Id { get; set; }

        [JsonProperty("tipologia")]
        public string Tipologia { get; set; }

        [JsonProperty("testo")]
        public string Testo { get; set; }

        [JsonProperty("tempo_massimo")]
        public int TempoMassimo { get; set; }

        [JsonProperty("immagine")]
        public string Immagine { get; set; }
        public int TempoImpiegato { get; set; }
        public int Index { get; set; }

        [...other code...]
    }
public partial class Risposta : INotifyPropertyChanged, ICloneable
    {
        public event PropertyChangedEventHandler PropertyChanged;
        [JsonProperty("id")]
        public long Id { get; set; }

        [JsonProperty("testo")]
        public string Testo { get; set; }

        [JsonProperty("corretta")]
        public long? Corretta { get; set; }

        [JsonProperty("associa")]
        public string Associa { get; set; }
        public bool Selezionata { get; set; }

        [...other code...]
    }

I'm using the NuGet package Fody.PropertyChanged to notify the changes, so theorically it should works

FoggyFinder commented 4 years ago

I specifically asked about class that contains DomandaAttuale property.

Anyway, here is a simple sample:

ListViewSample.zip

I've used Fody.PropertyChanged as well.

BenjaNapo commented 4 years ago

Sorry the, did you mean these files?

Modulo.cs

public partial class IstanzaMicro : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        [JsonProperty("id")]
        public long Id { get; set; }

        [JsonProperty("domande")]
        public Domanda[] Domande { get; set; }
        public Micro Micro;
        [...various attributes...]
    }

TestPage.xaml.cs

public partial class TestPage : ContentPage
    {
        public IstanzaMicro Micro { get; set; }
        public Domanda DomandaAttuale { get; set; }
        public int IndiceAttuale { get; set; }
        [...various attributes...]
        public TestPage(IstanzaMicro micro)
        {
            InitializeComponent();
            Micro = micro;
            BindingContext = this;
            SetupVisualizers();
            AvviaDomanda(1);
            Micro.TempoImpiegato = 0;
            TimerAlive = true;
            BackButtonClickable = true;
            Console.WriteLine(Micro);
            StartCountdown();
        }

        private void AvviaDomanda(int index)
        {
            IndiceAttuale = index;
            DomandaAttuale = Micro.Domande[IndiceAttuale - 1];
            [...other code...]
        }

        private void ProssimaSchermata(object sender, EventArgs e)
        {
            if (IndiceAttuale < 5)
                AvviaDomanda(++IndiceAttuale);
            else
            {
                [...other code...]
            }
        }

As you can see DomandaAttuale change only on AvviaDomanda, and AvviaDomanda is called twice (on constructor, and in ProssimaSchermata)

Btw thanks for ListViewSample.zip, I noticed that you use RelayCommand, is it necessary to make it work?

FoggyFinder commented 4 years ago

As you can see DomandaAttuale change only on AvviaDomanda, and AvviaDomanda is called twice (on constructor, and in ProssimaSchermata)

But TestPage itself doesn't implement INotifyPropertyChanged interface therefore View doesn't know that something is changed.

For now you can do

public partial class TestPage : ContentPage, INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;
        // the rest of your code

Though I'd suggest you to read some MVVM tutorial and move logic outside of TestPage.xaml.cs.

Btw thanks for ListViewSample.zip, I noticed that you use RelayCommand, is it necessary to make it work?

No, it's unrelated. RelayCommand is a way to react to Button.Click. You can use any other implementation of ICommand interface. I just take the one that I found simplest.

BenjaNapo commented 4 years ago

That's what happen when I add those lines... (even the first time doesn't work)

Screenshot_1595587014 I tried to notify manually in this way:

public partial class TestPage : ContentPage, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private Domanda domandaAttuale;
        public Domanda DomandaAttuale
        {
            get { return domandaAttuale; }
            set
            {
                domandaAttuale = value;
                OnPropertyChanged();
            }
        }
        protected void OnPropertyChanged([CallerMemberName] string domandaAttuale = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(domandaAttuale));
        }

But it still not working

Though I'd suggest you to read some MVVM tutorial and move logic outside of TestPage.xaml.cs.

Yep, you are right, I started use Xamarin with MVC pattern, but I noticed that somethings not works with binding

BenjaNapo commented 4 years ago

And btw I don't understand why till I was using Label it was working, but when I replaced them with math:TextView it worked no more...

FoggyFinder commented 4 years ago

But it still not working

Odd. It should work. Are there are any warnings or binding errors in the Debug.Output? If you put breakpoint to the

domandaAttuale = value;

Does it go there?

BenjaNapo commented 3 years ago

In the end I resolved placing an empty Label as 3rd column:

                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <ViewCell Height="69">
                                    <Grid VerticalOptions="FillAndExpand">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="10*" />
                                            <ColumnDefinition Width="90*" />
                                            <ColumnDefinition Width="1*" />
                                        </Grid.ColumnDefinitions>
                                        <ia:Checkbox
                                            Grid.Column="0"
                                            FillColor="{DynamicResource PrimaryColor}"
                                            IsChecked="{Binding Selezionata}"
                                            IsCheckedChanged="Checkbox_IsCheckedChanged"
                                            OutlineColor="{DynamicResource PrimaryColor}"
                                            Shape="Circle" />
                                        <math:TextView
                                            Grid.Column="1"
                                            LaTeX="{Binding Testo, Converter={StaticResource UrlDecoder}, Mode=TwoWay}"
                                            TextColor="{DynamicResource TextColor}"
                                            VerticalOptions="Center" />
                                        <Label Grid.Column="2" Text="" />
                                    </Grid>
                                </ViewCell>
                            </DataTemplate>
                        </ListView.ItemTemplate>