ListBox doesn't refresh after I delete an item - windows-phone-7

This is my class:
[Table]
public class ListData : INotifyPropertyChanged, INotifyPropertyChanging
{
private int _id;
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int Id
{
get
{
return _id;
}
set
{
if (_id != value)
{
OnPropertyChanging("Id");
_id = value;
OnPropertyChanged("Id");
}
}
}
private string _name;
[Column()]
public string Name
{
get
{ return _name; }
set
{
if (_name != value)
{
OnPropertyChanging("Name");
_name = value;
OnPropertyChanged("Name");
}
}
}
private string _url;
[Column()]
public string Url
{
get
{
return _url;
}
set
{
if (_url != value)
{
OnPropertyChanging("Url");
_url = value;
OnPropertyChanged("Url");
}
}
}
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
private void OnPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
}
public class ListDataContext : DataContext
{
public static string DBConnectionString = "Data Source=isostore:/ListDataDB.sdf";
public ListDataContext(string sConnectionString)
: base(sConnectionString)
{ }
public Table<ListData> Datas;
}
this is part of my xaml (listbox):
<ListBox x:Name="listData" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding Path=Name}"
Style="{StaticResource PhoneTextTitle2Style}"
x:Name="txbName"
Tap="txbName_Tap">
</TextBlock>
<Button Tag="{Binding BindsDirectlyToSource=True}"
Click="Button_Click"
Content="X"
BorderBrush="Red"
Background="Red"
Foreground="{StaticResource PhoneBackgroundBrush}"
VerticalAlignment="Top" BorderThickness="0" Width="70" Height="70">
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
here is my methods for loading data:
public void LoadData()
{
this.listData.ItemsSource = GetData();
}
public ObservableCollection<ListData> GetData()
{
List<ListData> listDatas = new List<ListData>();
using (var db = new ListDataContext(ConnectionString))
{
var query = from e in db.Datas
select e;
listDatas = query.ToList();
}
return new ObservableCollection<ListData>(listDatas);
}
and this is how I delete them:
private void Button_Click(object sender, RoutedEventArgs e)
{
try
{
var data = (ListData)((Button)sender).Tag;
using (var db = new ListDataContext(ConnectionString))
{
var entities = from i in db.Datas
where i.Name == data.Name
select i;
var entity = entities.FirstOrDefault();
db.Datas.DeleteOnSubmit(entity);
db.SubmitChanges();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
What´s the problem? Why isn't my ListBox refreshing after deleting? Is there some problem with ObservableCollection?

You are removing it from the database, but I don't see where you call GetData() again, or remove it from the ObservableCollection. Try calling GetData after you delete, or at least manually remove the selected item from the collection.

Related

Xamarin: Bind picker selected item with FreshMvvM ViewModel

I'm creating a form where a product status needs to be selected with a dropdown menu.
I've created a picker for this. The data is loaded from a list in my ViewModel, but it doesn't get sent back.
I've tried the same using entry fields and that works fine. I just have no idea how to link the picker with the view model.
Here's my code.
Xaml
</Grid>
<Label Text="Current status" Style="{StaticResource MainLabel}"/>
<Label Style="{StaticResource MainLabel}" Text="{Binding ProductionStatus, Mode=TwoWay}"/>
<!-- gets send when saved-->
<Entry Style="{StaticResource MainEntry}" Text="{Binding ProductionStatus, Mode=TwoWay}" Keyboard="Text" />
<Label Text="Remark" Style="{StaticResource MainLabel} "/>
<!-- gets send when saved-->
<Entry Text="{Binding Remark}" Keyboard="Text" Style="{StaticResource MainEntry}"/>
<!-- Does not bind with anything.-->
<Picker x:Name="statusPicker" ItemsSource="{Binding ProductionOrderStatusName}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding ProductionStatusName}"/>
<Button Style="{StaticResource PrimaryButton}" Text="Save" Command="{Binding SaveCommand}"></Button>
Code-behind ViewModel
public ICommand SaveCommand
{
get
{
return new Command(ExecuteSaveCommand);
}
}
private async void ExecuteSaveCommand()
{
int statusId = FindProductionOrderStatusId(ProductionStatus); //the production status should be the result of the selected value in the picker
Guid id = _CurrentProductionOrder.Id;
string remark = Remark; // value from the remark entery in the xaml
await __productionOrderService.UpdateAsync(id, remark,statusId);
}
Properties
public ObservableCollection<ProductionOrderStatus> productionOrderStatusName;
public ObservableCollection<ProductionOrderStatus> ProductionOrderStatusName
{
get { return productionOrderStatusName; }
set
{
productionOrderStatusName = value;
}
}
public int amount;
public int Amount
{
get { return amount; }
set
{
amount = value;
}
}
public DateTime finishDate;
public DateTime FinishDate
{
get { return finishDate; }
set
{
finishDate = value;
}
}
public int ordernumber;
public int OrderNumber
{
get { return ordernumber; }
set
{
ordernumber = value;
}
}
public string remark;
public string Remark
{
get { return remark; }
set
{
remark = value;
}
}
public string productionStatus;
public string ProductionStatus
{
get
{
return productionStatus;
}
set
{
productionStatus = value;
}
}
private string materialNumber;
public string MaterialNumber
{
get { return materialNumber; }
set
{
materialNumber = value;
}
}
private string materialDescription;
public string MaterialDescription
{
get { return materialDescription; }
set
{
materialDescription = value;
}
}
Code behind loading my data
public OrderViewModel()
{
_productionOrderStepService = new MockingProductionOrderStepService();
_materialService = new MockingMaterialService();
__productionOrderService = new MockingProductionOrderService();
__productionOrderStatusService = new MockingProductionOrderStatusService();
_seederService = new Seeder(__productionOrderService, _productionOrderStepService, __productionOrderStatusService, _materialService);
_seederService.EnsureSeeded();
}
public override void Init(object initData)
{
_CurrentProductionOrder = initData as ProductionOrder;
LoadItemState();
base.Init(initData);
}
private void LoadItemState()
{
ObservableCollection<ProductionOrderStatus> ProductionStatusName = new ObservableCollection<ProductionOrderStatus>(__productionOrderStatusService.GetListAllAsync().Result);
this.ProductionOrderStatusName = ProductionStatusName;
this.materialDescription = FindMaterialDescription(_CurrentProductionOrder.MaterialId);
this.materialNumber = FindMaterialNumber(_CurrentProductionOrder.MaterialId);
this.productionStatus = FindProductionOrderStatus(_CurrentProductionOrder.StatusId);
this.remark = _CurrentProductionOrder.Remark;
this.amount=_CurrentProductionOrder.Amount;
this.finishDate = _CurrentProductionOrder.FinishDate;
this.ordernumber = _CurrentProductionOrder.OrderNumber;
}
Thx for the help!
you are making this more complicated than it needs to be
<Picker x:Name="statusPicker"
// this is the List of items X to display
ItemsSource="{Binding ProductionOrderStatusName}"
// this tells the picker which property of X to display to the user
ItemDisplayBinding="{Binding Name}"
// this is the specific X the user has selected
SelectedItem="{Binding SelectedStatus}" />
then in your VM
public ObservableCollection<ProductionOrderStatus> ProductionOrderStatusName { get; set; }
public ProductionOrderStatus SelectedStatus { get; set; }

Binding in Xamarin.Forms does not work after web API request

I am trying to make simple app which will provide features to read/write data to database trough an Web API.
I have view model which is bind to view, but it is not working properly after web api get request, even that call was successfully done. I've tried to check value with display alert, value is correct, but it is not presented in view part, exactly in one label. Here is my code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SOSEUApp.Pages.NotePage"
Title="Dnevnik">
<ContentPage.ToolbarItems>
<ToolbarItem Text="GET" Clicked="ToolbarItem_Clicked"></ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<ActivityIndicator IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}"></ActivityIndicator>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Date,StringFormat='Date: {0}'}"></Label>
</StackLayout>
<StackLayout>
</StackLayout>
</StackLayout>
</ContentPage.Content>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NotePage : ContentPage
{
NoteViewModel nvm = new NoteViewModel();
public NotePage()
{
InitializeComponent();
BindingContext = nvm;
}
private async void ToolbarItem_Clicked(object sender, EventArgs e)
{
nvm.IsBusy = true;
nvm.Notes = await App.NotesWebApiService.GetAll(nvm.CurrentActiveNote.Route);
nvm.GetLastNote();
nvm.IsBusy = false;
await DisplayAlert("Info", nvm.Date.ToString(), "Close");
}
}
public class NoteViewModel : BaseViewModel
{
IList<Note> notes = new List<Note>();
public IList<Note> Notes
{
get { return notes; }
set { SetProperty(ref notes, value); }
}
private Note currentActiveNote = new Note();
public Note CurrentActiveNote { get { return currentActiveNote; } }
public string Date { get { return currentActiveNote.Date.ToString("dd.MM.yyyy"); } }
public string OrderedNumber
{
get { return currentActiveNote.OrderNumber.ToString(); }
set
{
string v = currentActiveNote.OrderNumber.ToString();
SetProperty(ref v, value);
currentActiveNote.OrderNumber = Convert.ToInt16(v);
}
}
public string Description
{
get { return currentActiveNote.Description; }
set
{
string v = currentActiveNote.Description;
SetProperty(ref v, value);
currentActiveNote.Description = v;
}
}
public void GetLastNote()
{
notes.OrderBy(a => a.Date);
currentActiveNote = notes.Last();
}
}
public class BaseViewModel : DataModel, INotifyPropertyChanged
{
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Thank you in advance!
just called SetProperty method and passed desired values there. It works
Said as Jason, you need to fire a PropertyChanged event when Date changes.Here is official document for reference.
Generally, usually writed in Set methods.As follow:
private string propertyname;
public string PropertyName
{
set { SetProperty(ref propertyname, value); }
get { return propertyname; }
}
Or write as follow:
public string PropertyName
{
set
{
if (propertyname!= value)
{
propertyname= value;
OnPropertyChanged("PropertyName");
}
}
get
{
return propertyname;
}
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
When data of model change , this will be invoked automatically.

Update list view on expand

I am coding a Xamarin.Forms project and I have a list view but whenever I show hidden content, for example, make an entry visible it the ViewCell overlaps the one beneath it.
Is there a way I could .Update() the listview or something to refresh it and make them all fit.
I don't want the refresh to cause it to go back to the top though.
Android seems to be able to automatically update the height when I show something.
I tried using HasUnevenRows="True" but that still didn't fix it.
Code:
Message.xaml
<StackLayout>
<local:PostListView x:Name="MessageView" HasUnevenRows="True" IsPullToRefreshEnabled="True" Refreshing="MessageView_Refreshing" SeparatorVisibility="None" BackgroundColor="#54a0ff">
<local:PostListView.ItemTemplate>
<DataTemplate>
<local:PostViewCell>
<StackLayout>
<Frame CornerRadius="10" Padding="0" Margin="10, 10, 10, 5" BackgroundColor="White">
<StackLayout>
<StackLayout x:Name="MessageLayout" BackgroundColor="Transparent" Padding="10, 10, 15, 10">
...
<Label Text="{Binding PostReply}" FontSize="15" TextColor="Black" Margin="10, 0, 0, 10" IsVisible="{Binding ShowReply}"/>
<StackLayout Orientation="Vertical" IsVisible="{Binding ShowReplyField}" Spacing="0">
<Entry Text="{Binding ReplyText}" Placeholder="Reply..." HorizontalOptions="FillAndExpand" Margin="0, 0, 0, 5"/>
...
</StackLayout>
<StackLayout x:Name="MessageFooter" Orientation="Horizontal" IsVisible="{Binding ShowBanners}">
<StackLayout Orientation="Horizontal">
...
<Image x:Name="ReplyIcon" Source="reply_icon.png" HeightRequest="20" HorizontalOptions="StartAndExpand" IsVisible="{Binding ShowReplyButton}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ReplyClick}" CommandParameter="{Binding .}"/>
</Image.GestureRecognizers>
</Image>
...
</StackLayout>
...
</StackLayout>
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
</local:PostViewCell>
</DataTemplate>
</local:PostListView.ItemTemplate>
</local:PostListView>
</StackLayout>
Message.cs
using Newtonsoft.Json;
using SocialNetwork.Classes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace SocialNetwork
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessagePage : ContentPage
{
public MessagePage()
{
InitializeComponent();
LoadPage();
}
private async void LoadPage()
{
await LoadMessages();
}
private async void RefreshPage()
{
await LoadMessages();
MessageView.EndRefresh();
}
private async Task LoadMessages()
{
//*Web Request*
MessageView.ItemsSource = FormatPosts(this, Navigation, page_result);
...
}
public IList<MessageObject> FormatPosts(Page page, INavigation navigation, string json)
{
IList<MessageObject> Posts = new List<MessageObject>() { };
var messages = JsonConvert.DeserializeObject<List<Message>>(json);
foreach (var message in messages)
{
MessageObject mo = MessageObject.CreateMessage(...);
Posts.Add(mo);
}
return Posts;
}
public async void ShowOptionActions(string id, string poster_id, object message)
{
...
}
public async void ShowReportOptions(string id, string poster_id)
{
...
}
public void SubmitReplyClick(string id, object msg)
{
...
}
public async void SendReplyAsync(string id, object msg, string reply)
{
await SendReply(id, msg, reply);
}
public void ReplyCommandClick(string id, object msg)
{
MessageObject message = (MessageObject) msg;
message.ShowReplyField = message.ShowReplyField ? false : true;
//Update Cell Bounds
}
private async Task SendReply(string id, object msg, string reply)
{
MessageObject message = (MessageObject)msg;
...
message.PostReply = reply;
//Update Cell Bounds
}
public async void LikeMessageClick(string id, object message)
{
await LikeMessage(id, message);
}
private async Task LikeMessage(string id, object msg)
{
...
}
public async void DeleteMessage(string id, object msg)
{
MessageObject message = (MessageObject)msg;
message.ShowBanners = false;
message.ShowReply = false;
...
//Update Cell Bounds
}
public async Task ReportMessage(...)
{
...
}
private void MessageView_Refreshing(object sender, EventArgs e)
{
RefreshPage();
}
}
public class MessageObject : INotifyPropertyChanged
{
private Boolean showBannersValue = true;
private string replyValue = String.Empty;
private bool showReplyValue;
private bool showReplyButtonValue;
private bool showReplyFieldValue;
private Command replyCommandValue;
private Command replySubmitValue;
private string replyTextValue;
...
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private MessageObject(...)
{
...
}
public static MessageObject CreateMessage(...)
{
return new MessageObject(...);
}
public Boolean ShowBanners
{
get
{
return this.showBannersValue;
}
set
{
if (value != this.showBannersValue)
{
this.showBannersValue = value;
NotifyPropertyChanged();
}
}
}
public Boolean ShowReplyField
{
get
{
return this.showReplyFieldValue;
}
set
{
if(value != this.showReplyFieldValue)
{
this.showReplyFieldValue = value;
NotifyPropertyChanged();
}
}
}
public string PostReply
{
get
{
return this.replyValue;
}
set
{
if (value != this.replyValue)
{
this.replyValue = value;
NotifyPropertyChanged();
}
}
}
public Boolean ShowReply
{
get
{
return this.showReplyValue;
}
set
{
if(value != this.showReplyValue)
{
this.showReplyValue = value;
NotifyPropertyChanged();
}
}
}
public Boolean ShowReplyButton
{
get
{
return this.showReplyButtonValue;
}
set
{
if (value != this.showReplyButtonValue)
{
this.showReplyButtonValue = value;
NotifyPropertyChanged();
}
}
}
public string ReplyText
{
get
{
return this.replyTextValue;
}
set
{
if(value != this.replyTextValue)
{
this.replyTextValue = value;
NotifyPropertyChanged();
}
}
}
public Command ReplyClick
{
get
{
return this.replyCommandValue;
}
set
{
if (value != this.replyCommandValue)
{
this.replyCommandValue = value;
NotifyPropertyChanged();
}
}
}
...
}
}
Save your IList<MessageObject> which gets returned from your FormatPosts method in a field IList<MessageObject> _messages = new List<MessageObject>()
And use the following snippet to update the ListView whenever you need, includes a check to see if the device runs on iOS:
if(Device.RuntimePlatform == Device.iOS)
{
MessageView.ItemsSource = null;
MessageView.ItemsSource = _messages;
}
Especially with iOS there are issues resizing rows in a ListView according to changes of cells (see here). There is a method ForceUpdateSize on Cell, which should notify the ListView that the size of the cell has changed, which should cause the ListView to resize its rows.
Oh, I faced the same thing.
I guess that you just need to add this somewhere in your listview:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.row='0'>
...
</Grid>
// This, in my case, makes my cell expand when it's true. Normal behavior
<Grid Grid.row='1' isVisible="{Binding Expand}">
...
</Grid>
</Grid>
Plus, if you want to update cells individually, I use a CustomObservableCollection:
public class CustomObservableCollection<T> : ObservableCollection<T>
{
public CustomObservableCollection() { }
public CustomObservableCollection(IEnumerable<T> items) : this()
{
foreach(var item in items)
this.Add(item);
}
public void ReportItemChange(T item)
{
NotifyCollectionChangedEventArgs args =
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Replace,
item,
item,
IndexOf(item));
OnCollectionChanged(args);
}
}
With a Custom ListView to do ItemClickCommand:
public class CustomListView : ListView
{
#pragma warning disable 618
public static BindableProperty ItemClickCommandProperty = BindableProperty.Create<CustomListView, ICommand>(x => x.ItemClickCommand, null);
#pragma warning restore 618
public CustomListView(ListViewCachingStrategy cachingStrategy = ListViewCachingStrategy.RetainElement) :
base(cachingStrategy)
{
this.ItemTapped += this.OnItemTapped;
}
public ICommand ItemClickCommand
{
get { return (ICommand)this.GetValue(ItemClickCommandProperty); }
set { this.SetValue(ItemClickCommandProperty, value); }
}
private void OnItemTapped(object sender, ItemTappedEventArgs e)
{
if(e.Item != null && this.ItemClickCommand != null && this.ItemClickCommand.CanExecute(e.Item))
{
this.ItemClickCommand.Execute(e.Item);
this.SelectedItem = null;
}
}
}
then in xaml:
...
...
<Customs:CustomListView
HasUnevenRows="true"
ItemsSource="{Binding PersonList}"
IsPullToRefreshEnabled="True"
RefreshCommand="{Binding DoRefreshCommand}"
ItemClickCommand="{Binding ItemClickCommand}">
...
...
</Customs:CustomListView>
Finally:
public Command<Person> ItemClickCommand { get; set; }
...
ItemClickCommand = new Command<Person>(SelectionExecute);
...
private void SelectionExecute(Person arg)
{
arg.Expand = !arg.Expand;
foreach(var item in PersonList)
{
if(item.Key == arg.Id)// you will change this probably
item.ReportItemChange(arg);
}
}
Hope it help a bit :)

Xamarin Forms - remove extra space / embedded ListView

I am trying to figure out how to remove the white space you see in the image below (surrounded by a red rectangle). Notice I have a ListView embedded in a parent ListView.
XAML
<ListView x:Name="___listview" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Button Image="{Binding ImageName}" Command="{Binding ShowDetailsCommand}" />
<ListView ItemsSource="{Binding Notes}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Note}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This probably isn't needed, but here is the model...
MODEL
namespace ViewCellClick
{
public class ModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Model : ModelBase
{
public Model()
{
_imageName = "ellipses_vertical.png";
_showDetails = true;
ShowDetailsCommand = new Command(() =>
{
ShowDetails = !_showDetails;
ImageName = (_imageName == "ellipses_vertical.png")
? "ellipses_horizontal.png"
: "ellipses_vertical.png";
});
}
bool _showDetails;
public bool ShowDetails
{
get { return _showDetails; }
set { if (_showDetails != value) { _showDetails = value; OnPropertyChanged("ShowDetails"); } }
}
string _imageName;
public string ImageName
{
get { return _imageName; }
set { if (_imageName != value) { _imageName = value; OnPropertyChanged("ImageName"); } }
}
public ICommand ShowDetailsCommand { get; set; }
List<ChildModel> _notes;
public List<ChildModel> Notes { get { return _notes; } set { _notes = value; } }
}
public class ChildModel : ModelBase
{
public ChildModel(string note) { _note = note; }
string _note;
public string Note
{
get { return _note; }
set { if (_note != value) { _note = value; OnPropertyChanged("Note"); } }
}
}
}
You can't do this with Xamarin.Forms.ListView and nesting them is not supported. Really on iOS this would be very difficult and I'm not sure you could get it working without some weird gesture behavior.

WP7 listbox binding not working properly

A noob error for sure (I started yesterday afternoon developing in WP7), but I'm wasting a lot time on it.
I post my class and a little part of my code:
public class ChronoLaps : INotifyPropertyChanged
{
private ObservableCollection<ChronoLap> laps = null;
public int CurrentLap
{
get { return lap; }
set
{
if (value == lap) return;
// Some code here ....
ChronoLap newlap = new ChronoLap()
{
// Some code here ...
};
Laps.Insert(0, newlap);
lap = value;
NotifyPropertyChanged("CurrentLap");
NotifyPropertyChanged("Laps");
}
}
public ObservableCollection<ChronoLap> Laps {
get { return laps; }
set
{
if (value == laps) return;
laps = value;
if (laps != null)
{
laps.CollectionChanged += delegate
{
MeanTime = Laps.Sum(p => p.Time.TotalMilliseconds) / (Laps.Count * 1000);
NotifyPropertyChanged("MeanTime");
};
}
NotifyPropertyChanged("Laps");
}
}
}
MainPage.xaml.cs
public partial class MainPage : PhoneApplicationPage
{
public ChronoLaps History { get; private set; }
private void butStart_Click(object sender, EventArgs e)
{
History = new ChronoLaps();
// History.Laps.Add(new ChronoLap() { Distance = 0 });
LayoutRoot.DataContext = History;
}
}
MainPage.xaml
<phone:PhoneApplicationPage>
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid Grid.Row="2">
<ScrollViewer Margin="-5,13,3,36" Height="758">
<ListBox Name="lbHistory" ItemContainerStyle="{StaticResource ListBoxStyle}"
ItemsSource="{Binding Laps}"
HorizontalAlignment="Left" Margin="5,25,0,0"
VerticalAlignment="Top" Width="444">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Lap}" Width="40" />
<TextBlock Text="{Binding Time}" Width="140" />
<TextBlock Text="{Binding TotalTime}" Width="140" />
<TextBlock Text="{Binding Distance}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Problem is that when I add one or more items to History.Laps collection, my listbox is not refreshed and these items don't appear.
But if I remove comment on // History.Laps.Add(new ChronoLap()... line, this item appear and so every other inserted later.
More: if I remove that comment and then write History.Laps.Clear() (before or after setting binding) binding is not working anymore. It's like it gets crazy if collection is empty.
I really don't understand the reason...
UPDATE AND SOLUTION:
If i move
History = new ChronoLaps();
LayoutRoot.DataContext = History;
from butStart_Click to public MainPage() everything works as expected.
Can someone explain me the reason?
Actually I see no point of having a separate class for ChronoLaps. Here is how I modified the code for MainPage.xaml.cs and everything seems to be working for me.
public partial class MainPage : PhoneApplicationPage
{
public ObservableCollection<ChronoLap> Laps { get; set; }
public double MeanTime { get; set; }
// Constructor
public MainPage()
{
InitializeComponent();
Laps = new ObservableCollection<ChronoLap>();
Laps.CollectionChanged += delegate
{
MeanTime = Laps.Sum(p => p.Time.TotalMilliseconds) / (Laps.Count * 1000);
};
DataContext = this;
Loaded += (s, e) =>
{
Laps.Add(new ChronoLap() {Time = TimeSpan.FromSeconds(1000)});
Laps.Add(new ChronoLap() {Time = TimeSpan.FromSeconds(1000)});
Laps.Add(new ChronoLap() {Time = TimeSpan.FromSeconds(1000)});
};
}
}
Try binding DataContext and ItemSource for listbox..
How i have done is..
<ListBox x:Name="AppList" Background="White" DataContext="{Binding DisplayItem}" SelectionChanged="AppList_SelectionChanged" Height="500" Width="auto">
<ListBox.ItemTemplate>
<DataTemplate>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And i dont know if it will help but still i will just post the code that i am using..
ItemList.cs
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
namespace AppHouseLibrary
{
public class ItemList : IComparable
{
private string _name;
public string WidgetName
{
get
{
return _name;
}
set
{
_name = value;
}
}
public int ID
{
get;
set;
}
private BitmapImage _Icon;
public BitmapImage Icon
{
get
{
return _Icon;
}
set
{
_Icon = value;
}
}
//public string arrow { get; set; }
public BitmapImage arrow { get; set; }
public int CompareTo(ItemList other)
{
return this.WidgetName.CompareTo(other.WidgetName);
}
}
}
I have a UIManager.cs class in which am refreshing the data before i load it on the UI to the user..
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Media.Imaging;
using System.Collections.Generic;
using StirLibrary.com.mportal.data.bean;
using com.mportal.utils;
using StirLibrary.com.mportal.utils;
namespace StirLibrary.com.UI
{
public class UIManager : INotifyPropertyChanged
{
private static UIManager instance = null;
private static string TAG = "UIManager";
BitmapImage arrowImage = Utils.returnImage(ImageUtils.ARROW);
public List<ItemList> data = new List<ItemList>();
public static UIManager getInstance()
{
if (instance == null)
{
instance = new UIManager();
}
return instance;
}
private ObservableCollection<ItemList> _displayItem = new ObservableCollection<ItemList>();
public ObservableCollection<ItemList> DisplayItem
{
get
{
return _displayItem;
}
}
private UIManager()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String PropertyName)
{
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public WidgetBean[] serviceBeanList = null;
public WidgetBean[] wheelBeanList = null;
public WidgetBean getServiceWidgetBean(int selectedIndex)
{
try
{
if (serviceBeanList != null)
{
return serviceBeanList[selectedIndex];
}
}
catch (Exception e)
{
Logger.log(TAG, e.Message);
}
return null;
}
public WidgetBean getWheelWidgetBean(int selectedIndex)
{
try
{
if (wheelBeanList != null)
{
return wheelBeanList[selectedIndex];
}
}
catch (Exception e)
{
Logger.log(TAG, e.Message);
}
return null;
}
public void DisplayCatalog(string[] ServiceDisplayName, string[] WheelDisplayName, BitmapImage[] ServiceIcons, WidgetBean[] ServiceBeanList, WidgetBean[] WheelBeanList)
{
try
{
DisplayItem.Clear();
string disp1 = string.Empty;
string name = ServiceDisplayName[0];
wheelBeanList = WheelBeanList;
serviceBeanList = ServiceBeanList;
for (int i = 0; i < ServiceDisplayName.Length; i++)
{
WidgetBean bean = serviceBeanList[i];
if (bean.isCategory())
{
DisplayItem.Add(new ItemList { WidgetName = ServiceDisplayName[i], Icon = ServiceIcons[i], arrow = arrowImage });
}
else
{
DisplayItem.Add(new ItemList { WidgetName = ServiceDisplayName[i], Icon = ServiceIcons[i] });
}
}
NotifyPropertyChanged("UI");
}
catch (Exception e)
{
Logger.log(TAG,e.Message);
}
}
public void DisplayCatalog(string[] displayName, BitmapImage[] icons, WidgetBean[] beanArray)
{
try
{
serviceBeanList = beanArray;
DisplayItem.Clear();
for (int i = 0; i < displayName.Length; i++)
{
WidgetBean bean = serviceBeanList[i];
if (bean.isCategory())
{
DisplayItem.Add(new ItemList { WidgetName = displayName[i], Icon = icons[i], arrow = arrowImage });
}
else
{
DisplayItem.Add(new ItemList { WidgetName = displayName[i], Icon = icons[i] });
}
}
NotifyPropertyChanged("UI");
}
catch (Exception e)
{
Logger.log(TAG,e.Message);
}
}
}
}

Resources