I have in my Xaml a pivot control :
<controls:Pivot ItemsSource="{Binding ObjectList}">
<controls:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock />
</DataTemplate>
</controls:Pivot.HeaderTemplate>
<controls:Pivot.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Value1}" />
<TextBlock Text="{Binding Value2}" />
</StackPanel>
</DataTemplate>
</controls:Pivot.ItemTemplate>
</controls:Pivot>
My ViewModel is :
public class MyObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class MyViewModel : ViewModelBase
{
public const string ObjectListPropertyName = "ObjectList";
private ObservableCollection<MyObject> _objectList;
public ObservableCollection<MyObject> ObjectList
{
get
{
return _objectList;
}
private set
{
if (_objectList == value)
return;
_objectList = value;
RaisePropertyChanged(ObjectListPropertyName);
}
}
private DispatcherTimer timer;
public MyViewModel()
{
ObservableCollection<MyObject> collection = new ObservableCollection<MyObject>
{
new MyObject {Value1 = "One"},
new MyObject {Value1 = "Two"},
new MyObject {Value1 = "Tree"}
};
ObjectList = collection;
timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2)};
timer.Tick += timer_Tick;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
foreach (MyObject myObject in _objectList)
{
myObject.Value2 = "Something";
}
Application.Current.RootVisual.Dispatcher.BeginInvoke( () => RaisePropertyChanged(ObjectListPropertyName));
}
}
When the timer_tick is reached, I supposed the pivot control to refresh with the new values ... but I can't see any changes.
What do I miss ?
Thanks in advance for your help
I'm guessing that possibly updating the members of the list without updating the list itself is the problem. When you raise the property changed event - it is for the entire collection. The collection is still pointing to an equal reference of itself, despite the fact that the members have changed.
Try placing a breakpoint in the setter and see if the property changed event is fired.
Related
I am using checkbox control under repeater to do a radio button functionality, everything seems to be fine but now stuck on how to bind the checkbox when the page loads. I have saved the radio button text whichever was selected and once user come back to page again I want to bin what he has selected last time. Not getting any hint here how to proceed.
<grial:Repeater
x:Name="PP"
SelectionMode="Single"
InitialSelection="Empty"
ItemSize="100"
HorizontalOptions="Start"
ItemsSource="{Binding BlowerPostions}">
<grial:Repeater.ItemTemplate>
<DataTemplate>
<grial:Checkbox
IsChecked="false"
UncheckedBorderColor="Black">
<Label
TextColor="Black"
Text="{ Binding . }"
Margin="8,0" />
</grial:Checkbox>
</DataTemplate>
</grial:Repeater.ItemTemplate>
<grial:Repeater.SelectedItemTemplate>
<DataTemplate>
<grial:Checkbox
IsChecked="true"
UncheckedBorderColor="Black"
InputTransparent="true">
<Label
TextColor="Black"
Text="{ Binding . }"
Margin="8,0" />
</grial:Checkbox>
</DataTemplate>
</grial:Repeater.SelectedItemTemplate>
</grial:Repeater>
View Model :
public class ProductionViewModel : INotifyPropertyChanged
{
public ObservableCollection<BlowerPostion> _blowerPostions;
public ObservableCollection<BlowerPostion> BlowerPostions
{
get => _blowerPostions;
set
{
_blowerPostions = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs("BlowerPostions"));
}
}
}
public void LoadData()
{
BlowerPostions = new ObservableCollection<BlowerPostion>();
BlowerPostions.Add(new BlowerPostion("Left", 1));
BlowerPostions.Add(new BlowerPostion("Standard", 1));
}
}
public class BlowerPostion
{
public string Text { get; set; }
public int Id { get; set; }
public BlowerPostion(string _text, int _id)
{
Text = _text;
Id = _id;
}
}
I don't use grial:Repeater,but you can refer to the following code which use CheckBox in ListView item.
Item.cs
public class Item
{
public string Name { get; set; }
public string Type { get; set; }
public string Image { get; set; }
//This field indicates whether or not it is selected
public bool isChecked { get; set; }
}
MyViewModel.cs
public class MyViewModel
{
public ObservableCollection<Item> items { get; private set; }
public MyViewModel() {
items = new ObservableCollection<Item>();
items.Add(new Item { Name = "Tomato", Type = "Fruit", Image = "tomato.png", isChecked = true });
items.Add(new Item { Name = "Romaine Lettuce", Type = "Vegetable", Image = "lettuce.png", isChecked = false });
items.Add(new Item { Name = "Zucchini", Type = "Vegetable", Image = "zucchini.png", isChecked = false });
}
}
TestPage1.xaml
<ContentPage.Content>
<ListView x:Name="listview" ItemsSource="{Binding items}" VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Padding="5,0,5,0">
<Label Text="{Binding Name}" HorizontalOptions="StartAndExpand" FontSize="30"/>
<input:CheckBox IsChecked="{Binding isChecked}" Type="Check" Color="White" BoxBackgroundColor="Green" TextColor="White" HeightRequest="40"
CheckChanged="CheckBox_CheckChanged" BindingContext="{Binding .}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
TestPage1.xaml.cs
public partial class TestPage1 : ContentPage
{
public List<Item> selectedItems; // define `selectedItems` as the list of selected items.
public MyViewModel viewModel;
public TestPage1 ()
{
InitializeComponent ();
selectedItems = new List<Item>(); // init the `selectedItems`
viewModel = new MyViewModel();
BindingContext = viewModel;
}
private void CheckBox_CheckChanged(object sender, EventArgs e)
{
var checkbox = (Plugin.InputKit.Shared.Controls.CheckBox)sender;
var ob = checkbox.BindingContext as Item;
if (ob != null)
{
System.Diagnostics.Debug.WriteLine("isChecked = " + ob.isChecked + "<---> Name = " + ob.Name +"<---> Type = " + ob.Type );
if (ob.isChecked)
{
selectedItems.Add(ob);
}
else {
// remove the item
}
}
}
}
Note:
1.add new field isChecked in item model
public bool isChecked { get; set; }
2.Add event CheckChanged for the item.And when we check the CheckBox,we can get the corresponding value isChecked of the CheckBox.
<input:CheckBox IsChecked="{Binding isChecked}" Type="Check" Color="White" BoxBackgroundColor="Green" TextColor="White" HeightRequest="40"
CheckChanged="CheckBox_CheckChanged" BindingContext="{Binding .}" />
I do not load a small data from the API, in C# code, they are loaded in advance and everything seems to be fine, but as soon as I open the page where ItemsSource = "{Binding BigData}", my UI is blocked for 10 seconds.
Are there any ideas to open the page first, then start loading data without blocking the UI?
I would to suggest you can kick off a task in your view models constructor that loads the data. Using Async and await to load bid data.
I do one sample that using ListView to display 100000 records.
<StackLayout>
<Label Text="test ui in xamarin.forms asyn" />
<ActivityIndicator IsRunning="{Binding isBusy}" IsVisible="{Binding isBusy}" />
<ListView x:Name="listview1" ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding name}" />
<Label HorizontalOptions="CenterAndExpand" Text="{Binding age}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class Page19 : ContentPage
{
public Page19()
{
InitializeComponent();
this.BindingContext = new ItemsViewModel();
}
}
public class ItemsViewModel:ViewModelBase
{
private bool _isBusy;
public bool isBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
RaisePropertyChanged("isBusy");
}
}
public ObservableCollection<people> Items { get; set; }
public ItemsViewModel()
{
Items = new ObservableCollection<people>();
isBusy = true;
Task.Run(async () => await LoadItems());
}
public async Task LoadItems()
{
var items = new ObservableCollection<people>(); // new collection
if (isBusy)
{
await Task.Delay(10000);
// var loadedItems = ItemsService.LoadItemsDirectory();
//foreach (var item in loadedItems)
// items.Add(item);
for (int i = 0; i < 100000; i++)
{
people p = new people();
p.name = "people " + i;
p.age = i;
items.Add(p); // items are added to the new collection
}
Items = items; // swap the collection for the new one
RaisePropertyChanged(nameof(Items)); // raise a property change in whatever way is right for your VM
isBusy = false;
}
}
}
public class people
{
public string name { get; set; }
public int age { get; set; }
}
ViewModelBase is one class that implementing INotifyPropertyChanged
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I Am working with a ListView Control in XF application. My XAML Code looks like this.
<ListView ItemsSource="{Binding RechargeList}" HasUnevenRows="True" VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Path=SelectedParkingID}" TextColor="Red" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
While my code behind looks like
private ObservableCollection<Recharge> _RechargeList = new ObservableCollection<Recharge>();
public ObservableCollection<Recharge> RechargeList
{
get
{
return _RechargeList;
}
set
{
SetProperty(ref _RechargeList, value);
}
}
And I add Items to Collection in DelegateCommand Event
RechargeList.Add(new Recharge() { SelectedParkingIDParkingID = ParkingID, RechargeAmount = double.Parse(RechargeAmount), BalanceAmount = 10 });
However, the Listview fails to refresh. Could some one help me ?
Looks like you have a typo
<TextCell Text="{Binding Path=SelectedParkingID}" TextColor="Red" />
Should be
<TextCell Text="{Binding Path=SelectedParkingIDParkingID }" TextColor="Red" />
based on what your model looks like. If you try to bind to a property that doesn't exist, it fails softly. So you're adding an item, but the TextCell doesn't render since it has no content.
Please try to implement INotifyPropertyChanged interface in your class.
public class Data : INotifyPropertyChanged
{
// boiler-plate
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
// props
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
}
Each property is then just something like:
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
I am new to two way binding in wp7.The below code does not assign the textbox value to the object automatically and returns null.
Xaml:
<Grid x:Name="ContentPanel" DataContext="{Binding usd}" Grid.Row="1" Margin="14,10,10,-10" >
<TextBox Text="{Binding UserName,Mode=TwoWay}" Name="txt1" Width="200" Height="60" FontSize="20" Margin="128,48,128,499"/>
<TextBox Text="{Binding Password,Mode=TwoWay}" Name="txt2" Width="200" Height="60" FontSize="20" Margin="128,263,128,284"/>
<TextBox Text="{Binding Email,Mode=TwoWay}" Name="txt3" Width="200" Height="60" FontSize="20" Margin="128,159,128,388"/>
<Button Content="Send" FontSize="18" Margin="179,413,170,129"
Click="Button_Click_1" />
</Grid>
Cs:
public class UserLogin:INotifyPropertyChanged
{
private string _username;
private string _pwd;
private string _email;
public string UserName
{
get
{
return _username;
}
set
{
_username = value;
OnPropertyChanged("UserName");
}
}
public string Password
{
get
{
return _pwd;
}
set
{
_pwd = value;
OnPropertyChanged("Password");
}
}
public string Email
{
get
{
return _email;
}
set
{
_email = value;
OnPropertyChanged("Email");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Instantiation:
public UserLogin usd = null;
In constructor:
usd = new UserLogin();
In Button ClickEvent:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// ContentPanel.DataContext = usd;
MessageBox.Show(usd.Email);
}
Null reference exception in Message box statement. Thanks..
you can just bind to public properties - so your: DataContext="{Binding usd}" should be wrong because usd is just a field
btw if you set this in your ctor too, an remove the xaml binding it could work
usd = new UserLogin();
ContentPanel.DataContext = usd;
About your control/page (which XAML belongs to it)
it's datacontext should contain a usd property
that property should also notifying property!
of course your control/page's datacontext class also should implement INotifyPropertyChanged
because your usd is not set as a property its just a variable.... do one thing
public UserLogin usd {get;set;}
usd = null;
Page1 creates and databinds to a new instance of Foo (call it theFoo)
theFoo.Name is set via textbox from Page1
theFoo is saved to a globally accessible data structure (list of Foos, whatever)
navigate from Page1 to Page2
Page2 databinds to the global list of Foos, to display all Foo instances
When I do this, I can verify that the Foo instance is added to the global list. But Page2 never shows any Foos.
If I manually add Foos to the global list (in code instead of from Page1), then navigate to Page2 without ever navigating to Page1 at all, I see Foos displayed in Page2.
What's the issue here?
Update:
Here's some relevant code...
Item.cs (Data and global storage structure)
public class Item
{
public string Name { get; set; }
}
internal static class ItemRepos
{
private static List<Item> _items = new List<Item>();
public static Item New()
{
return new Item();
}
public static int Count
{
get { return _items.Count; }
}
public static IEnumerable<Item> GetAll()
{
return _items;
}
public static Item Get( string name )
{
return _items.SingleOrDefault( item => item.Name == name );
}
public static void Save( Item item )
{
if ( _items.Contains( item ) == false )
{
_items.Add( item );
}
}
public static void Remove( Item item )
{
_items.Remove( item );
}
}
Relevant Page1.xaml fragment
<TextBlock Text="Name:"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="5" />
<TextBox x:Name="txtName"
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding Name}" />
Page1.xaml.cs
public partial class ItemDetail : PhoneApplicationPage
{
public ItemDetail()
{
InitializeComponent();
}
protected override void OnNavigatedTo( NavigationEventArgs e )
{
base.OnNavigatedTo( e );
this.DataContext = ItemRepos.New();
}
private void Nav( object sender, EventArgs e )
{
NavigationService.Navigate( new Uri( "/Page2.xaml", UriKind.RelativeOrAbsolute ) );
}
private void Save( object sender, EventArgs e )
{
ItemRepos.Save( (Item) this.DataContext );
}
}
Relevant Page2.xaml fragment
<controls:PivotItem Header="A-Z">
<ListBox x:Name="listAZ"
ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PivotItem>
Page2.xaml.cs
public partial class ViewItems : PhoneApplicationPage
{
public ViewItems()
{
InitializeComponent();
}
protected override void OnNavigatedTo( NavigationEventArgs e )
{
base.OnNavigatedTo( e );
this.DataContext = ItemRepos.GetAll();
}
}
Without seeing any of your code, its hard to know exactly what is going on, but I'll try guessing. If you add some code to your question, I can be more helpful. Here is a list of possible problems.
The list of Foos isn't actually static (globally accessible to both pages) and they are referencing different lists.
You Page1 isn't successfully updating the list of Foos.
If the list of Foos is an ObservableCollection, somewhere you are instatiating it more than once.
If the list of Foos isn't an ObservableCollection, you aren't setting up the data binding correctly between the two pages.
Update:
Try using the following instead of _items in Item.cs.
public static ObservableCollection<Item> Items = new ObservableCollection<Item>();
Then, change the DataContext of page 2 to use the collection directly:
protected override void OnNavigatedTo( NavigationEventArgs e )
{
base.OnNavigatedTo( e );
this.DataContext = ItemRepos.Items;
}