C# 服务完成后属性未更新

C# 服务完成后属性未更新,c#,xamarin.forms,data-binding,C#,Xamarin.forms,Data Binding,我正试图了解Xamarin.Forms中的数据绑定。我已经阅读了大量的指南并使用了一些示例,现在我正在尝试实现一些我自己的基本绑定 我有一个字符串文件,其中声明了一个空变量: public static class Strings { public static string UserDisplayName; } 在加载my View时,它运行一个异步函数从Azure SQL DB获取数据,然后填充字符串 Strings.UserDisplayName = user.FirstName

我正试图了解Xamarin.Forms中的数据绑定。我已经阅读了大量的指南并使用了一些示例,现在我正在尝试实现一些我自己的基本绑定

我有一个字符串文件,其中声明了一个空变量:

public static class Strings
{
    public static string UserDisplayName;
}
在加载my View时,它运行一个异步函数从Azure SQL DB获取数据,然后填充字符串

Strings.UserDisplayName = user.FirstName;
在我的视图页面中,我已将标签绑定到变量userDisplayNm

编辑:

谢谢你的回复。根据下面的回复,我想我已经接近了,这是我现在得到的,尽管MenuViewModel.LoadAsync抛出了一个由于其保护级别而无法访问的错误,所以我还不能编译来检查它。这就是你的建议吗&关于保护级别问题有什么想法吗

字符串文件:

public static class Strings
{
    public static string UserDisplayName;
}
视图模型:

namespace Travel_Comp.ViewModels
{
    public sealed class MenuViewModel : INotifyPropertyChanged
    {
        //Azure sync process
        ServerManager manager;
        public event PropertyChangedEventHandler PropertyChanged;


        public MenuViewModel()
        {
            //Initial set of UserDisplayNm 
            this.UserDisplayNm = Strings.UserDisplayName;
        }

        async void LoadAsync()
        {
            try
            {
                //Run process to populate Strings.UserDisplayNm, set Syncitems to true to sync with Server
                foreach (var user in await manager.GetUsersAsync(syncItems: true))
                {
                    Strings.UserDisplayName = user.FirstName;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"Error while retrieving user name: {e}");
            }
        }

        public string UserDisplayNm
        {
            set
            {
                if (Strings.UserDisplayName != value)
                {
                    value = Strings.UserDisplayName;

                    if (PropertyChanged != null)
                    {
                        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserDisplayNm)));
                    }
                }
            }
            get
            {
                return "Welcome, " + Strings.UserDisplayName;
            }
        }

    }
}
视图:


因此,如果您正在寻找有关MVVM的指导,您应该知道,通常您会将依赖项放在视图模型构造函数中,这里是Azure服务

此外,您还可以使用现有的MVVM框架,使事情变得简单,如或

但是,如果您想要获得完整的版本,您也可以从查看代码隐藏中调用您的vm代码

因此,我建议对MenuViewModel进行以下修改:

namespace Travel_Comp.ViewModels
{
    public sealed class MenuViewModel : INotifyPropertyChanged
    {
        //Azure sync process
        ServerManager manager;
        public event PropertyChangedEventHandler PropertyChanged;


        public MenuViewModel()
        {
            //Initial set of UserDisplayNm 
            this.UserDisplayNm = Strings.UserDisplayName;
        }

        async void LoadAsync()
        {
            try
            {
                //Run process to populate Strings.UserDisplayNm, set Syncitems to true to sync with Server
                foreach (var user in await manager.GetUsersAsync(syncItems: true))
                {
                    Strings.UserDisplayName = user.FirstName;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"Error while retrieving user name: {e}");
            }
        }

        public string UserDisplayNm
        {
            set
            {
                if (Strings.UserDisplayName != value)
                {
                    value = Strings.UserDisplayName;

                    if (PropertyChanged != null)
                    {
                        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserDisplayNm)));
                    }
                }
            }
            get
            {
                return "Welcome, " + Strings.UserDisplayName;
            }
        }

    }
}
然后在您的视图中查看代码隐藏:

void OnAppearing()
{
    _menuViewModel.LoadAsync();
}
要解决以下问题:由于其保护级别而无法访问,可以尝试在LoadAsync函数之前添加公共访问修饰符

我创建了一个简单的演示来模拟你的代码

主要代码是:

 public sealed class TestModel: INotifyPropertyChanged
{

 //*******************************************
    string _userDisplayName;

    public string UserDisplayName {
        set { SetProperty(ref _userDisplayName, value); }

        get { return _userDisplayName; }
    }

    public async void LoadAsync()
    {
        try
        {
            UserDisplayName = "updated value: Angela";

            Strings.UserDisplayName = UserDisplayName;
        }
        catch (Exception exception)
        {
            Debug.WriteLine($"Error while retrieving user name: {exception}");
        }
    }


    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
其效果是:

Strings.UserDisplayName类需要实现INotifyPropertyChanged,然后您需要一个字符串实例,但您不能,因为它是静态的……让我们来看看。我已经编辑了我的原始问题,以纳入我希望的建议,但它仍然抛出错误,因此我还无法编译以进行检查。这是否符合你的建议?我理解正确了吗?另外,值得补充的是,我不想把它放在一个静态字符串中,这只是我处理它的方式。我很乐意绑定一个基本属性,这样当同步运行或失败时,它只显示欢迎,当它完成时,它会更新为显示欢迎,Stephen
private IAzureService _azureService;
private string _userDisplayNm;

public MenuViewModel(IAzureService azureService)
{
    _azureService = azureService;
}

public string UserDisplayNm
{
    get
    {
        return _userDisplayNm;
    }
    set
    {
        if (_userDisplayNm != value)
        {
            _userDisplayNm = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserDisplayNm)));
        }
    }
}

public async void LoadAsync()
{
    try
    {
        UserDisplayNm = await _azureService.GetUserName();
    }
    catch (Exception exception)
    {
        Debug.WriteLine($"Error while retrieving user name: {exception}")
    }
}
void OnAppearing()
{
    _menuViewModel.LoadAsync();
}
public async void LoadAsync(){
   //....
  }
 public sealed class TestModel: INotifyPropertyChanged
{

 //*******************************************
    string _userDisplayName;

    public string UserDisplayName {
        set { SetProperty(ref _userDisplayName, value); }

        get { return _userDisplayName; }
    }

    public async void LoadAsync()
    {
        try
        {
            UserDisplayName = "updated value: Angela";

            Strings.UserDisplayName = UserDisplayName;
        }
        catch (Exception exception)
        {
            Debug.WriteLine($"Error while retrieving user name: {exception}");
        }
    }


    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
 <Label Text="{Binding UserDisplayName }" BackgroundColor="Yellow" 
        VerticalOptions="Center" HorizontalOptions="Fill"  HeightRequest="50" />
 <Button  Text="update the Label value"  Clicked="Button_Clicked"/>
public partial class MyPage1 : ContentPage
{
    TestModel model;

    public MyPage1 ()
    {
        InitializeComponent ();

        model = new TestModel();
        BindingContext = model;       
    }

    private void Button_Clicked(object sender, EventArgs e)
    {
        model.LoadAsync();
    }
}