【问题标题】:Xamarin Forms -> Activity Indicator not working if Commands of statements to be executedXamarin Forms - > Activity Indicator not working if statements of statements to be executed
【发布时间】:2018-09-06 07:40:14
【问题描述】:

使用 Visual Studio 2017 社区 15.8.1

这是在查看了有关 ActivityIndi​​cator 的 stackoverflow 的所有选项之后。因此,尽管这可能是重复的,但没有什么能帮助我。 所以最终决定发布我的锻炼并从这里获得最好的帮助。

我到目前为止所尝试的:-
1. {Binding IsLoading} + INotifyPropertyChanged + public void RaisePropertyChanged(string propName) + IsLoading = true;概念。
2. ActivityIndi​​cator_Busy.IsVisible = false; (直接控制访问)

这两种方法大多被推荐,我在过去几周里深入研究了每一种方法。但没有任何问题。

我取得了什么成就?:-
ActivityIndi​​cator_Busy.IsVisible = false;只有当我在执行语句之前返回时,概念才能顺利运行(用于测试目的); Button Clicked 事件的声明。 (附图) 但是,一旦我删除了退货;按下按钮后,在暂停一段时间后,主页会立即打开。

我的问题:-
1. 这对于当前场景来说是特别的,当用户点击欢迎按钮时,如何让 ActivityIndi​​cator 立即运行。
2. 同样,当应用程序启动时,还有一个空白的白屏,持续几秒钟,几乎 30 秒,我也想显示 ActivityIndi​​cator。但不知道如何在哪个实例上强加该逻辑。

我的意见

我的 MainPage.xaml 文件:- (编辑于 2018 年 9 月 6 日晚上 9 点 11 分)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Name="page_main_page"
             NavigationPage.HasBackButton="False"
             NavigationPage.HasNavigationBar="False"
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:appNutri"
             BindingContext="{x:Reference page_main_page}"
             x:Class="appNutri.MainPage">
    <ContentPage.Content>
        <StackLayout BackgroundColor="White" 
                     HorizontalOptions="FillAndExpand" 
                     VerticalOptions="FillAndExpand">
            <StackLayout>
                <Image x:Name="Image_Welcome" 
                       Source="welcome.png" 
                       HorizontalOptions="CenterAndExpand" 
                       VerticalOptions="CenterAndExpand"
                       WidthRequest="300" 
                       HeightRequest="300" />
                <Button x:Name="Button_Welcome" 
                        Clicked="Button_Welcome_Clicked" 
                        Text="Welcome!" 
                        BackgroundColor="DeepSkyBlue" 
                        HorizontalOptions="CenterAndExpand" 
                        VerticalOptions="CenterAndExpand"
                        TextColor="White" 
                        HeightRequest="60" />
            </StackLayout>
            <StackLayout BackgroundColor="White" 
                         HorizontalOptions="FillAndExpand" 
                         VerticalOptions="FillAndExpand">
                <ActivityIndicator 
                        x:Name="ActivityIndicator_Busy"
                        Color="Black"
                        IsEnabled="True" 
                        HorizontalOptions="Center" 
                        VerticalOptions="Center"
                        IsRunning="{Binding Source={x:Reference page_main_page}, Path=IsLoading}" 
                        IsVisible="{Binding Source={x:Reference page_main_page}, Path=IsLoading}" />
            </StackLayout>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

我的 MainPage.cs 代码:-
(于 2018 年 9 月 6 日晚上 9 点 13 分编辑)

using appNutri.Model;
using SQLite;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace appNutri
{
    public partial class MainPage : Xamarin.Forms.ContentPage, INotifyPropertyChanged 
    {

        private bool isLoading;

        public bool IsLoading
        {
            get
            {
                return isLoading;
            }

            set
            {
                isLoading = value;
                RaisePropertyChanged("IsLoading");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propName)
        {           
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));

            }
        }

        public MainPage()
        {
            InitializeComponent();
            BindingContext = this;
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            BindingContext = this;
        }

        protected async void Button_Welcome_Clicked(object sender, EventArgs e)
        {
            IsLoading = true;

            await Select_Local_User_Information();

            IsLoading = false;
        }

        private async Task Select_Local_User_Information()
        {

            IsLoading = true;

            string where_clause = "";

            try
            {
                Sql_Common.Database_Folder_Path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);

                string Database_Full_Path = Path.Combine(Sql_Common.Database_Folder_Path, Sql_Common.Database_Name);

                SQLiteConnection connection = new SQLiteConnection(Database_Full_Path);

                //connection.DropTable<User_Master>();
                //connection.Delete(connection.Table<User_Master>());
                //connection.CreateTable<User_Master>(CreateFlags.ImplicitPK | CreateFlags.AutoIncPK);

                connection.CreateTable<User_Master>();

                int count = connection.ExecuteScalar<int>("Select count(*) from User_Master");

                if (count == 0)
                {
                    connection.DropTable<User_Master>();
                    connection.CreateTable<User_Master>();

                    //IsLoading = false;
                    //IsBusy = false;

                    await Navigation.PushAsync(new User_Register_Page());

                }
                else
                {
                    Sql_Common.User_Logged = true;

                    var Local_User_Data = connection.Table<User_Master>().ToList();

                    User_Master.Logged_User_Details_Container.First_Name = Local_User_Data[0].First_Name;
                    User_Master.Logged_User_Details_Container.Cell1 = Local_User_Data[0].Cell1;

                    where_clause = " Upper ( First_Name ) = " + "'" + User_Master.Logged_User_Details_Container.First_Name.ToUpper().Trim() + "'" + " and " +
                                   " Cell1 = " + "'" + User_Master.Logged_User_Details_Container.Cell1.Trim() + "'";

                    int records = Sql_Common.Get_Number_Of_Rows_Count("User_Master", where_clause);

                    if (records == 0)
                    {
                        connection.DropTable<User_Master>();
                        connection.CreateTable<User_Master>();

                        IsLoading = false;

                        await Navigation.PushAsync(new User_Register_Page());

                    }
                    else
                    {
                        User_Master.User_Master_Table(where_clause, User_Master.Logged_User_Details_Container);

                        IsLoading = false;

                        await Navigation.PushAsync(new User_Home_Page());

                    }

                }

                connection.Close();
            }
            catch (SQLiteException ex)
            {
                string ex_msg = ex.Message;
            }

            IsLoading = false;
        }
    }
}


2018 年 10 月 4 日
终于用This Answer解决了


【问题讨论】:

  • 您能否将您的代码剥离为minimal reproducible example?如果没有其他任何东西,解决问题会更容易。
  • 附带说明: 您正在手动连接您的 SQL 语句。这是 SQLite,如果用户想要 xe 无论如何都可以访问数据,但如果你在未来某个地方访问了真正的 My-MS-、PostgreSQL 数据库,这将为 SQLi 攻击打开大门。
  • New Query 刚刚提出您的担忧,也是一位用户之前的建议之一。对于相同的更详细的问题和任何解决方案将不胜感激。我想要一个单独的讨论,因此这里不再回复了
  • 另一边注:如果您在RaisePropertyChanged 中使用CallerMemberName 属性,则不必写出属性的名称,但它会自动确定由编译器。
  • 请看看我的更新对你有没有帮助。

标签: xamarin.forms visual-studio-2017


【解决方案1】:

2018 年 9 月 10 日更新

您认为通过将INotifyPropertyChanged 添加到您的类定义并添加事件来实现INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

连同它的事件调用器

public void RaisePropertyChanged(string propName)
{           
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

无论如何,既然ContentPage 已经实现了INotifyPropertyChanged,添加那些没有实现INotifyPropertyChangedContentPage 已经定义了事件(或者更确切地说是 BindableObjectContentPage 间接继承的事件)。任何依赖于页面中属性更改通知的对象都将订阅祖先的PropertyChanged 事件,而不是您定义的PropertyChanged 事件,因此ActivityIndicator 不会更新。

只需删除您定义的事件并调用OnPropertyChanged 而不是RaisePropertyChanged() 就可以了。

private bool isLoading;

public bool IsLoading
{
    get
    {
        return isLoading;
    }

    set
    {
        isLoading = value;
        OnPropertyChanged();
    }
}

由于OnPropertyChanged 被声明为

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)

您不必手动传递属性名称。由于CallerMemberNameAttribute,编译器会为您执行此操作。

结束更新

XAML 扩展 {Binding IsLoading}ActivityIndicator 绑定到页面的 BindingContext。默认情况下,BindingContextnull,因此没有什么可以绑定的,你所有的努力都无济于事。

使用视图模型

首选的解决方案是使用视图模型并将其分配给MainPage.BindingContext,例如

var page = new MainPage()
{
    BindingContext = new MainPageViewModel()
}

但是如果你走那条路,你应该把你所有的 UI 逻辑移到那个视图模型中,并将你的 SQL 访问和业务逻辑封装在其他类中,以保持视图模型不受资源访问和业务的影响逻辑。在代码中包含资源访问和逻辑可能适用于这个小示例,但可能会变成无法维护的混乱。

没有视图模型

无论如何,您没有必须使用视图模型来使用绑定。您可以为页面(或某些子页面)设置BindingContext 或使用BindingExtensionSource

设置BindingContext

BindingContext 从任何页面或视图传递给它的子级。您首先必须为您的页面命名为x:Name="Page"(不必使用Page,无论如何,您不能使用页面的类名)并将BindingContext设置为该页面

<ContentPage ...
    x:Name="Page"
    BindingContext="{x:Reference Page}"
    ...>

现在绑定到IsLoading 应该可以工作了。

Binding 中使用Source

如果您想引用视图的BindingContext 以外的其他内容,BindingExtension 有一个属性Source。您也必须为您的页面命名(见上文)

<ContentPage ...
    x:Name="Page"
    ...>

现在您可以在绑定中引用它

<ActivityIndicator 
    ...
    IsRunning="{Binding Path=IsLoading, Source={x:Reference Page}}" 
    IsVisible="{Binding Path=IsLoading, Source={x:Reference Page}}"/>

【讨论】:

  • 详细感谢您的快速推荐。我按照你的建议实施了。事实上,我已经尽可能多地使用可能的变化来完成这个逻辑超过 3-4 次。但是您的代码对设置 BindingContext 有额外的建议,我在任何地方都没有找到。检查 XAML 中的更新代码以及 cs 文件。可惜没有运气!但是您可能会发现我在 Button Clicked 事件中调用了私有异步任务 Select_Local_User_Information()。等待。是的,但如果我把 return;进行测试,它有效。看看你是否还能找到任何问题。
  • 如果您对我更新的代码有更多的改进意见,我们将不胜感激。可能我认为您提供的详细信息非常接近解决方案。
  • 我将在今天晚些时候更新我的答案。
  • 嘿,我今天来不及了。看看它仍然在我的议程上。
  • @YeshwantMudholkar “是的,但是如果我输入 return; 进行测试,它可以工作。” 是什么意思?您能否展示有效的代码和无效的代码,以便更好地了解问题所在?
猜你喜欢
  • 2022-12-02
  • 2018-05-28
  • 2011-04-20
  • 2014-05-24
  • 2014-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-16
相关资源
最近更新 更多