Pourquoi est-ce que j'obtiens un "Cast spécifié n'est pas valide" lorsque j'utilise une vue personnalisée avec une liaison, mais pas lorsque je le définis manuellement?
Je construis une vue personnalisée en utilisant une ObservableCollection comme BindableProperty (mon objectif était de mettre à jour le format de la vue en fonction de son contenu, dans ce cas une liste de couleurs). Je pense que la propriété est correctement configurée dans la vue personnalisée. Si je définis la propriété pouvant être liée en C #, cela fonctionne. Si j'essaye d'utiliser un {Binding varname} à l'intérieur de XAML, cela génère une erreur "Specified Cast is not Valid". J'essaie de comprendre où est mon problème.
Ci-dessous dans MainPage publique (), vous pouvez voir la ligne commentée où j'ai défini manuellement la propriété. Si je décommente ceci et prends "Color = {Binding Test}" hors de MainPage.xaml, tout fonctionne. Si je l'exécute tel quel, j'obtiens l'exception.
Voici tout mon code:
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public ObservableCollection<Color> Test { get; set; }
public MainPage()
{
Test = new ObservableCollection<Color>();
Test.Add(Color.Blue);
Test.Add(Color.Green);
Test.Add(Color.Red);
Test.Add(Color.Purple);
this.BindingContext = this;
InitializeComponent();
//myView.Colors = Test;
}
private void OnClicked(object sender, EventArgs e)
{
myView.Colors.Add(Color.Yellow);
}
}
MainPage.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"
xmlns:local="clr-namespace:ColorBoxes"
x:Class="ColorBoxes.MainPage">
<StackLayout HorizontalOptions="CenterAndExpand">
<local:ColorBoxView x:Name="myView" Colors="{Binding Test}" WidthRequest="400" HeightRequest="600"/>
<Button Text="Add Color" Clicked="OnClicked"/>
</StackLayout>
</ContentPage>
ColorBoxView.xaml.cs
public partial class ColorBoxView : ContentView
{
public ColorBoxView()
{
InitializeComponent();
}
public static BindableProperty ColorList = BindableProperty.Create(
propertyName: "Colors",
returnType: typeof(ObservableCollection<Color>),
declaringType: typeof(ColorBoxView),
defaultValue: new ObservableCollection<Color>(),
defaultBindingMode: BindingMode.OneWay,
propertyChanged: HandleColorListPropertyChanged
);
public ObservableCollection<Color> Colors
{
get { return (ObservableCollection<Color>) base.GetValue(ColorList); }
set
{
if (value != this.Colors)
{
base.SetValue(ColorList, value);
Colors.CollectionChanged += Items_CollectionChanged;
}
}
}
void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
buildView(Colors);
}
private void buildView(ObservableCollection<Color> newColors)
{
/* This code is never reached */
}
private static void HandleColorListPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
ObservableCollection<Color> newColors = (ObservableCollection<Color>) newValue;
ColorBoxView thisView = (ColorBoxView) bindable;
thisView.buildView(newColors);
}
ColorBoxView.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"
x:Class="ColorBoxes.ColorBoxView" >
<Grid x:Name="BoxGrid" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
RowSpacing="0" ColumnSpacing="0" Padding="0">
</Grid>
</ContentView>
Trace de la pile:
System.InvalidCastException: Specified cast is not valid.
at ColorBoxes.MainPage.InitializeComponent () [0x00023] in /Users/docmani/RiderProjects/ColorBoxes/ColorBoxes/ColorBoxes/obj/Debug/netstandard2.0/MainPage.xaml.g.cs:26
at ColorBoxes.MainPage..ctor () [0x00060] in /Users/docmani/RiderProjects/ColorBoxes/ColorBoxes/ColorBoxes/MainPage.xaml.cs:25
at ColorBoxes.App..ctor () [0x0000f] in /Users/docmani/RiderProjects/ColorBoxes/ColorBoxes/ColorBoxes/App.xaml.cs:15
at ColorBoxes.iOS.AppDelegate.FinishedLaunching (UIKit.UIApplication app, Foundation.NSDictionary options) [0x00007] in /Users/docmani/RiderProjects/ColorBoxes/ColorBoxes/ColorBoxes.iOS/AppDelegate.cs:25
at at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr)
at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIKit/UIApplication.cs:86
at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0000e] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIKit/UIApplication.cs:65
at ColorBoxes.iOS.Application.Main (System.String[] args) [0x00001] in /Users/docmani/RiderProjects/ColorBoxes/ColorBoxes/ColorBoxes.iOS/Main.cs:16
Informations supplémentaires - J'ai essayé de mettre à niveau Xamarin.Forms dans NuGet depuis un certain temps. Maintenant, il ne sera même pas compilé avec la liaison. Ça me donne:
MainPage.xaml(9, 46): [XFC0009] No property, BindableProperty, or event found for "Colors", or mismatching type between value and property.
Mais encore une fois, si je définis manuellement la valeur en C #, cela fonctionne toujours. Il me manque définitivement quelque chose dans mon XAML mais je ne sais pas ce que c'est.
Réponses
Vous pouvez modifier ce qui BindableProperty
suit ColorListProperty
, puis faire un test dans le fichier xaml.
public partial class ColorBoxView : ContentView
{
public ColorBoxView ()
{
InitializeComponent ();
}
public static readonly BindableProperty ColorListProperty = BindableProperty.Create(
nameof(ColorList),
typeof(ObservableCollection<object>),
typeof(ColorBoxView),
new ObservableCollection<object>(),
BindingMode.OneWay,
propertyChanged: ContractListPropertyChangedDelegate);
public ObservableCollection<object> ColorList
{
get =>
(ObservableCollection<object>)GetValue(ColorListProperty);
set => SetValue(ColorListProperty, value);
}
private static void ContractListPropertyChangedDelegate(BindableObject bindable, object oldValue, object newValue)
{
// throw new NotImplementedException();
// ColorBoxView colorBoxView= bindable as ColorBoxView;
}
<local:ColorBoxView x:Name="myView" ColorList="{Binding Test}" WidthRequest="400" HeightRequest="600"/>
Si vous voulez que l' CollectionChanged
événement soit exécuté, lorsque vous cliquez sur le bouton, vous pouvez ajouter le constructeur de ColorList.CollectionChanged += ColorList_CollectionChanged
in ColorBoxView
.
public ColorBoxView ()
{
InitializeComponent ();
ColorList.CollectionChanged += ColorList_CollectionChanged;
}
private void ColorList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// throw new NotImplementedException();
}
Votre set
méthode sera exécutée, lorsque vous définissez un nouveau ObservableCollection
, si vous ajoutez simplement l'élément à existé ObservableCollection
, utilisez peut CollectionChanged
- ColorBoxView
être mieux.
Ainsi, après de nombreuses recherches, j'ai trouvé plusieurs exemples où le type de BindingProperty était défini sur «IEnumerable». Après avoir refait mon code, j'ai trouvé ce qui suit qui fonctionne:
public partial class ColorBoxView : ContentView
{
public ColorBoxView()
{
InitializeComponent();
}
public static readonly BindableProperty ColorsProperty =
BindableProperty.Create (nameof (Colors), typeof (IEnumerable), typeof (ColorBoxView), null,
BindingMode.Default, null, HandleColorListPropertyChanged);
public IEnumerable Colors
{
get { return (IEnumerable) GetValue (ColorsProperty); }
set
{
SetValue (ColorsProperty, value);
var oc = (ObservableCollection<Color>) (this.Colors);
oc.CollectionChanged += Items_CollectionChanged;
}
}
private void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
buildView( (ObservableCollection<Color>)Colors);
}
private void buildView(ObservableCollection<Color> newColors)
{
/* Update code */
}
private static void HandleColorListPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
ObservableCollection<Color> newColors = (ObservableCollection<Color>) newValue;
ColorBoxView thisView = (ColorBoxView) bindable;
thisView.Colors = newColors;
thisView.buildView(newColors);
}
}
Je serais très intéressé de connaître la raison officielle pour laquelle cela fonctionne. Les commentaires sont appréciés.