I request data from server and after I got response I display them in grid and list boxes -TextBlock(like a table).Until here every thing is okay I finish the display function after that i must call new URL and desirelized the new JSON data to update the my grid table value for example:- my small application request login first after login success i request new URL that retrieve items in (JSON array) with sell price and buy price ==> here i draw my grid with this data like table as i told you before finally i need request new URL retrieve just the items that changed on the server with new prices ===> i dont know how to search in my grid textblock table to update the desired row, please help me (Please check my code below they told me there is an error with INPC and the for loop because when i request the second URL two times the new data not updated in my table ---> Please Advice)
This the my code with only the second URL call i don't know how to implement the third call and how to search into my table at the run time:-
public ObservableCollection<Data> DataReceivedCollection { get; set; }
private void FireRequest2()
{
var request = HttpWebRequest.Create(new Uri("http://74.54.46.178/vertexweb10/webservice.svc/getallsymbols?AccountID=1122336675")) as HttpWebRequest;
request.Method = "GET";
request.CookieContainer = cookieJar;
request.BeginGetResponse(ar =>
{
HttpWebRequest req2 = (HttpWebRequest)ar.AsyncState;
using (var response = (HttpWebResponse)req2.EndGetResponse(ar))
{
using (Stream stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
var outerRoot1 = JsonConvert.DeserializeObject<OuterRootObject1>(reader.ReadToEnd());
JArray jsonArray = JArray.Parse(outerRoot1.d);
JToken jsonArray_Item = jsonArray.First;
while (jsonArray_Item != null)
{
string Name = jsonArray_Item.Value<string>("Name");
string Bid = jsonArray_Item.Value<string>("Bid");
string Ask = jsonArray_Item.Value<string>("Ask");
string ID = jsonArray_Item.Value<string>("ID");
DataReceivedCollection = new ObservableCollection<Data>();
DispatchInvoke(() =>
{
myList.ItemsSource = DataReceivedCollection;
// and to add data you do it like this:
DataReceivedCollection.Add(new Data() { symid = ID, textFirst = Name, textSecond = Bid, textThird = Ask });
}
);
//Be careful, you take the next from the current item, not from the JArray object.
jsonArray_Item = jsonArray_Item.Next;
}
}
}
}
}, request);
}
And here is my XAML:-
<Grid Background="#FFC9DC97" x:Name="ContentPanel" Grid.Row="1" Margin="12,140,12,0">
<ListBox Name="myList" Background="#FFC9DC97">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="ide" Text="{Binding symid}" Grid.Column="3" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding textFirst}" Grid.Column="0" HorizontalAlignment="Left" Foreground="#FF1C69D8"/>
<TextBlock Text="{Binding textSecond}" Grid.Column="1" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding textThird}" Grid.Column="2" HorizontalAlignment="Right"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Here is the INotifyPropertyChanged Class
public class Data : INotifyPropertyChanged
{
private string _textFirst;
public string textFirst
{
[DebuggerStepThrough]
get { return _textFirst; }
[DebuggerStepThrough]
set
{
if (value != _textFirst)
{
_textFirst = value;
OnPropertyChanged("textFirst");
}
}
}
private string _textSecond;
public string textSecond
{
[DebuggerStepThrough]
get { return _textSecond; }
[DebuggerStepThrough]
set
{
if (value != _textSecond)
{
_textSecond = value;
OnPropertyChanged("textSecond");
}
}
}
private string _textThird;
public string textThird
{
[DebuggerStepThrough]
get { return _textThird; }
[DebuggerStepThrough]
set
{
if (value != _textThird)
{
_textThird = value;
OnPropertyChanged("textThird");
}
}
}
private string _symid;
public string symid
{
[DebuggerStepThrough]
get { return _symid; }
[DebuggerStepThrough]
set
{
if (value != _symid)
{
_symid = value;
OnPropertyChanged("symid");
}
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
Please help me
In this fragment...
DataReceivedCollection = new ObservableCollection<Data>();
DispatchInvoke(() =>
{
myList.ItemsSource = DataReceivedCollection;
// and to add data you do it like this:
DataReceivedCollection.Add(new Data() { symid = ID, textFirst = Name, textSecond = Bid, textThird = Ask });
}
You are destroying all previous data by reinitializing the DataReceivedCollection. So it is empty. Then in the dispatcher thread you are binding to it, and then adding to it. And you are repeating the whole thing all over on each pass or the while (jsonArray_Item != null) loop.
The observable collection and binding should be set once, at initialization time. Not each and every time you pass through a loop. If you want to set the collection to empty, use `DataReceivedCollection.Clear();
Move these lines to one-time initialization...
DataReceivedCollection = new ObservableCollection<Data>();
myList.ItemsSource = DataReceivedCollection;
Moreover, since you are dispatching the 'Add' (which is correct), you are inviting a closure problem, i.e., variables may be out of scope before the dispatcher thread executes.
Move these lines into the dispatcher thread...
string Name = jsonArray_Item.Value<string>("Name");
string Bid = jsonArray_Item.Value<string>("Bid");
string Ask = jsonArray_Item.Value<string>("Ask");
string ID = jsonArray_Item.Value<string>("ID");
Your implementation of INPC looks good. At this point it's just your logic flow that needs adjustment.
Related
So I've been making this program for my end project. and I'm using pages in a tab control to us in my WPF. in these pages, the user fills in all the data in the pages and then press a button, that will save the data and send it to the server. Or they can load in the data from the server, and the data should appear in the textboxes in the MVVM. But it doesn't, neither does it read any data from the textboxes. I can't figure out what I'm doing wrong. Under here are some bits of my code (it has 59 items, so I just show a few). And only the insert from the server. since if this is fixed, the save/update would also be fixed easily I think. Thank you in advance
Window.xaml.cs -
public void DataInlezen(string Json)
{
DataGebruiker viewmodel = new DataGebruiker();
var Djson = JsonConvert.DeserializeObject<JsonCS>(Json);
foreach (var AlgeInf in Djson.algemeneInformatie)
{
viewmodel.Achternaam = AlgeInf.Achternaam;
viewmodel.Voornaam = AlgeInf.Voornaam;
viewmodel.Straat = AlgeInf.Straat;
viewmodel.Huisnummer = AlgeInf.Huisnummer;
viewmodel.Postcode = AlgeInf.Postcode;
viewmodel.Stad = AlgeInf.Stad;
viewmodel.Email = AlgeInf.Email;
viewmodel.GSM = AlgeInf.GSM;
viewmodel.Beroep = AlgeInf.Beroep;
viewmodel.Leeftijd = AlgeInf.Leeftijd;
viewmodel.Geslacht = AlgeInf.Geslacht;
}
Model -
public class DataGebruiker : INotifyPropertyChanged
{
private string _Achternaam;
public string Achternaam
{
get
{
return _Achternaam;
}
set
{
_Achternaam = value;
OnPropertyChanged("Achternaam");
}
}
private string _Voornaam;
public string Voornaam
{
get
{
return _Voornaam;
}
set
{
_Voornaam = value;
OnPropertyChanged("Voornaam");
}
}
//more stuff
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
ViewModel -
internal class DataGebruikerViewModel
{
private readonly DataGebruiker _DataGebruiker;
public DataGebruikerViewModel()
{
_DataGebruiker = new DataGebruiker();
}
public DataGebruiker DataGebruiker
{
get
{
return _DataGebruiker;
}
}
XAML -
<Page.DataContext>
<local:DataGebruikerViewModel/>
</Page.DataContext>
<!--some layout stuff-->
<TextBox Name="TextBoxAchternaam" Grid.Column="1" Grid.Row="0" Text="{Binding DataGebruiker.Achternaam}" FontSize="24" VerticalContentAlignment="Center" Margin="6" />
<TextBox Name="TextBoxVoornaam" Grid.Column="1" Grid.Row="1" Text="{Binding DataGebruiker.Voornaam}" FontSize="24" VerticalContentAlignment="Center" Margin="6" />
<Button Name="ButtonOpzoeken" Grid.Column="1" Content="Opzoeken" Grid.Row="8" Width="150" HorizontalAlignment="Center" FontSize="24" Click="ButtonOpzoeken_Click"/> //button to save
I believe it is because the instance of that DataGebruiker which is binded in the view is different than the one instantiated by DataInlezen method.
I have been trying to make this small demo app, that is basically a timer with storable time intervals. I dislike the timepicker controls that are used in the android version so I decided to make my own custom control (using ContentView instead of a custom renderer) to group three pickers for hours, minutes and seconds and update a single TimeSpan object.
My goal is to have a ContentView that is used to represent TimeSpan as parts of a ListView cell, here is how it looks for a better understanding. Problem is I can't seem to get the binding to work proprely. As shown in the image all values are zero, though in this mock option the should all have minute values set. Instead they are all set to zero, and as much as I check they also don't set back all the way back to the model.
As it's supposed to work is when the user selects a value in on of the fields on the right --> it should set the boud property string in the view model --> which should update the value of the TimeSpan property --> which should update the model. And the other way should look like: the list view item gets rendered --> the value of the TimeSpan property is bound --> this sets the value in the viewmodel --> this updates the model.
I'll admit I'm a bit fuzzy on how exactly the end value in the ViewModel is actually bound to the custom control (as the other way would seem to need to work when calling propertyChanged callback).
TimeSpanPropertyChanged and the TimeSpan accessors don't seem to be called after the initial values are set (break point doesn't trigger), unless I use a static value (like "1" instead of "(Binding Duration)" which is the property name in the model). The data returned from the mock service is correct up to and including the creation of PresetViewModel objects.
PresetsPage.xaml
<ListView ItemsSource="{Binding Intervals}" SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate >
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Entry Text="{Binding Name}"/>
<controls:CustomTimeOffsetSelector TimeSpan="{Binding Duration}" Grid.Column="1"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Button Text="Add interval"/>
</ListView.Footer>
</ListView>
It's values are bound to the view model
PresetViewModel.cs
public class PresetViewModel : INotifyPropertyChanged
{
private ObservableCollection<Interval> intervals;
public event PropertyChangedEventHandler PropertyChanged = (o, e) => { };
public Preset Data { get; private set; }
public string Name
{
get => Data.Name;
set
{
Data.Name = value;
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
public ObservableCollection<Interval> Intervals
{
get => intervals;
set
{
intervals = value;
PropertyChanged(this, new PropertyChangedEventArgs("Intervals"));
}
}
public PresetViewModel(Preset preset)
{
Data = preset;
Intervals = new ObservableCollection<Interval>(Data.Intervals);
}
//...
}
The Custom control looks like:
CustomTimeOffsetSelector.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App2.Controls.CustomTimeOffsetSelector">
<ContentView.Content>
<StackLayout Orientation="Horizontal">
<Picker ItemsSource="{Binding HourList}" SelectedItem="{Binding Hours}"/>
<Label Text=":" FontSize="Small" VerticalTextAlignment="Center" Margin="0"/>
<Picker ItemsSource="{Binding MinuteList}" SelectedItem="{Binding Minutes}"/>
<Label Text=":" FontSize="Small" VerticalTextAlignment="Center" Margin="0"/>
<Picker ItemsSource="{Binding SecondList}" SelectedItem="{Binding Seconds}"/>
</StackLayout>
</ContentView.Content>
</ContentView>
CustomTimeOffsetSelector.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomTimeOffsetSelector : ContentView
{
public static readonly BindableProperty TimeSpanProperty =
BindableProperty.Create(
nameof(TimeSpan),
typeof(TimeSpan),
typeof(CustomTimeOffsetSelector),
defaultValue: TimeSpan.Zero,
defaultBindingMode: BindingMode.OneWay,
propertyChanged: TimeSpanPropertyChanged
);
private static void TimeSpanPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = bindable as CustomTimeOffsetSelector;
control.viewModel.Data = (TimeSpan)newValue;
}
public TimeSpan TimeSpan
{
get => (TimeSpan)GetValue(TimeSpanProperty);
set => SetValue(TimeSpanProperty, value);
}
IntervalViewModel viewModel;
public CustomTimeOffsetSelector()
{
viewModel = new IntervalViewModel();
BindingContext = viewModel;
InitializeComponent();
}
}
And finaly the view model.
IntervalViewModel.cs
public class IntervalViewModel : INotifyPropertyChanged
{
#region List init area
private static List<string> hourValues;
public static List<string> HourValues
{
get
{
if (hourValues == null)
{
hourValues = new List<string>();
for (int i = 0; i < 24; i++)
hourValues.Add(i.ToString("00"));
}
return hourValues;
}
}
private static List<string> minuteValues;
public static List<string> MinuteValues
{
get
{
if (minuteValues == null)
{
minuteValues = new List<string>();
for (int i = 0; i < 60; i++)
minuteValues.Add(i.ToString("00"));
}
return minuteValues;
}
}
private static List<string> secondValues;
public static List<string> SecondValues
{
get
{
if (secondValues == null)
{
secondValues = new List<string>();
for (int i = 0; i < 60; i++)
secondValues.Add(i.ToString("00"));
}
return secondValues;
}
}
public List<string> HourList => HourValues;
public List<string> MinuteList => MinuteValues;
public List<string> SecondList => SecondValues;
#endregion
private TimeSpan data;
public TimeSpan Data
{
get => data;
set
{
data = value;
PropertyChanged(this, new PropertyChangedEventArgs("Hours"));
PropertyChanged(this, new PropertyChangedEventArgs("Minutes"));
PropertyChanged(this, new PropertyChangedEventArgs("Seconds"));
}
}
public string Hours
{
get
{
return data.Hours.ToString("00");
}
set
{
data = new TimeSpan(int.Parse(value), data.Minutes, data.Seconds);
PropertyChanged(this, new PropertyChangedEventArgs("Hours"));
}
}
public string Minutes
{
get
{
return data.Minutes.ToString("00");
}
set
{
data = new TimeSpan(data.Hours, int.Parse(value), data.Seconds);
PropertyChanged(this, new PropertyChangedEventArgs("Minutes"));
}
}
public string Seconds
{
get
{
return data.Seconds.ToString("00");
}
set
{
data = new TimeSpan(data.Hours, data.Minutes, int.Parse(value));
PropertyChanged(this, new PropertyChangedEventArgs("Seconds"));
}
}
public IntervalViewModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
}
Interval.cs
public class Interval
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; }
public TimeSpan Duration { get; set; }
}
I know some of the names of files and properties are not the best ones, as well as that using a base ViewModel class would make evrything cleaner my intention was to refactor after I get it working.
I've just been staring at this issue for the last two days and can't seem to think of anything and the answers I find seem to give similar code to what I have currently, which so far has not worked. I'd really appreciate any help in the matter - thank you.
I have a command which sends text when the send button is clicked. The binding is set to two way and the updatesource trigger to propertychanged. but the value of the textbox doesnt change to string.empty which is included in the sendCommand, even though the command was able to take the updated textbox value for a new message.
public class BuddyChatViewModel : BaseViewModel
{
private string chat;
public string Chat
{
get { return chat; }
set
{
chat = value;
RaisePropertyChanged();
}
}
public RelayCommand sendChatCommand { get; private set; }
string username = "";
string buddy = "";
UriStrings url = new UriStrings();
BuddiesHomeModel buddiesList = new BuddiesHomeModel();
HttpService http = new HttpService();
StorageService store = new StorageService();
string response = "";
BuddyChatModel buddyChat = new BuddyChatModel();
List<BuddyChat2Datum> buddychatList = new List<BuddyChat2Datum>();
BuddyChat2Datum tempDatum = new BuddyChat2Datum();
private ObservableCollection<BuddyChat2Datum> buddyChatOC = new ObservableCollection<BuddyChat2Datum>();
public ObservableCollection<BuddyChat2Datum> BuddyChatOC
{
get { return buddyChatOC; }
set
{
buddyChatOC = value;
RaisePropertyChanged();
}
}
private async void sendChatExecute()
{
int i = 0;
string s = url.buddychatText(username, buddy, chat);
chat = "";
response = await http.GetAsync(s);
buddyChat = JsonConvert.DeserializeObject<BuddyChatModel>(response);
buddychatList.Clear();
for (i = 0; i < buddyChat.data.Count; i++)
{
tempDatum.conversation = buddyChat.data[i].conversation;
tempDatum.datetime = buddyChat.data[i].datetime;
tempDatum.from = buddyChat.data[i].from;
tempDatum.to = buddyChat.data[i].to;
if (tempDatum.from == username)
tempDatum.isLeft = false;
else
tempDatum.isLeft = true;
buddychatList.Add(tempDatum);
tempDatum = new BuddyChat2Datum();
}
BuddyChatOC.Clear();
for (i = 0; i < buddychatList.Count; i++)
{
BuddyChatOC.Add(buddychatList[i]);
}
Navigate<BuddyChatViewModel>(buddychatList);
}
#region State Management
public override void LoadState(object navParameter, Dictionary<string, object> state)
{
sendChatCommand = new RelayCommand(sendChatExecute);
int i = 0;
base.LoadState(navParameter, state);
BuddyChatOC.Clear();
// load test items again; in production this would retrieve the live item by id or get it from a local data cache
List<BuddyChat2Datum> buddychatList = (List<BuddyChat2Datum>)navParameter;
//var mes = new MessageDialog(buddychatList.Count.ToString());
//await mes.ShowAsync();
for(i=0;i<buddychatList.Count;i++)
{
BuddyChatOC.Add(buddychatList[i]);
}
username = buddychatList[i-1].username;
buddy = buddychatList[i-1].buddy;
}
public override void SaveState(Dictionary<string, object> state)
{
base.SaveState(state);
}
#endregion
}
}
xaml code:
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListView x:Name="chatList" HorizontalAlignment="Stretch" ItemsSource="{Binding BuddyChatOC}" ItemTemplateSelector="{StaticResource ChatSelector}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<RelativePanel Grid.Row="1" Margin="5,10,5,10">
<TextBox x:Name="sendtext" Margin="0,0,2,0" Text="{Binding Chat, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" RelativePanel.AlignLeftWithPanel="True" RelativePanel.LeftOf="sendtextbutton"/>
<Button x:Name="sendtextbutton" Content="Send" Command="{Binding sendChatCommand}" RelativePanel.AlignRightWithPanel="True" >
</Button>
</RelativePanel>
</Grid>
Implement INotifyPropertyChanged in BuddyChatViewModel.
public class BuddyChatViewModel : INotifyPropertyChanged, BaseViewModel
{
private string chat;
public string Chat
{
get { return chat; }
set
{
chat = value;
NotifyPropertyChanged("Chat");
}
}
//INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
If you're using MVVMLight (and from how this question is tagged I assume you do), you need to specify changed property name in RaisePropertyChanged call.
That should work in your case:
public string Chat
{
get { return chat; }
set
{
chat = value;
RaisePropertyChanged(() => Chat);
}
}
I am having difficulties with binding and INotifyPropertyChanged.
I have a ListView with is bound to an ObservableCollection and there is no problem at startup: data is correctly added to the ListView. When I add a new item to the collection, however, it doesn't update the UI. I'm sure the collection contains the object because I added a button to show the whole content of the collection.
Here is my UI code:
<StackPanel>
<Button Content="Show title" Tapped="Button_Tapped"/>
<ListView ItemsSource="{Binding Subscriptions}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2" Margin="0 0 10 0"
Source="{Binding IconUri.AbsoluteUri}"/>
<TextBlock Grid.Column="1"
Text="{Binding Title.Text}" Style="{StaticResource BaseTextBlockStyle}"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Text="{Binding LastUpdatedTime.DateTime}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
And here is the data context class:
class RssReaderData : INotifyPropertyChanged
{
private string[] acceptContentTypes = {
"application/xml",
"text/xml"
};
private ObservableCollection<SyndicationFeed> _subscriptions;
public ObservableCollection<SyndicationFeed> Subscriptions
{
get { return _subscriptions; }
set { NotifyPropertyChanged(ref _subscriptions, value); }
}
public int SubscriptionsCount
{
get { return Subscriptions.Count; }
}
public RssReaderData()
{
Subscriptions = new ObservableCollection<SyndicationFeed>();
AddFeedAsync(new Uri("http://www.theverge.com/rss/index.xml"));
AddFeedAsync(new Uri("http://blogs.microsoft.com/feed/"));
}
public async Task<bool> AddFeedAsync(Uri uri)
{
// Download the feed at uri
HttpClient client = new HttpClient();
var response = await client.GetAsync(uri);
// Check that we retrieved the resource without error and that the resource has XML content
if (!response.IsSuccessStatusCode || !acceptContentTypes.Contains(response.Content.Headers.ContentType.MediaType))
return false;
var xmlFeed = await response.Content.ReadAsStringAsync();
// Create a new SyndicationFeed and load the XML to it
SyndicationFeed newFeed = new SyndicationFeed();
newFeed.Load(xmlFeed);
// If the title hasn't been set, the feed is invalid
if (String.IsNullOrEmpty(newFeed.Title.Text))
return false;
Subscriptions.Add(newFeed);
return true;
}
#region INotifyPropertyChanged management
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool NotifyPropertyChanged<T> (ref T variable, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(variable, value)) return false;
variable = value;
NotifyPropertyChanged(propertyName);
return true;
}
#endregion
}
As you can see I implemented the INotifyPropertyChanged interface while I think I shouldn't even have to (the ObservableCollection does that for me). I don't care about notifying the changes in the items I add to my collection, what I need is to notify when a new item is added to it.
I would say that my code is OK as is, but it seems not and I don't see why :-/
Also, while I'm at it, I have two quick questions: what's are the differences between a ListView and a ListBox and between a Grid and a GridView ?
Thank you for you help :-)
EDIT : as requested, here's the code-behind of the page
RssReaderData context = new RssReaderData();
public FeedsPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
private async void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
string feedsTitles = "\n";
foreach (var feed in context.Subscriptions)
{
feedsTitles += "\n " + feed.Title.Text;
}
MessageDialog d = new MessageDialog("There are " + context.SubscriptionsCount + " feeds:" + feedsTitles);
await d.ShowAsync();
}
private async void NewFeedSubscribeButton_Tapped(object sender, TappedRoutedEventArgs e)
{
string feedUri = NewFeedUriInput.Text;
if (String.IsNullOrEmpty(feedUri))
return;
if (!Uri.IsWellFormedUriString(feedUri, UriKind.Absolute))
{
MessageDialog d = new MessageDialog("The URL you entered is not valid. Please check it and try again.", "URL Error");
await d.ShowAsync();
return;
}
bool feedSubscribed = await context.AddFeedAsync(new Uri(feedUri));
if (feedSubscribed)
{
NewFeedUriInput.Text = String.Empty;
FeedsPivot.SelectedIndex = 0;
}
else
{
MessageDialog d = new MessageDialog("There was an error fetching the feed. Are you sure the URL is referring to a valid RSS feed?", "Subscription error");
await d.ShowAsync();
return;
}
}
private void FeedsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (FeedsList.SelectedIndex > -1 && FeedsList.SelectedIndex < context.SubscriptionsCount)
{
Frame.Navigate(typeof(FeedDetailsPage), context.Subscriptions[FeedsList.SelectedIndex]);
}
}
It turned out that you have created two instances of RssReaderData - one in code behind and one in xaml with:
<Page.DataContext>
<DataModel:RssReaderData/>
</Page.DataContext
In this situation the collection to which your ListView is bound to is not the same you refer in the code behind - context.
The simple solution may be to remove above lines from XAML and set DataContext in the code behind:
public FeedsPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
this.DataContext = context;
}
The other case is that it also probably may not update properly the UI, as KooKiz has pointed out - better would be first to create the item, then add it to a collection.
You may also take a look at this question and its answers which deals with problem when item changes inside ObservableCollection.
I have problem that i don't know how to bind data in windows phone 8 and the scenario is :-
I request data from server and after I got response I display them in grid and list boxes (like a table).
Until here every thing is okay after I finish the display function there is a new function that retrieves the update to last request.
Here is my problem:
When I receive the new data I don't know how bind this new data to same old table.
For example: the first request is return the gold price 1500$---> I display in table--->then new request--> the update function return the new price of gold is 1502$.
How to update desired row that have gold price textblock with the new price while the application running.
This the first request:-
public ObservableCollection<Data> DataReceivedCollection { get; set; }
private void FireRequest2()
{
var request = HttpWebRequest.Create(new Uri("http://74.54.46.178/vertexweb10/webservice.svc/getallsymbols?AccountID=1122336675")) as HttpWebRequest;
request.Method = "GET";
request.CookieContainer = cookieJar;
request.BeginGetResponse(ar =>
{
HttpWebRequest req2 = (HttpWebRequest)ar.AsyncState;
using (var response = (HttpWebResponse)req2.EndGetResponse(ar))
{
using (Stream stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
var outerRoot1 = JsonConvert.DeserializeObject<OuterRootObject1>(reader.ReadToEnd());
JArray jsonArray = JArray.Parse(outerRoot1.d);
JToken jsonArray_Item = jsonArray.First;
while (jsonArray_Item != null)
{
string Name = jsonArray_Item.Value<string>("Name");
string Bid = jsonArray_Item.Value<string>("Bid");
string Ask = jsonArray_Item.Value<string>("Ask");
string ID = jsonArray_Item.Value<string>("ID");
DataReceivedCollection = new ObservableCollection<Data>();
DispatchInvoke(() =>
{
myList.ItemsSource = DataReceivedCollection;
// and to add data you do it like this:
DataReceivedCollection.Add(new Data() { symid = ID, textFirst = Name, textSecond = Bid, textThird = Ask });
}
);
//Be careful, you take the next from the current item, not from the JArray object.
jsonArray_Item = jsonArray_Item.Next;
}
}
}
}
}, request);
}
And here is my XAML that i want to dsiaply the requested data from firerequest2();
<Grid Background="#FFC9DC97" x:Name="ContentPanel" Grid.Row="1" Margin="12,140,12,0">
<ListBox Name="myList" Background="#FFC9DC97">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="ide" Text="{Binding symid}" Grid.Column="3" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding textFirst}" Grid.Column="0" HorizontalAlignment="Left" Foreground="#FF1C69D8"/>
<TextBlock Text="{Binding textSecond}" Grid.Column="1" HorizontalAlignment="Center"/>
<TextBlock Text="{Binding textThird}" Grid.Column="2" HorizontalAlignment="Right"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
To here every thing is working fine i don't know to update the grid with the new data from the next function
public class Data : INotifyPropertyChanged
{
private string _textFirst;
public string textFirst
{
[DebuggerStepThrough]
get { return _textFirst; }
[DebuggerStepThrough]
set
{
if (value != _textFirst)
{
_textFirst = value;
OnPropertyChanged("textFirst");
}
}
}
private string _textSecond;
public string textSecond
{
[DebuggerStepThrough]
get { return _textSecond; }
[DebuggerStepThrough]
set
{
if (value != _textSecond)
{
_textSecond = value;
OnPropertyChanged("textSecond");
}
}
}
private string _textThird;
public string textThird
{
[DebuggerStepThrough]
get { return _textThird; }
[DebuggerStepThrough]
set
{
if (value != _textThird)
{
_textThird = value;
OnPropertyChanged("textThird");
}
}
}
private string _symid;
public string symid
{
[DebuggerStepThrough]
get { return _symid; }
[DebuggerStepThrough]
set
{
if (value != _symid)
{
_symid = value;
OnPropertyChanged("symid");
}
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
Your dataReceived collection needs to be declared like this because it is the subject of a binding...
public ObservableCollection<Data> DataReceivedCollection { get; set; }
And in the initialization code, it needs to be instantiated like this...
DataReceivedCollection = new ObservableCollection<Data>();
And your data class should be declared something like this (not all properties declared)
public class Data : INotifyPropertyChanged
{
private string _textFirst;
public string TextFirst
{
[DebuggerStepThrough]
get { return _textFirst; }
[DebuggerStepThrough]
set
{
if (value != _textFirst)
{
_textFirst = value;
OnPropertyChanged("TextFirst");
}
}
}
private string _textSecond;
public string TextSecond
{
[DebuggerStepThrough]
get { return _textSecond; }
[DebuggerStepThrough]
set
{
if (value != _textSecond)
{
_textSecond = value;
OnPropertyChanged("TextSecond");
}
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
Doing these things will ensure that the binding engine gets the information it needs to populate your List Box.
This is only a start that will give you some better results. As mentioned in the commentary, your next port of call is to take up a study of INotifyPropertyChanged.