1 Свойство не обновляется Привязка форм Xamarin - PullRequest
1 голос
/ 23 марта 2020

Одно свойство [ TripTotal] на моей ContentPage в формах Xamarin привязано к ярлыку На экране не происходит обновление. Я перешел к установщику свойств и обнаружил, что это «Уведомление». См. Оператор Console.WriteLine ($ "{TripTotal}") ниже. Он обновляет OneTime при запуске. 994 В чем здесь проблема?

From Application output

TripTotal 100

TripTotal 101

TripTotal 102

TripTotal 103

TripTotal 104

TripTotal 105

TripTotal 106

TripTotal 107

TripTotal 108

TripTotal 109

TripTotal 110



    <?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" x:Class="TripCalculator.Views.ExpensePage"
             Title="{Binding Title}" x:Name="BrowseItemsPage">
    <ContentPage.ToolbarItems>
        <ToolbarItem Text="Add" Clicked="AddItem_Clicked" />
    </ContentPage.ToolbarItems>
    <StackLayout Padding="10">
        <StackLayout Orientation="Horizontal" >
            <Label Text="TripTotal:" FontSize="Title"/>
            <Label Text="{Binding TripTotal, Mode=TwoWay, StringFormat ='${0:F2}'}" x:Name="trip_total" FontSize="Title" HorizontalOptions="End"/>
            <Label Text="{Binding TripTotal_str, Mode=TwoWay}"  FontSize="Title" HorizontalOptions="End"/>
        </StackLayout>
        <BoxView BackgroundColor="Black" HeightRequest="2" WidthRequest="500"/>
        <ListView x:Name="ItemsListView"
                  ItemsSource="{Binding SubTotals}"
                  VerticalOptions="FillAndExpand"
                  HasUnevenRows="true"
                  RefreshCommand="{Binding LoadItemsCommand}"
                  IsPullToRefreshEnabled="true"
                  IsRefreshing="{Binding IsBusy, Mode=OneWay}"
                  CachingStrategy="RecycleElement"
                  ItemSelected="OnItemSelected">  
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Padding="10">
                            <Label Text="{Binding StudentName}"
                                   LineBreakMode="NoWrap"
                                   Style="{DynamicResource ListItemTextStyle}"
                                   FontSize="Subtitle" />
                            <Label Text="{Binding Amount, StringFormat ='${0:F2}'}"       
                                   Style="{DynamicResource ListItemDetailTextStyle}"
                                   FontSize="Subtitle"  />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

enter image description here

Вот ViewModel

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using TripCalculator.Models;
using Newtonsoft.Json;
using Xamarin.Forms;
using System.Windows.Input;
using TripCalculator.Helpers;
using TripCalculator.Views;

namespace TripCalculator.ViewModels
{
    public class ExpenseVM : BaseVM
    {
        public ExpenseVM()
        {
            Title = "Students";
            Expenses = new ObservableCollection<Expense>();
            Messages = new ObservableCollection<string>();
            SubTotals = new ObservableCollection<SubTotal>();
            StudentTotals = new ObservableDictionary<string, double>();
            Expenses.CollectionChanged += (s, e) => TripTotal += 1;
            MessagingCenter.Subscribe<NewExpensePage, Expense>
                (this, "AddItem", async (obj, item) =>
            {
                var newItem = item as Expense;

                await DataSource.AddItemAsync(newItem);

                await this.ExecuteLoadItemsCommand();
            });

        }

        #region OBSERVABLES
        public ICommand LoadItemsCommand
        {
            get
            {
                return new Command (async() => await ExecuteLoadItemsCommand());
            }
        }

        public ICommand UpdateItemCommand
        {
            get
            {
                return new Command<object>(async (id) => await ExecuteUpdateItemCommand(id));
            }
        }

        public Command DeleteCommand
        {
            get
            {
                return new Command (async () => await ExecuteDeleteCommand());
            }
        }

        private Expense selectedItem;
        public Expense SelectedItem {   get => selectedItem;
                                        set => SetProperty(ref selectedItem, value); }
        private ObservableCollection<Expense> expenses;
        public ObservableCollection<Expense> Expenses
        {
            get => expenses;
            set => SetProperty(ref expenses, value);
        }

        private ObservableCollection<SubTotal> subtotals;
        public ObservableCollection<SubTotal> SubTotals
        {
            get => subtotals;
            set => SetProperty(ref subtotals, value);
        }

        public ObservableCollection<string> Students { get { return QueryDistinctNames(); } }

        public ObservableCollection<string> Messages; 

        private ObservableDictionary<string, double> studentTotals;
        public ObservableDictionary<string, double> StudentTotals
        {
            get => studentTotals;
            internal set => SetProperty(ref studentTotals, value);
        }

        double tripTotal = 99;
        public double TripTotal
        {
            get => tripTotal;
            set => SetProperty(ref tripTotal, value);

        }

        public List<string> ExpenseIds { get { return queryAllExpenseIds(); } }

        private void reQueryStudents()
        {
            var studList = Students;
            foreach (string student in studList)
            {
                studentTotals[student] = QueryStudentTotal(student);
            }
            //set Notification cyccle
            this.SubTotals.Clear();
            foreach (var k in studentTotals.Keys)
            {
                subtotals.Add(new SubTotal { Amount = studentTotals[k], StudentName = k });

            }
            //TripTotal = QueryTotal();
            SubTotals = subtotals;

            StudentTotals = studentTotals;

        }

        public double QueryStudentTotal(string student)
        {
            return (from exp in Expenses select exp)
                                .Where(e => e.StudentName == student)
                                .Sum(e => e.Amount);
        }

        internal List<string> queryAllExpenseIds()
        {
            var lst = Expenses.ToList<Expense>();
            // Left As Distinct() although there should be no redundant Id's
            return lst.Select(x => x.Id).Distinct().ToList();
        }

        private double QueryTotal()
        {
            return (from exp in Expenses select exp).Sum(e => e.Amount);
        }

        private ObservableCollection<string> QueryDistinctNames()
        {
            var lst = Expenses.ToList<Expense>();
            List<string> myStudents = lst.Select(x => x.StudentName).Distinct().ToList();
            return myStudents.ToObservableCollection<string>();

        }
        #endregion

        #region CMDS_AND_DATASTORE_OPS
        async Task ExecuteLoadItemsCommand()
        {
            if (IsBusy)
                return;

            IsBusy = true;

            try
            {
                Expenses.Clear();
                if (DataSource != null)
                {
                    var items = await DataSource.GetItemsAsync(true);
                    double sum = 0;
                    foreach (var item in items)
                    {
                        Expenses.Add(item);
                        sum += item.Amount;
                    }
                    reQueryStudents();
                    tripTotal = sum;
                    OnPropertyChanged(nameof(TripTotal));
                }else
                { Console.WriteLine("null Data Store"); }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            finally
            {
                IsBusy = false;
            }
        }

        async Task ExecuteUpdateItemCommand(object newAmt)
        {
            if (IsBusy)
                return;

            IsBusy = true;
            try
            {
                double newCost = (double)newAmt;

                Expense newExpense = new Expense
                {
                    Amount = newCost,
                    StudentName = SelectedItem.StudentName
                };


                var didUpdate = await DataSource.UpdateItemAsync(SelectedItem, newExpense);
                if (didUpdate)
                {  
                    await ExecuteLoadItemsCommand();
                }    
            }
            catch
            {

            }
        }

        private async Task ExecuteDeleteCommand()
        {
            await DataSource.DeleteItemAsync(SelectedItem.Id);
            await ExecuteLoadItemsCommand();
        }
        #endregion

        #region UTIL
        public override string ToString()
        {
            var serializedItem = JsonConvert.SerializeObject(Expenses);
            return serializedItem;
        }

        public void FromString(string json)
        {
            // Hold your hat a bunch of conversions ahead :)
            Expenses = JsonConvert.DeserializeObject<IEnumerable<Expense>>(json)
                                   .ToList()
                                   .ToObservableCollection<Expense>();

        }
        #endregion
    }
}

Вот BaseViewModel

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

using TripCalculator.Models;
using TripCalculator.Services;

namespace TripCalculator.ViewModels
{
    public class BaseVM
    {
        protected static IDataSource<Expense> DataSource
        {
            get { return App.DataSource;  }
        }

        bool isBusy = false;
        public bool IsBusy
        {
            get { return isBusy; }
            set { SetProperty(ref isBusy, value); }
        }

        string title = string.Empty;
        public string Title
        {
            get { return title; }
            set { SetProperty(ref title, value); }
        }

        #region INotifyPropertyChanged

        protected bool SetProperty<T>(ref T backingStore, T value,
            [CallerMemberName]string propertyName = "",
            Action onChanged = null)
        {
            if (EqualityComparer<T>.Default.Equals(backingStore, value))
                return false;

            backingStore = value;
            onChanged?.Invoke();
            OnPropertyChanged(propertyName);
            if (propertyName=="TripTotal")
            {
                Console.WriteLine($"TripTotal {value}");
            }
            return true;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;

            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }
}

Ответы [ 2 ]

1 голос
/ 23 марта 2020

О, глупый я! Я нашел это. Мне нужно было только добавить INotifyPropertyChanged в BaseVM. Да, у меня была рабочая реализация, но я не сказал Xamarin, что отправляю уведомления. Я ничего не изменил. Жизнь программиста. О, и другие поля обновлялись, потому что они были привязаны к ObservableCollecitons

1 голос
/ 23 марта 2020

В ExecuteLoadItemsCommand () вам нужно сделать это TripTotal = sum; вместо этого tripTotal = sum;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...