我试图在Xamarin.Forms中更新一个标签,但它破坏了整个应用程序。

问题描述 投票:0回答:1

我的应用程序的主页面上有一个标签,应该是每十五秒更新一次,但它只更新一次,之后,很多事情都停止工作。例如,如果我在标签更新后尝试打开一个新页面,页面的标题就会被画在与后退按钮相同的位置(这两个按钮都是Xamarin在工具栏中生成的),而且页面的内容根本无法加载。另外,我在页面上有一个ListView,如果我尝试选择一个项目(应该是打开一个新的页面),只有第一次能成功,之后ListView就消失了,但是选中的项目后面出现的橙色框框却一直在那里。

目前标签的工作原理是,我在App类中有一个计时器,它从一个列表中随机选择一段文本,我在App的OnStart()函数中加载了这段文本(这部分工作正常),然后启动一个应该更新标签的事件。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Timers;
using System.Reflection;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

using Partylist.Views;
using Partylist.Models;

namespace Partylist
{
    public partial class App : Application, INotifyPropertyChanged
    {
        // Variable to store the currently selected event.
        public static Event selectedEvent;
        // Variable to store the currently selected list.
        public static PartylistList selectedList;
        // Struct to store information about tips.
        public struct Tip
        {
            // A short version of the tip for the banner at the bottom of the screen.
            public string Summary { get; set; }
            // The full tip, which you can read by clicking the "More" button in the banner.
            public string Full { get; set; }
        }
        // Array of tips.
        public List<Tip> tips = new List<Tip>();
        // Current tip.
        public Tip CurrentTip { get; set; }
        // Timer that gets the tip to update.
        public Timer tipTimer = new Timer(15000);
        // Random number generator for choosing the tip.
        public Random rand = new Random();
        // Event that tells the tip banners on the pages to update.
        public static event EventHandler TipUpdate;

        // Constructor.
        public App()
        {
            // Do whatever initialization stuff this does.
            InitializeComponent();

            // Subscribes the timer's event handling function to its event.
            tipTimer.Elapsed += OnTimerElapsed;

            // Open the first page: the list of events.
            MainPage = new NavigationPage(new EventsPage()) {
                BarTextColor = Color.FromHex("FF4081")
            };
        }

        // Loads tips data.
        private void LoadTips()
        {
            // Variable for the assembly.
            var assembly = IntrospectionExtensions.GetTypeInfo(typeof(App)).Assembly;
            // Variable for the stream I use to read the text file.
            Stream tipsStream = assembly.GetManifestResourceStream("Partylist.Resources.tips.txt");
            // And a variable for the StreamReader.
            StreamReader tipsReader = new StreamReader(tipsStream);
            // Read the whole file into the list of tips.
            while (!tipsReader.EndOfStream)
            {
                // Read a line into a "sumamry" variable.
                string sum = tipsReader.ReadLine();
                // Read another line into a "full" variable.
                string full = tipsReader.ReadLine();
                // Add an item to the list of tips that uses "summary" as the summary 
                // and "full" as the full tip.
                tips.Add(new Tip()
                {
                    Summary = sum,
                    Full = full
                });
            }
            // Random index of the chosen tip.
            int index = rand.Next(tips.Count);
            // Set the current tip as the tip at that index.
            CurrentTip = tips.ElementAt(index);
            // Start timer (if it needs it).
            tipTimer.Start();
        }

        // Event handling function for when the timer goes off.
        private void OnTimerElapsed(object source, ElapsedEventArgs e)
        {
            // Random index of the chosen tip.
            int index = rand.Next(tips.Count);
            // Set the current tip as the tip at that index.
            CurrentTip = tips.ElementAt(index);
            // Fire the event to update the pages' tip banners.
            TipUpdate?.Invoke(this, e);
        }

        // Standard lifecycle events.
        protected override void OnStart()
        {
            // Call a function that loads the tips.
            LoadTips();
        }

        protected override void OnSleep()
        {
        }

        protected override void OnResume()
        {
        }
    }
}

在页面的OnAppearing()方法中,我将标签的文本设置为当前的提示(此时是空的),并将更新标签的函数订阅到定时器发射的事件中。

using Partylist.Models;

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Partylist.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class EventsPage : ContentPage
    {
        // Text of the tip banner.
        public string BannerText { get; set; }

        // List of events, used to populate 
        // the page's ListView (see the XAML).
        public ObservableCollection<Event> EventList { get; set; }

        // Constructor.
        public EventsPage()
        {
            // Does all the stuff to make the page
            // exist that doesn't involve anything 
            // specific to this particular page in
            // this particular app.
            InitializeComponent();
            // Set the label's BindingContext to the 
            // App class so it can update its text.
            tipLabel.BindingContext = (App)App.Current;
        }

        // Runs when the page appears.
        protected override void OnAppearing()
        {
            // Call the regular OnAppearing method.
            base.OnAppearing();

            // Set the BindingContext of the page to itself.
            BindingContext = this;

            // Update the ListView.
            UpdateListView();

            // Set the banner's text to the current tip's sumamry.
            tipLabel.Text = ((App)App.Current).CurrentTip.Summary;
            OnPropertyChanged("CurrentTip");

            // Subscribe the OnTipUpdate function to the tipUpdate event in the app
            // class.
            App.TipUpdate += OnTipUpdate;
        }

        // Function to update the ListView whent he page loads or when something changes.
        private void UpdateListView()
        {
            // Set the EventList to a new ObservableCollection
            // which will be populated.
            EventList = new ObservableCollection<Event>();

            // Loop to populate the ObservableCollection.
            for (int i = 0; i < Directory.GetDirectories(
                Environment.GetFolderPath(
                    Environment.SpecialFolder
                    .LocalApplicationData))
                .Length; i++)
            {
                // Add a new event.
                EventList.Add(new Event()
                {
                    // Set the folder name to the name of the folder 
                    // that the even corresponds to.
                    FolderName = new DirectoryInfo(Directory.GetDirectories(
                    Environment.GetFolderPath(
                        Environment.SpecialFolder
                        .LocalApplicationData))[i]).Name,
                    // Sets the date/time created to the folder's
                    // creation date.
                    DateCreated = Directory
                    .GetCreationTime(Directory.GetDirectories(
                    Environment.GetFolderPath(
                        Environment.SpecialFolder
                        .LocalApplicationData))[i]),
                    // Sets the date/time last edited to the 
                    // folder's write date.
                    DateEdited = Directory
                    .GetLastWriteTime(Directory.GetDirectories(
                    Environment.GetFolderPath(
                        Environment.SpecialFolder
                        .LocalApplicationData))[i])
                });
                // Set the ItemsSource of the ListView in the 
                // XAML to the ObservableCollection.
                EventsListView.ItemsSource = EventList;
                // Calls OnPropertyChanged() which makes the ListView update.
                OnPropertyChanged("EventList");
            }
        }

        // Function to go to the "New Event" page.
        async void OnNewEventClicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync(new NewEventPage());
        }

        // Function for when a ListView item is selected.
        async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            App.selectedEvent = (Event)e.SelectedItem;
            await Navigation.PushAsync(new ListsPage());
        }

        // Function to delete an event if the "Delete" context action is selected.
        async void OnDelete(object sender, EventArgs e)
        {
            // Represents the thing to be deleted.
            var del = (MenuItem)sender;
            // Displays a confirmnation popup and stores the user's answer in a variable.
            var answer = await DisplayAlert("Delete this event?", 
                "Are you sure you want to delete the event: \"" + 
                ((Event)del.CommandParameter).FolderName + "\"?", "Delete", "Cancel");
            // If the user accepted, delete the event with the MenuItem that ran this function.
            if (answer)
            {
                Directory.Delete(Path.Combine(Environment.GetFolderPath(
                    Environment.SpecialFolder.LocalApplicationData), 
                    ((Event)del.CommandParameter).FolderName), true);
                // Set the ItemsSource to null and back to make the ListView update.
                EventsListView.ItemsSource = null;
                UpdateListView();
            }
        }

        // Function for when the current tip updates.
        public void OnTipUpdate(object sender, EventArgs e)
        {
            // Make the label's text update.
            tipLabel.Text = ((App)App.Current).CurrentTip.Summary;
            OnPropertyChanged("CurrentTip");
        }
    }
}

另外,这里是页面的XAML,以防有什么问题。

<?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="Partylist.Views.EventsPage"
             Title="Events"
             BackgroundColor="White">
    <ContentPage.ToolbarItems>
        <ToolbarItem IconImageSource="settings_gear.png"
                     Priority="0"/>
    </ContentPage.ToolbarItems>

    <ContentPage.Content>
        <!--Main layout of the page-->
        <StackLayout>
            <!--ListView of the events-->
            <ListView x:Name="EventsListView"
                      ItemSelected="OnItemSelected">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <!--These contewxt actions are buttons that appear
                            when you long press the item (Android) or swipe 
                            left (iOS).-->
                            <ViewCell.ContextActions>
                                <MenuItem Clicked="OnDelete"
                                          CommandParameter="{Binding .}"
                                          Text="Delete"
                                          IsDestructive="true"/>
                            </ViewCell.ContextActions>
                            <!--This is the content that actually appears-->
                            <StackLayout Padding="20,5">
                                <Label Text="{Binding FolderName}"
                                       TextColor="#FF7700"
                                       FontSize="Large"/>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

            <!--"New Event" button-->
            <Button Text="+ Add New Event"
                    TextColor="#ff418b"
                    FontSize="Large"
                    BackgroundColor="#00ffffff"
                    Clicked="OnNewEventClicked"/>

            <!--The banner at the bottom of the screen that gives tips-->
            <Frame BorderColor="#ff418b"
                   Padding="0">
                <FlexLayout Direction="Row"
                            AlignItems="Stretch"
                            JustifyContent="SpaceBetween">
                    <!--The "Tip" icon-->
                    <Image Source="tip_icon.png"
                           Margin="10"
                           FlexLayout.Basis="50"/>
                    <!--The short version of the tip-->
                    <Label x:Name="tipLabel"
                           VerticalTextAlignment="Center"
                           TextColor="#bb0099"
                           FontSize="Medium"
                           FontAttributes="Bold"
                           FlexLayout.Basis="250"/>
                    <!--The button that opens up a screen 
                    with tyhe rest of the tip-->
                    <Button Text="More"
                            TextColor="White"
                            FontAttributes="Bold"
                            FontSize="Medium"
                            BackgroundColor="#ff418b"
                            FlexLayout.Basis="100"/>
                </FlexLayout>
            </Frame>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

我到底做错了什么,我怎么才能让我的应用在标签更新时不崩溃?

events xamarin.forms label
1个回答
1
投票

你需要更新 Main thread:

Device.BeginInvokeOnMainThread (() => {
  label.Text = "Async operation completed";
});

请参考。xamarin.forms.device.beginvokeonmainthread。

© www.soinside.com 2019 - 2024. All rights reserved.