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我已经更新了我的答案。请