Обновления графического интерфейса Xamarin не выполняются при асинхронности

#c# #android #user-interface #xamarin.forms

#c# #Android #пользовательский интерфейс #xamarin.forms

Вопрос:

Я пытаюсь создать простой графический интерфейс с помощью Xamarin и сталкиваюсь со всевозможными проблемами. Это эксперимент, поэтому код немного уродливый, но в основном у меня есть 2 страницы; Главная страница и страница настроек. На главной странице я хочу выйти и выполнить вызов REST и заполнить список (в настоящее время жестко заданный). Все шло нормально, пока я не решил выполнить вызов Rest как асинхронную задачу. Теперь topLabel не будет обновляться, listview тоже не будет (хотя, если я нажму на него, это произойдет). и кнопка не будет переключать страницы. Многое из этого для меня ново, но это похоже на игру в whack a mole. Я решаю 1 проблему, а еще 4 перестают работать. Я уверен, что мне не хватает какой-то фундаментальной вещи. Любая помощь приветствуется.

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"
             x:Class="TheButton.MainPage">
    <StackLayout Spacing="1">
    <Label x:Name="topLabel" TextColor="{Binding TopTextColor}" Text="{Binding itsTopText}" BackgroundColor="LightSlateGray" HorizontalTextAlignment="Center"
           FontSize="Title" HorizontalOptions ="FillAndExpand" Padding="10,10,30,10" HeightRequest="50"/>
    
    <StackLayout Spacing="5" VerticalOptions="FillAndExpand">
         <ListView x:Name="soundsListView" ItemsSource="{Binding itsButtonSounds}" Margin="5"
                   ItemSelected="OnListViewItemSelected"
                   ItemTapped="OnListViewItemTapped"
                   RowHeight="70">
                   
             <ListView.ItemTemplate>
                 <DataTemplate >
                     <ViewCell >
                         <Grid BackgroundColor="{Binding LEDColor}" Padding="1">
                             <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                        <Label VerticalTextAlignment="Center" HorizontalTextAlignment="Start" VerticalOptions="CenterAndExpand" Grid.Column="1" Grid.Row="0"
                           Text="{Binding Name}" FontSize="Title"
                           FontAttributes="Bold" />
                        <Label Grid.ColumnSpan="3"
                           Grid.Column="2"
                           Text="{Binding Description}"
                           VerticalOptions="StartAndExpand" VerticalTextAlignment="Center"/>
                         </Grid>
                     </ViewCell>
                 </DataTemplate>
             </ListView.ItemTemplate>
        </ListView>
         <StackLayout Spacing="5" Orientation="Horizontal" HeightRequest="120" VerticalOptions="Center" HorizontalOptions="Center">
        <Button Text="Add Sound" FontSize="45" Clicked="Handle_Clicked" VerticalOptions="Fill" HorizontalOptions="Fill"/>
             </StackLayout>
    </StackLayout>
        </StackLayout>
</ContentPage>
 

И главная страница

     namespace TheButton
    {
        public partial class MainPage : ContentPage
        {
            public string whatever = "[ {  "Name": "hello", "ImageUrl": "./Sounds/hello.mp3",  "color": [255,0,255], "Description":"hello!", "order": 1  }]";
            public ObservableCollection<ButtonSound> itsButtonSounds { get; private set; }
            public ButtonSound tappedItem;
            public bool connectedToModule;
            public string itsTopText { get; set; }
            public Color TopTextColor { get; set; }
            public MainPage()
            {
                BindingContext = this;
                InitializeComponent();
                connectedToModule = false;
                Console.WriteLine("HI!!!!!");
                itsButtonSounds = new ObservableCollection<ButtonSound>();
                TopTextColor = Color.Red;
                itsTopText = "CONNECTING... Please Wait!";
                Console.WriteLine("Connection");
                Task.Run( connectToModule);
                Console.WriteLine("Done Connection");
                
                
                MessagingCenter.Subscribe<SettingPage, ButtonSound>(this, "Hi", (itsSender, butt) =>
                {
                    Console.WriteLine("HI!!!!!");
                    if (tappedItem== null)
                    {
                        itsButtonSounds.Add(butt);
                    }
                    else
                    {
                        var index = itsButtonSounds.IndexOf(tappedItem);
                        itsButtonSounds[index] = butt;
                    }
                });
            }
            int count = 0;
    
    
    
    
            void Handle_Clicked(object sender, System.EventArgs e)
            {
                if (connectedToModule ==true)
                {
                    tappedItem = null;
                    SettingPage comingPage = new SettingPage();
                    comingPage.SetToButtonSound();
                    Navigation.PushModalAsync(comingPage);
                }
                else
                {
                    DisplayAlert("Not Connected!", "Please put Button into Connection mode by holding the options button for 3 seconds", "OK");
                }
            }
    
    
    
            public async Task connectToModule()
            {
                var client = new RestClient("http://raspberrypi:5000");
                var request = new RestRequest("con", Method.GET);
                
                while (true)
                {
                    Console.WriteLine("REST REQUIESSTT");
                    var response = client.Execute(request);
                    if (response.StatusCode == System.Net.HttpStatusCode.OK amp;amp; response.Content == "accept")
                    {
                        connectedToModule = true;
                        Console.WriteLine($"CONNECTED! OMGOMG!{connectedToModule}");
                        InitButtonSounds();
                        Console.WriteLine(response.Content);
                        
                        Console.WriteLine(connectedToModule == true);
                        break;
                    }
                    Thread.Sleep(500);
                }
            }
    
    
    
        public void InitButtonSounds()
            {
                
                //string fileName = "TheButton.data.json";
                //var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "data.json");
                //Console.WriteLine(backingFile);
                //string jsonString = File.ReadAllText(backingFile);
                Console.WriteLine("Nonesense! initbuttons!");
                TopTextColor = Color.LightCyan;
                itsTopText = "Connected To... Button1";
                var jsonButtons = JsonConvert.DeserializeObject<List<ButtonSound>>(whatever);
                foreach( ButtonSound bs in jsonButtons)
                    {
                    bs.LEDColor = Color.FromRgb(bs.color[0], bs.color[1], bs.color[2]);
                    Console.WriteLine(bs.Name);
                    itsButtonSounds.Add(bs);
                }
    
                itsButtonSounds.Add(new ButtonSound
                {
                    Name =  "Hellosss",
                    Description ="C:/SomePath",
                    ImageUrl = "THIS IS REDUNDANT",
                    LEDColor = Color.FromRgb(225,0,0)
    
                  });
                itsButtonSounds.Add(new ButtonSound
                {
                    Name = "bye",
                    Description = "C:/SomePath BLah BLah BLah bladsfgdsfsadhfdshfkadshfkldsafhasdklfdshfdklsafjhdslfka fdsf adsf dsaf adsf h Blah",
                    ImageUrl = "THIS IS REDUNDANT",
                    LEDColor = Color.FromRgb(12, 233, 31)
    
                });
                itsButtonSounds.Add(new ButtonSound
                {
                    Name = "Doot",
                    Description = "C:/SomePath",
                    ImageUrl = "THIS IS REDUNDANT",
                    LEDColor = Color.FromRgb(122, 122, 11)
    
                });
                soundsListView.ItemsSource = itsButtonSounds;
            }
    
            void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)
            {
                ButtonSound selectedItem = e.SelectedItem as ButtonSound;
            }
    
            void OnListViewItemTapped(object sender, ItemTappedEventArgs e)
            {
                tappedItem = e.Item as ButtonSound;
                Console.WriteLine(tappedItem.Name);
                SettingPage comingPage = new SettingPage();
                comingPage.itsButton = tappedItem;
                comingPage.SetToButtonSound();
                Navigation.PushModalAsync(comingPage);
    
    
    
            }
    
    
    
        }
    }
 

Комментарии:

1. docs.microsoft.com/en-us/xamarin/essentials/main-thread

2. Мои конструкторы и все, что не является асинхронной задачей, не выполняются в основном потоке?

3. Сейчас у меня нет времени просматривать ваш код, но обычно, когда проблема заключается в том, что «Пользовательский интерфейс не обновляется», ответ — «MainThread»

4. Перенос содержимого в асинхронные задачи, которые повлияли на графический интерфейс, в основной поток. BeginInvokeOnMainThread(() => Кажется, это исправляет. Однако кажется, что это накладка на плохой дизайн, и я не могу понять, как это исправить должным образом.

5. Замечены две вещи (но не уверены): 1. Вам может понадобиться это Task.Run(async () => в вашем конструкторе. 2. Попробуйте await Task.Delay(500); вместо « Нитки. Sleep(500); «