Xaml ContentView绑定没有';我不能正常工作

Xaml ContentView绑定没有';我不能正常工作,xaml,xamarin,xamarin.forms,Xaml,Xamarin,Xamarin.forms,我有这样一个可恢复控件来显示加载微调器: <?xml version="1.0" encoding="UTF-8"?> <ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014

我有这样一个可恢复控件来显示加载微调器:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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="Framework.Controls.Loading" x:Name="LoadingControl" IsVisible="{Binding LoadingIndicator}"
             HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
  <ContentView.Content>
      <ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Color="DarkBlue" 
                         IsVisible="{Binding LoadingIndicator}"
                         IsRunning="{Binding LoadingIndicator}">
      </ActivityIndicator>
    </ContentView.Content>
</ContentView>
我的控件背后的代码如下所示:

<controls:Loading LoadingIndicator="{Binding IsLoading}"></controls:Loading>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
    public static readonly BindableProperty LoadingIndicatorProperty =
        BindableProperty.Create(
            propertyName: nameof(LoadingIndicator), typeof(bool),
            typeof(Loading), default(string), BindingMode.OneWayToSource);

    public bool LoadingIndicator
    {
        get => (bool)GetValue(LoadingIndicatorProperty);
        set => SetValue(LoadingIndicatorProperty, value);
    }

    public Loading()
    {
        InitializeComponent();
        BindingContext = this;
    }
}
如果IsLoading绑定得到更新,我是否需要编写代码来处理更改

这是我正在使用控件的页面的完整代码:

ItemCreatePage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage Title="{Binding PageTitle}"
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:userControls="clr-namespace:Framework.UserControls"
             xmlns:converters="clr-namespace:Framework.ValueConverters"
             xmlns:controls="clr-namespace:Framework.Controls;assembly=Framework.Android"
             x:Class="Framework.Views.Item.ItemCreatePage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <converters:DoubleConverter x:Key="DoubleConverter"></converters:DoubleConverter>
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.Content>
        <Grid>
            <ScrollView>
                <Grid RowSpacing="0" VerticalOptions="Start">
                  <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                  </Grid.RowDefinitions>

                  <StackLayout Grid.Row="1" Padding="20,20,20,0" VerticalOptions="Start">

                    <Label Text="Category" />
                    <userControls:BindablePicker
                        ItemsSource="{Binding Categories}"
                        SelectedItem="{Binding Path=Item.CategoryName, Mode=OneWay}"
                        DisplayMemberPath="Name"
                        SelectedValuePath="Id"
                        SelectedValue="{Binding Path=Item.CategoryId, Mode=TwoWay}"/>

                    <Label Text="Description" />
                    <Editor Text="{Binding Item.Description}" HeightRequest="100"/>

                    <Label Text="Area"/>
                    <Entry Text="{Binding Item.LineNumber}"/>

                    <Label Text="Identifier"/>
                    <Entry Text="{Binding Item.Identifier}"/>

                    <Label Text="Code"/>
                    <Entry Text="{Binding Item.Code}"/>

                    <Label Text="Priority" />
                    <userControls:BindablePicker
                        ItemsSource="{Binding Priorities}"
                        SelectedItem="{Binding Path=Item.ItemPriority, Mode=OneWay}"
                        DisplayMemberPath="Name"
                        SelectedValuePath="Id"
                        SelectedValue="{Binding Path=Item.ItemPriorityCode, Mode=TwoWay}"/>

                    <Label Text="Owner" />
                    <userControls:BindablePicker
                        ItemsSource="{Binding Users}"
                        SelectedItem="{Binding Path=Item.OwnerName, Mode=OneWay}"
                        DisplayMemberPath="Name"
                        SelectedValuePath="Id"
                        SelectedValue="{Binding Path=Item.OwnerId, Mode=TwoWay}"/>

                    <Label Text="Due Date" />
                    <DatePicker Date="{Binding Item.DateDue}" />

                    <Label Text="Date Identified" />
                    <DatePicker Date="{Binding Item.DateIdentified}" />

                    <Label Text="Status" />
                    <userControls:BindablePicker
                        ItemsSource="{Binding Statuses}"
                        SelectedItem="{Binding Path=Item.Status, Mode=OneWay}"
                        DisplayMemberPath="Name"
                        SelectedValuePath="Id"
                        SelectedValue="{Binding Path=Item.StatusCode, Mode=TwoWay}"/>


                    <Label Text="Comment" />
                    <Editor Text="{Binding Item.Comment}" HeightRequest="100"/>

                    <Label Text="IOM" />
                    <Entry Text="{Binding Item.OutcomeMeasurementInitial, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />

                    <Label Text="FOM" />
                    <Entry Text="{Binding Item.OutcomeMeasurementFinal, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />

                    <Label Text="Longitude" />
                    <Entry Text="{Binding Item.Longitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />

                    <Label Text="Latitude" />
                    <Entry Text="{Binding Item.Latitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />

                    <Button Margin="0,20,0,20" Command="{Binding OnSave}" BackgroundColor="{StaticResource Primary}"
                            BorderRadius="2" Text="Save" VerticalOptions="End" TextColor="White" ></Button>

                  </StackLayout>
                </Grid>
            </ScrollView>
            <controls:Loading LoadingIndicator="{Binding IsLoading}"></controls:Loading>
        </Grid>
      </ContentPage.Content>
</ContentPage>
视图模型代码:

public class ItemCreateViewModel : FormViewModel<Data.Entities.Item>
{
    public async Task GetDeviceLocation()
    {
        this.Item = await this.Item.AddDeviceLocation();
        OnPropertyChanged(nameof(this.Item));
    }

    public ILookupService LookupService { get; set; }

    public IItemService ItemService { get; set; }

    #region selectLists
    public List<EnumListItem<ItemPriority>> Priorities => EnumExtensions.ToEnumList<ItemPriority>();

    public List<EnumListItem<ItemStatus>> Statuses => EnumExtensions.ToEnumList<ItemStatus>();

    public string PageTitle => $"{PageTitles.ItemCreate}{this.OfflineStatus}";

    public List<Data.Entities.User> Users => UserService.GetAll(this.Offline);

    public List<Data.Entities.Lookup> Categories => LookupService.GetLookups(this.Offline, LookupTypeCode.ItemCategories);
    #endregion

    public Data.Entities.Item Item { get; set; }

    public ICommand OnSave => new Command(async () =>
    {
        await Loading(CreateItem);
    });

    private async Task CreateItem()
    {
        // ... Save logic is here
    }
公共类ItemCreateViewModel:FormViewModel
{
公共异步任务GetDeviceLocation()
{
this.Item=等待this.Item.AddDeviceLocation();
OnPropertyChanged(此项的名称);
}
公共ILookupService LookupService{get;set;}
公共IItemService项服务{get;set;}
#区域选择列表
公共列表优先级=>EnumExtensions.ToEnumList();
公共列表状态=>EnumExtensions.ToEnumList();
公共字符串PageTitle=>$“{PageTitles.ItemCreate}{this.OfflineStatus}”;
public List Users=>UserService.GetAll(this.Offline);
公共列表类别=>LookupService.GetLookups(this.Offline,LookupTypeCode.ItemCategories);
#端区
public Data.Entities.Item{get;set;}
公共ICommand OnSave=>new命令(异步()=>
{
等待加载(CreateItem);
});
专用异步任务CreateItem()
{
//…保存逻辑在这里
}
FormViewModel:

public class FormViewModel<T> : BaseViewModel
{
    public IValidator<T> Validator => Resolve<IValidator<T>>();

    public bool IsLoading { get; set; }

    /// <summary>
    /// Render a loading spinner whilst we process a request
    /// </summary>
    /// <param name="method"></param>
    /// <returns></returns>
    public async Task Loading(Func<Task> method)
    {
        IsLoading = true;
        await method.Invoke();
        IsLoading = false;
    }
}
public class BaseViewModel : IViewModelBase
{
    public BaseViewModel()
    {
        if (this.GetCurrentUserToken() != null && !UserService.IsActive())
        {
            SettingService.ClearToken();
            Bootstrapper.MasterDetailPage.IsPresented = false;
            Application.Current.MainPage = new LoginPage();
        }
    }

    public T Resolve<T>() => AutofacBootstrapper.Container.Resolve<T>();

    public IUserService UserService => Resolve<IUserService>();

    public INavigator Navigator => AutofacBootstrapper.Navigator;

    public IDisplayAlertFactory DisplayAlert { get; set; }

    public INavigation MasterNavigation => Bootstrapper.MasterDetailPage?.Detail?.Navigation;

    public bool Offline => SettingService.GetSetting<bool>(CacheProperties.Offline);

    public string OfflineStatus => this.Offline ? " - Offline" : string.Empty;

    public Token GetCurrentUserToken() => SettingService.GetToken() ?? null;

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyname = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
    }
}
公共类FormViewModel:BaseViewModel
{
公共IValidator验证器=>Resolve();
公共bool IsLoading{get;set;}
/// 
///在处理请求时呈现加载微调器
/// 
/// 
/// 
公共异步任务加载(Func方法)
{
IsLoading=true;
wait方法。Invoke();
IsLoading=false;
}
}
BaseViewModel:

public class FormViewModel<T> : BaseViewModel
{
    public IValidator<T> Validator => Resolve<IValidator<T>>();

    public bool IsLoading { get; set; }

    /// <summary>
    /// Render a loading spinner whilst we process a request
    /// </summary>
    /// <param name="method"></param>
    /// <returns></returns>
    public async Task Loading(Func<Task> method)
    {
        IsLoading = true;
        await method.Invoke();
        IsLoading = false;
    }
}
public class BaseViewModel : IViewModelBase
{
    public BaseViewModel()
    {
        if (this.GetCurrentUserToken() != null && !UserService.IsActive())
        {
            SettingService.ClearToken();
            Bootstrapper.MasterDetailPage.IsPresented = false;
            Application.Current.MainPage = new LoginPage();
        }
    }

    public T Resolve<T>() => AutofacBootstrapper.Container.Resolve<T>();

    public IUserService UserService => Resolve<IUserService>();

    public INavigator Navigator => AutofacBootstrapper.Navigator;

    public IDisplayAlertFactory DisplayAlert { get; set; }

    public INavigation MasterNavigation => Bootstrapper.MasterDetailPage?.Detail?.Navigation;

    public bool Offline => SettingService.GetSetting<bool>(CacheProperties.Offline);

    public string OfflineStatus => this.Offline ? " - Offline" : string.Empty;

    public Token GetCurrentUserToken() => SettingService.GetToken() ?? null;

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyname = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
    }
}
公共类BaseViewModel:IViewModelBase
{
公共BaseViewModel()
{
if(this.GetCurrentUserToken()!=null&&!UserService.IsActive())
{
SettingService.ClearToken();
Bootstrapper.MasterDetailPage.IsPresented=false;
Application.Current.MainPage=新登录页面();
}
}
public T Resolve()=>AutofacBootstrapper.Container.Resolve();
public IUserService UserService=>Resolve();
public INavigator Navigator=>AutofacBootstrapper.Navigator;
公共IDisplayAlertFactory DisplayAlert{get;set;}
public INavigation MasterNavigation=>Bootstrapper.MasterDetailPage?.Detail?.Navigation;
public bool Offline=>SettingService.GetSetting(CacheProperties.Offline);
public string OfflineStatus=>this.Offline?”-Offline:string.Empty;
公共令牌GetCurrentUserToken()=>SettingService.GetToken()??空;
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyname=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyname));
}
}

在更改
IsLoading
属性时,您没有调用PropertyChanged事件。如果要刷新UI,需要为所选属性调用此事件

IsLoading
属性的实现更改为:

private bool _isLoading;
public bool IsLoading
{ 
   get=> _isLoading;
   set
   {
      _isLoading=value;
      OnPropertyChanged(nameof(IsLoading));
   }
}
and it should work

更改
IsLoading
属性时,您没有调用PropertyChanged事件。如果要刷新UI,需要为所选属性调用此事件

IsLoading
属性的实现更改为:

private bool _isLoading;
public bool IsLoading
{ 
   get=> _isLoading;
   set
   {
      _isLoading=value;
      OnPropertyChanged(nameof(IsLoading));
   }
}
and it should work

您不需要在此处设置自定义控件的BindingContext:

public Loading()
    {
        InitializeComponent();
        BindingContext = this;//It's wrong!
                              //because the custom control's BindingContext will
                              //automatically be set to the BindingContext of
                              //the page where it's used which is what we usually want.
    }
以下是实现您想要的目标的方法:

自定义控件的XAML:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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="Framework.Controls.Loading" x:Name="LoadingControl"
             HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
  <ContentView.Content>
      <ActivityIndicator x:Name="TheIndicator" HorizontalOptions="CenterAndExpand"
                         VerticalOptions="CenterAndExpand" Color="DarkBlue"/>
    </ContentView.Content>
</ContentView>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
    public static readonly BindableProperty LoadingIndicatorProperty =
        BindableProperty.Create(propertyName:nameof(LoadingIndicator),
        returnType: typeof(bool), declaringType: typeof(Loading), defaultValue: default(bool),
        defaultBindingMode:BindingMode.Default, propertyChanged:LoadingBindingChanged);

    private static void LoadingBindingChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        var view = (Loading)(bindable);
        view.SetLoadingVisibility((bool)newvalue);
    }

    public Loading()
    {
        InitializeComponent();
        IsVisible = false; // we do this because by default a view' IsVisible property is true
    }

    public bool LoadingIndicator
    {
        get => (bool)GetValue(LoadingIndicatorProperty);
        set => SetValue(LoadingIndicatorProperty, value);
    }

    public void SetLoadingVisibility(bool show)
    {
        IsVisible = show;
        TheIndicator.IsVisible = show;
        TheIndicator.IsRunning = show;
    }
}

您不需要在此处设置自定义控件的BindingContext:

public Loading()
    {
        InitializeComponent();
        BindingContext = this;//It's wrong!
                              //because the custom control's BindingContext will
                              //automatically be set to the BindingContext of
                              //the page where it's used which is what we usually want.
    }
以下是实现您想要的目标的方法:

自定义控件的XAML:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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="Framework.Controls.Loading" x:Name="LoadingControl"
             HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
  <ContentView.Content>
      <ActivityIndicator x:Name="TheIndicator" HorizontalOptions="CenterAndExpand"
                         VerticalOptions="CenterAndExpand" Color="DarkBlue"/>
    </ContentView.Content>
</ContentView>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
    public static readonly BindableProperty LoadingIndicatorProperty =
        BindableProperty.Create(propertyName:nameof(LoadingIndicator),
        returnType: typeof(bool), declaringType: typeof(Loading), defaultValue: default(bool),
        defaultBindingMode:BindingMode.Default, propertyChanged:LoadingBindingChanged);

    private static void LoadingBindingChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        var view = (Loading)(bindable);
        view.SetLoadingVisibility((bool)newvalue);
    }

    public Loading()
    {
        InitializeComponent();
        IsVisible = false; // we do this because by default a view' IsVisible property is true
    }

    public bool LoadingIndicator
    {
        get => (bool)GetValue(LoadingIndicatorProperty);
        set => SetValue(LoadingIndicatorProperty, value);
    }

    public void SetLoadingVisibility(bool show)
    {
        IsVisible = show;
        TheIndicator.IsVisible = show;
        TheIndicator.IsRunning = show;
    }
}


我认为在BindableProperty的定义中,它必须是
default(bool)
而不是
default(string)
如果您能告诉我们您如何处理
IsLoading
属性更改(代码),那就太好了。我的意思是,如果您正在
引发PropertyChange
事件,您将如何以及如果您正在引发PropertyChange事件-如果不这样做,则该标志的更改将永远不会传播到视图中。请尝试更改BindingMode.OneWayToSource为
BindingMode.OneWay
BindingMode.TwoWay
。我正要建议类似于@Vahid的事情(即
BindingMode.one-way
)。但是我不会选择
BindingMode.two-way
。谢谢,我更正了BindableProperty定义并尝试了各种绑定模式,但没有用。我认为在bindableperty的定义中,它必须是
default(bool)
而不是
default(string)
如果您能告诉我们您如何处理
IsLoading
属性更改(代码),那就太好了。我的意思是,如果您正在
引发PropertyChange
事件,您将如何以及如果您正在引发PropertyChange事件-如果不这样做,则该标志的更改将永远不会传播到视图中。请尝试更改BindingMode.OneWayToSource为
BindingMode.OneWay
BindingMode.TwoWay
。我正要建议类似于@Vahid的事情(即,
绑定模式。单向
)。我不会选择BindingMode。不过是双向的。。谢谢,我更正了BindingTableProperty定义并尝试了各种绑定模式,但没有效果。@CiaranGallagher您在哪里有IsLoading属性?抱歉,我已经包括FormViewModel和BaseViewModel。它似乎没有实现INotifyPropertyChanged。实际上是IViewM下面的odelBase没有继承INotifyPropertyChanged。@CiaranGallagher我已经更新了答案。请检查它是否解决了问题problem@CiaranGallagher不,它应该在外面工作box@CiaranGallagher哪里有IsLoading属性?抱歉,我已经包括FormViewModel和BaseViewModel。它似乎没有实现INotifyPropertyChanged。实际上下面的IViewModelBase确实继承了INotifyPropertyChanged。@CiaranGallagher我已经更新了我的答案。请