Reputation: 849
Scenario - There is a ListView binded to a ObservableCollection of string. Listview has one label and one UserControl (containing nothing but a label). Both are binded to the same collection.
Also, there is a button which generate some random data for the collection.
Problem is when I run the app and click on Generate Data button the label gets updated but not the UserControl.
Below is the sample code.
MainPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TestSample"
xmlns:controls="clr-namespace:TestSample.Controls"
x:Class="TestSample.MainPage">
<StackLayout>
<Button Text="Generate Data" Clicked="Button_Clicked"/>
<ListView Grid.Row="1" HorizontalOptions="Center" ItemsSource="{Binding Collection}" SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="Center">
<Label Text="{Binding}"/>
<Label Text=" - "/>
<controls:MagicBox Text="{Binding}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public ObservableCollection<string> Collection { get; set; }
public MainPage()
{
InitializeComponent();
Collection = new ObservableCollection<string>
{
"XX",
"XX",
"XX"
};
this.BindingContext = this;
}
public void Button_Clicked(object sender, EventArgs e)
{
var rand = new Random();
for (int i = 0; i < 3; i++)
{
Collection[i] = rand.Next(10, 100).ToString();
}
}
}
UserControl
<ContentView.Content>
<Grid>
<Label Text="{Binding Text}" />
</Grid>
public partial class MagicBox : ContentView
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(string), typeof(MagicBox), "XX");
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public MagicBox ()
{
InitializeComponent ();
this.BindingContext = this;
}
}
I also tried with ObservableCollection of a POCO class instead of string after implementing INotifyPropertyChanged, didn't worked. If I bind the MagicBox Text to a string directly it works but not if I bind it to some property.
Upvotes: 0
Views: 2429
Reputation: 16232
doing
this.BindingContext = this;
in MagicBox.xaml.cs forces the BindingContext to the current object. It also means that the BindingContext from the parent is no longer inherited.
in order to make it work, change your code behind to
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MagicBox : ContentView
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(string), typeof(MagicBox), default(string));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public MagicBox ()
{
InitializeComponent ();
}
}
and your xaml to
<?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="TestSample.Controls.MagicBox"
x:Name="box">
<ContentView.Content>
<Grid>
<Label Text="{Binding Text, Source={x:Reference box}}" />
</Grid>
</ContentView.Content>
</ContentView>
I tested it. it works.
Upvotes: 3
Reputation: 530
First, update your custom control:
Change your "Text" dependency property definition => Set the binding mode to "OneWay" and add propertyChanged event handler like this:
public partial class MagicBox : ContentView
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(TextVM), typeof(MagicBox), "XX", BindingMode.OneWay, null, new BindableProperty.BindingPropertyChangedDelegate(TextPropertyChanged));
public TextVM Text
{
get { return (TextVM)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
Then add the 'Text' propertyChanged method into your custom control like this:
private static void TextPropertyChanged(BindableObject sender, object oldValue, object newValue )
{
Label updatedLabel = sender as Label;
if(updatedLabel == null) return;
updatedLabel.Text = (newValue as TextVM)?.MyText;
}
Make an Observable object that embed the text property, in order to throw the 'PropertyChanged' event:
public class TextVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _myText;
public string MyText
{
get => _myText;
set
{
_myText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MyText"));
}
}
}
Then in your XAML, update the text binding:
<controls:MagicBox Text="{Binding MyText}"/>
Don't forget to update your collection type and the random number generation process...
It should be good !
Upvotes: 0
Reputation: 106
I think the problem is the line "this.BindingContext = this;" in your custom control.
You should Bind like this: Text="{Binding Path=BindingContext, Source={x:Reference ListViewName}}"
Make sure add x:Name to your Listview. No tested, but hope it help you.
Upvotes: 0