Pagination in ListView (UWP) - c#

I have UWP where I have ListView on xaml.
Here is code how I receive json, set it to List
public class TopPostsViewModel : INotifyPropertyChanged
{
private List<Child> postsList;
public List<Child> PostsList
{
get { return postsList; }
set { postsList = value; OnPropertyChanged(); }
}
public TopPostsViewModel()
{
Posts_download();
}
public async void Posts_download()
{
string url = "https://www.reddit.com/top/.json?count=50";
var json = await FetchAsync(url);
RootObject rootObjectData = JsonConvert.DeserializeObject<RootObject>(json);
PostsList = new List<Child>(rootObjectData.data.children);
}
private async Task<string> FetchAsync(string url)
{
string jsonString;
using (var httpClient = new System.Net.Http.HttpClient())
{
var stream = await httpClient.GetStreamAsync(url);
StreamReader reader = new StreamReader(stream);
jsonString = reader.ReadToEnd();
}
return jsonString;
}
public event PropertyChangedEventHandler PropertyChanged;
//[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In XAML I show it Like this:
<ListView x:Name="OrderList" ItemsSource="{Binding PostsList}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid x:Name="GridInf" Height="204" BorderBrush="#FFFBF8F8" BorderThickness="0,0,0,1">
<Image x:Name="Image" HorizontalAlignment="Left" Height="200" Width="200" Tapped="Image_Tapped">
<Image.Source>
<BitmapImage UriSource="{Binding data.thumbnail}" />
</Image.Source>
</Image>
<TextBlock Text="{Binding data.title}" HorizontalAlignment="Left" Margin="252,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="134" Width="1028"/>
<TextBlock HorizontalAlignment="Left" Margin="252,139,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="97" Width="218">
<Run Text="Comments: "/>
<Run Text="{Binding data.num_comments}"/>
</TextBlock>
<TextBlock HorizontalAlignment="Left" Margin="470,134,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Height="102" Width="312"/>
<TextBlock HorizontalAlignment="Left" Margin="787,139,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="97" Width="493">
<Run Text="Author: "/>
<Run Text="{Binding data.author}"/>
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
I need to paginate this ListView - show only 10 posts per page (now I have 50 posts in my List)
How I can do this?

Use two List(One is for all items and another is for the 10 items to be displayed)
private List<ItemSource> postsList = new List<ItemSource>(); //Given List
private List<ItemSource> displayPostsList = new List<ItemSource>(); //List to be displayed in ListView
int pageIndex = -1;
int pageSize = 10; //Set the size of the page
private void NextButton_Click(object sender, RoutedEventArgs e)
{
pageIndex++;
displayPostsList = postsList.Skip(pageIndex * pageSize).Take(pageSize).ToList();
}
private void PreviousButton_Click(object sender, RoutedEventArgs e)
{
pageIndex--;
displayPostsList = postsList.Skip(pageIndex * pageSize).Take(pageSize).ToList();
}
//Call NextButton_Click in page Constructor to show defalut 10 items
NextButton_Click(null, null);
Don't forget to use INotifyPropertyChanged in order to update the ListViewItems (OR) Use ObservableCollection in the place of List and use this answer to convert List into ObservableCollection

Related

flipview on windows phone 8.1 error "The name '' does not exist in the current context"

I have a flipview containing photo, descbox, detailBtn, hideBtn. I would like if the photo tapped, then descbox and hideBtn appear, while detailBtn not appear. And if the photo tapped again, then descbox and hideBtn not appear, while detailBtn looks.
Or if detailBtn clicked, then descbox and hideBtn appear, while detailBtn not appear. And if hideBtn clicked, then descbox and hideBtn not appear, while detailBtn appear.
I'm using the code below:
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
var item = await NatureDataSource.GetItemAsync((String)e.NavigationParameter);
var group = await NatureDataSource.GetGroupByItemAsync(item);
this.DefaultViewModel["Group"] = group;
this.DefaultViewModel["Item"] = item;
}
public bool _IsOn;
public bool IsOn
{
get
{
return _IsOn;
}
set
{
_IsOn = value;
}
}
private void photo_Tapped(object sender, TappedRoutedEventArgs e)
{
IsOn = !IsOn;
if (!IsOn)
{
descbox.Visibility = Visibility.Collapsed;
detailBtn.Visibility = Visibility.Visible;
hideBtn.Visibility = Visibility.Collapsed;
}
else
{
descbox.Visibility = Visibility.Visible;
detailBtn.Visibility = Visibility.Collapsed;
hideBtn.Visibility = Visibility.Visible;
}
}
private void detailBtn_Tapped(object sender, TappedRoutedEventArgs e)
{
descbox.Visibility = Visibility.Visible;
detailBtn.Visibility = Visibility.Collapsed;
hideBtn.Visibility = Visibility.Visible;
}
but an error message like the following:
XAML:
<FlipView x:Name="narrowFlipview" Grid.Row="1" ItemsSource="{Binding Group.Items}" SelectedItem="{Binding Item, Mode=TwoWay}" Foreground="{x:Null}">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid x:Name="ContentRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EdgeUIThemeTransition Edge="Left"/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<ScrollViewer x:Name="myScroll" VerticalScrollBarVisibility="Auto" Margin="0,0,0,0" VerticalScrollMode="Auto" HorizontalScrollBarVisibility="Auto" ZoomMode="Enabled" MinZoomFactor="1" HorizontalScrollMode="Auto">
<StackPanel Height="325" Width="558">
<Image x:Name="photo" Source="{Binding ImagePath}" Stretch="Uniform" Height="320" Tapped="photo_Tapped" Margin="0,0,0.333,0" />
<Border x:Name="descbox" Background="#A52C2C2C" Height="120" VerticalAlignment="Bottom" Visibility="Collapsed" Margin="0,-120,0,0">
<ScrollViewer VerticalScrollMode="Auto" Height="auto" HorizontalScrollBarVisibility="Visible">
<StackPanel Width="538">
<TextBlock x:Name="desc" Text="{Binding Description}" FontFamily="verdana" FontSize="17" Foreground="#CCFFFFFF" TextWrapping="Wrap" Padding="0,10" TextAlignment="Justify" Height="auto"/>
</StackPanel>
</ScrollViewer>
</Border>
<Image x:Name="detailBtn" Source="images/media/arrow_up.png" Margin="0,-40,0,0" Height="40" Width="40" HorizontalAlignment="Right" Tapped="detailBtn_Tapped"/>
<Image x:Name="hideBtn" Source="images/media/arrow_down.png" Margin="0,-285,0,0" Height="40" Width="40" HorizontalAlignment="Right" Visibility="Collapsed" Tapped="hideBtn_Tapped"/>
</StackPanel>
</ScrollViewer>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
How to handle it?
Note:
I tried to use the way in Photo and description on Flipview, but on windows phone 8.1 can not be used
You get the error "descbox" and others don't exist in the current context because they are names of UIElements part of a dataTemplate. The dataTemplate would load only during runtime. The target you want to achieve can be achieved with Data Binding and MVVM concepts far less complexly.
I've created a solution for you based on the data you had provided in the question using Data Binding. I've skipped out the MVVM portion as that is vast enough for an answer. So getting started...
The Updated XAML
based on your code project a few things you could change by putting in the DataBinding for the buttons. like below:
<FlipView x:Name="narrowFlipview" Grid.Row="1" ItemsSource="{Binding Group.Items}" SelectedItem="{Binding Item, Mode=TwoWay}" Foreground="{x:Null}">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid x:Name="ContentRoot">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EdgeUIThemeTransition Edge="Left"/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<ScrollViewer x:Name="myScroll" VerticalScrollBarVisibility="Auto" Margin="0,0,0,0" VerticalScrollMode="Auto" HorizontalScrollBarVisibility="Auto" ZoomMode="Enabled" MinZoomFactor="1" HorizontalScrollMode="Auto">
<StackPanel Height="325" Width="558">
<Image x:Name="photo" Source="{Binding ImagePath}" Stretch="Uniform" Height="320" Tapped="photo_Tapped" Margin="0,0,0.333,0" />
<Border x:Name="descbox" Background="#A52C2C2C" Height="120" VerticalAlignment="Bottom" Visibility="{Binding IsDescriptionVisible,Converter={StaticResource boolToVisibilityConverter}}" Margin="0,-120,0,0">
<ScrollViewer VerticalScrollMode="Auto" Height="auto" HorizontalScrollBarVisibility="Visible">
<StackPanel Width="538">
<TextBlock x:Name="desc" Text="{Binding Description}" FontFamily="verdana" FontSize="17" Foreground="#CCFFFFFF" TextWrapping="Wrap" Padding="0,10" TextAlignment="Justify" Height="auto"/>
</StackPanel>
</ScrollViewer>
</Border>
<AppBarButton x:Name="detailBtn" Icon="Upload" Margin="0,-40,0,0" Height="40" Width="40" HorizontalAlignment="Right" Visibility="{Binding IsDescriptionVisible,Converter={StaticResource boolToInverseVisibilityConverter}}" Click="DetailsBtn_Click"/>
<AppBarButton x:Name="hideBtn" Icon="Download" Margin="0,-285,0,0" Height="40" Width="40" HorizontalAlignment="Right" Visibility="{Binding IsDescriptionVisible,Converter={StaticResource boolToVisibilityConverter}}" Click="HideBtn_Click"/>
</StackPanel>
</ScrollViewer>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
Note: There are UI fails as the margins are not correct but then that's your code. Try not using Margins for an adaptable layout. you could refer my answer here for more details.
The Code Behind for the same:
I've changed the what to do when the Tapped Event is fired. I've left your code commented for reference reasons. The changes in the Code are as below:
private void photo_Tapped(object sender, TappedRoutedEventArgs e)
{
var currentItem = narrowFlipview.SelectedItem as WaterfallDataItem;
currentItem.IsDataVisible = !currentItem.IsDataVisible;
//IsOn = !IsOn;
//if (!IsOn)
//{
// descbox.Visibility = Visibility.Collapsed;
// detailBtn.Visibility = Visibility.Visible;
// hideBtn.Visibility = Visibility.Collapsed;
//}
//else
//{
// descbox.Visibility = Visibility.Visible;
// detailBtn.Visibility = Visibility.Collapsed;
// hideBtn.Visibility = Visibility.Visible;
//}
}
private void DetailsBtn_Click(object sender, RoutedEventArgs e)
{
var currentItem = narrowFlipview.SelectedItem as WaterfallDataItem;
currentItem.IsDescriptionVisible = true;
}
private void HideBtn_Click(object sender, RoutedEventArgs e)
{
var currentItem = narrowFlipview.SelectedItem as WaterfallDataItem;
currentItem.IsDescriptionVisible = false;
}
The WaterfallDataItem Class Changes:
I've added the Interface INotifyPropertyChanged and added two new properties IsDataVisible and IsDescriptionVisible which raise the PropertyChanged event incase they are changed.
public class WaterfallDataItem:INotifyPropertyChanged
{
public WaterfallDataItem(String uniqueId, String title, String imagePath, String description, String content)
{
this.UniqueId = uniqueId;
this.Title = title;
this.Description = description;
this.ImagePath = imagePath;
this.Content = content;
}
public string UniqueId { get; private set; }
public string Title { get; private set; }
public string Description { get; private set; }
public string ImagePath { get; private set; }
public string Content { get; private set; }
//for the image tap to show description functionality
private bool isDataVisible;
public bool IsDataVisible
{
get { return isDataVisible; }
set
{
isDataVisible = value;
if (value)
IsDescriptionVisible = true;
RaisePropertyChanged("IsDataVisible");
}
}
//for hide and show the details pannel and hide and show content based on that
private bool isDescriptionVisible;
public bool IsDescriptionVisible
{
get { return isDescriptionVisible; }
set { isDescriptionVisible = value; RaisePropertyChanged("IsDescriptionVisible"); }
}
//raises the event to the view if any of these properties change
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return this.Title;
}
}
Please Note: The PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); Only works with C# 6.0 which is available in Visual Studio 2015. For any editions you will have to use
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
as the new Null Condition operator is only available in C# 6.0. For more information on What's new in c# 6.0 please refer this
The Converter:
The Converter is used to convert the true or false value from the properties to visibility
public class boolToVisibilityConverter : IValueConverter
{
public bool isInverseReq { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{
bool val = (bool)value;
if(isInverseReq)
{
if (val)
return Visibility.Collapsed;
else
return Visibility.Visible;
}
else
{
if (val)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
The App.xaml
Finally to make the converter work we need to add the converter to the app.xaml. Modify the Application.Resources like below:
<Application.Resources>
<local:boolToVisibilityConverter x:Key="boolToVisibilityConverter" isInverseReq="False"/>
<local:boolToVisibilityConverter x:Key="boolToInverseVisibilityConverter" isInverseReq="True"/>
</Application.Resources>
Please note all the classes are simply made in the main project no sub folders, So if you place the classes and converters in some different namespace, do remember to update the xmlns: tag on the xaml
Edit: To Make your solution work:
At the bare minimum you could change a few things in your code to make it work, I have made the changes in the answer. The above changes would let you implement the functionality without changing your code very much. Do let me know if there is anything I missed.

Binding an Observable Collection to a GridView

My UWP is required to have a Favorites page that allows the user to reorder and save the data on the page. Originally my data comes from a large JSON file which is deserialized using Newtonsoft's Json.net, and is stored for this page in a Dictionary which then fills the public ObservableCollection.
This is where I now get lost, setting the ObservableCollection as the DataContext and then using the data as a Binding in the XAML code to populate the GridView with all the Titles, Subtitles and Images that each Item requires.
In theory this should work, but in my trials and tests the page remains blank while all the C# code behind the scenes makes it seem like it should be populated.
I don't know why the page is not filling to I am turning to the collective help of all of you.
P.S: I don't really care about the neatness of this code, I just want to get it working.
XAML File
<Page
x:Name="pageRoot"
x:Class="Melbourne_Getaway.FavouritesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Melbourne_Getaway"
xmlns:data="using:Melbourne_Getaway.Data"
xmlns:common="using:Melbourne_Getaway.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<x:String x:Key="AppName">Favourites</x:String>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition />
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.RowSpan="2"
Padding="60,136,116,46"
SelectionMode="None"
IsSwipeEnabled="false"
CanReorderItems="True"
CanDragItems="True"
AllowDrop="True"
ItemsSource="{Binding Items}">
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="107">
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" />
</Border>
<StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding Title}" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource BaseTextBlockStyle}" Height="30" Margin="15,0,15,0" FontWeight="SemiBold" />
<TextBlock Text="{Binding Group}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource BaseTextBlockStyle}" TextWrapping="NoWrap" Margin="15,-15,15,10" FontSize="12" />
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button" />
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40" />
</Grid>
</Grid>
CS File
using Melbourne_Getaway.Common;
using Melbourne_Getaway.Data;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Windows.Storage;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace Melbourne_Getaway
{
public sealed partial class FavouritesPage : Page
{
public ObservableCollection<ItemData> Items { get; set; }
private ObservableDictionary defaultViewModel = new ObservableDictionary();
private NavigationHelper navigationHelper;
private RootObject jsonLines;
private StorageFile fileFavourites;
private Dictionary<string, ItemData> ItemData = new Dictionary<string, ItemData>();
public FavouritesPage()
{
loadJson();
getFavFile();
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
private void setupObservableCollection()
{
Items = new ObservableCollection<ItemData>(ItemData.Values);
DataContext = Items;
}
private async void loadJson()
{
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///DataModel/SampleData.json"));
string lines = await FileIO.ReadTextAsync(file);
jsonLines = JsonConvert.DeserializeObject<RootObject>(lines);
feedItems();
}
private async void getFavFile()
{
Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
fileFavourites = await storageFolder.GetFileAsync("MelbGetaway.fav");
}
private async void feedItems()
{
if (await FileIO.ReadTextAsync(fileFavourites) != "")
{
foreach (var line in await FileIO.ReadLinesAsync(fileFavourites))
{
foreach (var Group in jsonLines.Groups)
{
foreach (var Item in Group.Items)
{
if (Item.UniqueId == line)
{
var storage = new ItemData()
{
Title = Item.Title,
UniqueID = Item.UniqueId,
ImagePath = Item.ImagePath,
Group = Group.Title
};
ItemData.Add(storage.UniqueID, storage);
}
}
}
}
}
else
{//should only execute if favourites file is empty, first time use?
foreach (var Group in jsonLines.Groups)
{
foreach (var Item in Group.Items)
{
var storage = new ItemData()
{
Title = Item.Title,
UniqueID = Item.UniqueId,
ImagePath = Item.ImagePath,
Group = Group.Title
};
ItemData.Add(storage.UniqueID, storage);
await FileIO.AppendTextAsync(fileFavourites, Item.UniqueId + "\r\n");
}
}
}
setupObservableCollection();
}
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
#region NavigationHelper loader
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
private async void MessageBox(string Message)
{
MessageDialog dialog = new MessageDialog(Message);
await dialog.ShowAsync();
}
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
this.defaultViewModel["Groups"] = sampleDataGroups;
}
#endregion NavigationHelper loader
#region NavigationHelper registration
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
navigationHelper.OnNavigatedFrom(e);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
navigationHelper.OnNavigatedTo(e);
}
#endregion NavigationHelper registration
}
public class ItemData
{
public string UniqueID { get; set; }
public string Title { get; set; }
public string Group { get; set; }
public string ImagePath { get; set; }
}
}
Without a good Minimal, Complete, and Verifiable code example it is impossible to know for sure what's wrong. However, one glaring error does appear in your code:
private void setupObservableCollection()
{
Items = new ObservableCollection<ItemData>(ItemData.Values);
DataContext = Items;
}
In your XAML, you bind to {Binding Items}. With the DataContext set to the Items property value, the correct binding would actually be just {Binding}.
Alternatively, if you want to keep the XAML the way it is, you would have to set DataContext = this; instead. Of course, if you did it that way, then you would run into the problem that you don't appear to be raising INotifyPropertyChanged.PropertyChanged, or even implementing that interface. You can get away with that if you are sure the property will be set before the InitializeComponent() method is called, but in the code you've shown that does not appear to be the case.
So if you want to set the binding to {Binding Items} you also need to implement INotifyPropertyChanged and make sure you raise the PropertyChanged event with the property name "Items" when you actually set the property.
If the above does not address your question, please improve the question by providing a good MCVE that reliably reproduces the problem.
I figured it out. my problem lied in the way I was trying to pass the Data to the page itself. Instead of using DataContext = Items;and trying to access the data that way. I instead set the direct ItemsSource for the GridView.
The end result was simply changing DataContext = Items to itemGridView.ItemsSource = Items;

Binding issues with label but not listbox

So I have done binding before and I'm not sure what is wrong with my binding this time. Can someone please explain to me why I can't bind my labels to the appropriate properties? The listbox works perfectly and as expected but I don't know why the labels aren't working.
XAML
<Label Content="{Binding MetricName}" Height="25"></Label>
<Label Content="{Binding MetricUnit}" Height="25"></Label>
<ListBox x:Name="EconomicSelection" Grid.Column="1" HorizontalAlignment="Left" Height="150" Margin="5,5,0,5" VerticalAlignment="Top" Width="399" FontFamily="{DynamicResource FontFamily}" FontSize="11" ItemsSource="{Binding EconomicBenchmarks}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="P" Grid.Column="0"/>
<TextBox Text="{Binding Percentile}" Grid.Column="0" Width="30"/>
<Label Content="Value: " Grid.Column="1"/>
<TextBox Text="{Binding Value}" Grid.Column="1"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#
private Metric economicMetric;
public Metric EconomicMetric
{
get { return economicMetric; }
set
{
if (value == economicMetric) return;
economicMetric = value;
OnPropertyChanged();
}
}
private string metricName;
public string MetricName
{
get { return metricName; }
set
{
if (value == metricName) return;
metricName = value;
OnPropertyChanged();
}
}
private string metricUnit;
public string MetricUnit
{
get { return metricUnit; }
set
{
if (value == metricUnit) return;
metricUnit = value;
OnPropertyChanged();
}
}
private ObservableCollection<EconomicBenchmark> economicBenchmarks = new ObservableCollection<EconomicBenchmark>();
public ObservableCollection<EconomicBenchmark> EconomicBenchmarks
{
get { return economicBenchmarks; }
}
public EconomicMeasuresTable()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
public event EconomicMeasuresChangedEventHandler EconomicMeasuresChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (propertyName == "EconomicMetric")
{
metricName = economicMetric.EconomicName;
metricUnit = economicMetric.Unit;
economicBenchmarks.Clear();
foreach (var economicBenchmark in economicMetric.EconomicBenchmarks)
{
economicBenchmarks.Add(economicBenchmark);
}
}
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private void SetButton_Click(object sender, RoutedEventArgs e)
{
if (EconomicMeasuresChanged != null)
{
EconomicMeasuresChanged(this, new EventArgs());
}
}
}
public delegate void EconomicMeasuresChangedEventHandler(object sender, EventArgs e);
List
List<Metric> metrics = new List<Metric>();
metrics.Add(new Metric { EconomicItem = "NVP", EconomicName = "Net Present Value", EconomicBenchmarks = GetEconomicBenchmarks(new[] { 10, 50, 90 }, new[] { 400, 550, 700 }), Unit = "$m" });
metrics.Add(new Metric { EconomicItem = "ROI", EconomicName = "Return On Investment", EconomicBenchmarks = GetEconomicBenchmarks(new[] {10, 50, 90}, new[] {10, 20, 30}), Unit = "%" });
metrics.Add(new Metric { EconomicItem = "STOIIP", EconomicName = "STOIIP", EconomicBenchmarks = GetEconomicBenchmarks(new[] {10, 50, 90}, new[] {500, 550, 600}), Unit = "MMbbl" });
What you are doing in this code is maybe incomplete and a bit strange. First of all, i would never use OnPropertyChanged to set any value, but maybe thats only my opinion...
Anyway, in the code you have shown us,you set metricName and metricUnit in OnPropertyChanged if property is EconomicMetric. But you never set EconomicMetric anywhere so the binding properties would never be set. You need to have a better look at your code.
What I understand from your code is Metric class contains the collection EconomicBenchmarks, then why don't you bind directly that collection with view like this,
<ListBox x:Name="EconomicSelection" Grid.Column="1" HorizontalAlignment="Left" Height="150" Margin="5,5,0,5" VerticalAlignment="Top" Width="399" FontSize="11" ItemsSource="{Binding EconomicMetric.EconomicBenchmarks}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="P" Grid.Column="0"/>
<TextBox Text="{Binding Percentile}" Grid.Column="0" Width="30"/>
<Label Content="Value: " Grid.Column="1"/>
<TextBox Text="{Binding Value}" Grid.Column="1"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

WPF Update Listbox Databinding

I'm new to WPF and am working on Databinding a Listbox from a xml file, everything loads correctly when the program starts, however I'm having trouble making the listbox update after I insert a new record. Everything that I've read is pointing to use a ObservableCollection which I am, but I can't figure out how to get the Listbox to refresh. I have tried calling a update to the ItemsSource but it still doesn't seem to work. Ideally I would like to just have a Refresh button that A user can click to update the listbox. Does anyone have any suggestions on a calling a update to the list box
Thanks Michael
public class ContactList
{
string contactFile = #"U:\Peridot\Users\" + Program.getUser.ToString() + ".xml";
public ContactList()
{
}
public ContactList(string contactFullName, string contactCellNumber,string contactBusinessNumber, string contactExtension, string contactEmail, string contactStatus,string contactAuralinkStatus, string contactAuralinkID)
{
this.ContactFullName = contactFullName;
this.ContactCellNumber = contactCellNumber;
this.ContactBusinessNumber = contactBusinessNumber;
this.ContactExtension = contactExtension;
this.ContactEmail = contactEmail;
this.ContactStatus = contactStatus;
this.ContactAuralinkStatus = contactAuralinkStatus;
this.ContactAuralinkID = contactAuralinkID;
}
private string ContactFullName;
public string PropContactFullName
{
get { return ContactFullName; }
set { ContactFullName = value; }
}
private string ContactCellNumber;
public string PropContactCellNumber
{
get { return ContactCellNumber; }
set { ContactCellNumber = value; }
}
private string ContactBusinessNumber;
public string PropContactBusinessNumber
{
get { return ContactBusinessNumber; }
set { ContactBusinessNumber = value; }
}
private string ContactEmail;
public string PropContactEmail
{
get { return ContactEmail; }
set { ContactEmail = value; }
}
private string ContactStatus;
public string PropContactStatus
{
get { return ContactStatus; }
set { ContactStatus = value; }
}
private string ContactAuralinkStatus;
public string PropContactAuralinkStatus
{
get { return ContactAuralinkStatus; }
set { ContactAuralinkStatus = value; }
}
public string ContactAuralinkID;
public string PropContactAuralinkID
{
get { return ContactAuralinkID; }
set { ContactAuralinkID = value; }
}
private string ContactExtension;
public string PropContactExtension
{
get { return ContactExtension; }
set { ContactExtension = value; }
}
}
public class Contacts : System.Collections.ObjectModel.ObservableCollection<ContactList>
{
string contactFile = #"U:\Peridot\Users\" + Program.getUser.ToString() + ".xml";
//Added this
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
public Contacts(): base()
{
getContactFile();
XDocument doc = XDocument.Load(contactFile);
var contacts = from r in doc.Descendants("Contact")
select new
{
FullName = r.Element("FullName").Value,
CellNumber = r.Element("CellNumber").Value,
BusinessNumber = r.Element("BusinessNumber").Value,
Extension = r.Element("Extension").Value,
Email = r.Element("Email").Value,
AuralinkID = r.Element("AuralinkID").Value
};
foreach (var r in contacts)
{
Add(new ContactList(r.FullName,r.CellNumber , r.BusinessNumber,r.Extension, r.Email, "", "",r.AuralinkID));
}
}
private void getContactFile()
{
if (!File.Exists(contactFile))
{
new XDocument(
new XElement("Contacts"
)
)
.Save(contactFile);
}
}
}
private void addContactICON_MouseDown(object sender, MouseButtonEventArgs e)
{
if (!doesContactExist())
{
try
{
XDocument doc = XDocument.Load(#"U:\Peridot\Users\" + Program.getUser.ToString() + ".xml");
XElement contact = new XElement("Contact");
contact.Add(new XElement("ContactID", contactID.ToString()));
contact.Add(new XElement("FullName", contactNameLBL.Content.ToString()));
contact.Add(new XElement("CellNumber", c1.Content.ToString()));
contact.Add(new XElement("BusinessNumber", businessPhoneIcon.ToolTip.ToString()));
contact.Add(new XElement("Extension", c3.Content.ToString()));
contact.Add(new XElement("Email", emailIcon.ToolTip.ToString()));
contact.Add(new XElement("AuralinkID", videoIcon.ToolTip.ToString()));
doc.Element("Contacts").Add(contact);
doc.Save(#"U:\Peridot\Users\" + Program.getUser.ToString() + ".xml");
MessageBox.Show(contactNameLBL.Content.ToString() + " has been added to your contacts.");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
else
MessageBox.Show("Contact Already Exists");
}
XAML
<StackPanel>
<StackPanel.Resources>
<local:Contacts x:Key="contactListobj"></local:Contacts>
</StackPanel.Resources>
<ListBox x:Name="contactList" Width="305" Margin="5,3,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" ItemsSource="{Binding Source={StaticResource contactListobj}}" Height="450" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" >
<TextBlock Text="{Binding PropContactFullName}" ToolTip="{Binding PropContactFullName}" Height="35" Width="175" FontSize="12"/>
<TextBlock x:Name="contactEmailLBL" Text="{Binding PropContactEmail}" ToolTip="{Binding PropContactEmail}" Cursor="Hand" Width="30" Height="35" MouseLeftButtonUp="contactEmailLBL_MouseLeftButtonUp" Foreground="{x:Null}" FontSize="1">
<TextBlock.Background>
<ImageBrush Stretch="Uniform" ImageSource="Images/emailICON.png"/>
</TextBlock.Background>
</TextBlock>
<TextBlock x:Name="cellNumberLBL" Text="{Binding PropContactCellNumber}" ToolTip="{Binding PropContactCellNumber}" Cursor="Hand" MouseLeftButtonUp="cellNumberLBL_MouseLeftButtonUp" Width="30" Height="35" Foreground="{x:Null}" FontSize="1">
<TextBlock.Background>
<ImageBrush Stretch="Uniform" ImageSource="Images/mobilePhoneICON.png"/>
</TextBlock.Background>
</TextBlock>
<TextBlock x:Name="businessNumberLBL" Text="{Binding PropContactBusinessNumber}" ToolTip="{Binding PropContactBusinessNumber}" Cursor="Hand" Width="30" Height="35" MouseLeftButtonUp="businessNumberLBL_MouseLeftButtonUp" Foreground="{x:Null}" FontSize="1">
<TextBlock.Background>
<ImageBrush Stretch="Uniform" ImageSource="Images/BusinessPhoneICON.png"/>
</TextBlock.Background>
</TextBlock>
<TextBlock x:Name="auralinkLBL" Text="{Binding PropContactAuralinkID}" ToolTip="{Binding PropContactAuralinkID}" Cursor="Hand" Width="30" Height="35" Foreground="{x:Null}" FontSize="1" MouseLeftButtonUp="auralinkLBL_MouseLeftButtonUp">
<TextBlock.Background>
<ImageBrush Stretch="Uniform" ImageSource="Images/VideoICON.png"/>
</TextBlock.Background>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
From what I can tell based on the source code for ObservableCollection, the problem is most likely that the Add method you are using to add ContactList objects to your ObservableCollection is part of the Collection class that ObservableCollection inherits from. This does not fire the CollectionChanged event on the ObservableCollection so your binding is never notified that the collection has changed. Try calling the OnCollectionChanged protected method after you add each item to the collection.

Silverlight: Difficulty with ListBox and {Binding} syntax

I'm building a test Windows Phone 7 Silverlight app. (I've been following this tutorial.) I'm having a problem binding the list items to item properties.
Get tweets for an entered username:
private void button1_Click(object sender, RoutedEventArgs e)
{
WebClient twitter = new WebClient();
twitter.DownloadStringCompleted += new DownloadStringCompletedEventHandler(twitter_DownloadStringCompleted);
twitter.DownloadStringAsync(new Uri(String.Format("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name={0}", username.Text)));
}
Add those tweets to the listbox:
struct TwitterItem
{
public string UserName { get; set; }
public string Message { get; set; }
public string ImageSource { get; set; }
}
void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{
return;
}
XElement xmlTweets = XElement.Parse(e.Result);
IEnumerable<TwitterItem> tweetItems = from tweet in xmlTweets.Descendants("status")
select new TwitterItem
{
ImageSource = tweet.Element("user").Element("profile_image_url").Value,
Message = tweet.Element("text").Value,
UserName = tweet.Element("user").Element("screen_name").Value
};
listBox1.ItemsSource = tweetItems;
PageTitle.Text = tweetItems.First().UserName;
}
PageTitle.Text reveals that the tweets are being correctly parsed... but they aren't being displayed correctly.
Here is the listbox I'm trying to use:
<ListBox Height="454" Width="418" HorizontalAlignment="Left" Margin="36,128,0,0" Name="listBox1" VerticalAlignment="Top">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<Image Source="{Binding ImageSource}" Height="73" Width="73" VerticalAlignment="Top" Margin="0,10,8,0" />
<StackPanel Width="370">
<TextBlock Text="{Binding UserName}" Foreground="BlanchedAlmond" FontSize="28"/>
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" FontSize="24"/>
<TextBlock Text="shows up just fine" TextWrapping="Wrap" FontSize="24"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The third TextBlock shows up just fine, so it's not an issue of having 0 height or width. I suspect that the problem is something with Text={Binding Property}. Or am I doing something else wrong?
I was defining TwitterItem as an inner class of MainPage, when it needed to be a top-level class.
You'll need to pass in an ObservableCollection to listBox1.ItemsSource. Then the UI will update when you set it.
ObservableCollection<TwitterItem> o = new ObservableCollection<TwitterItem>();
foreach(TwitterItem t in tweetItems)
{
o.Add(t);
}
listBox1.ItemsSource = o;
I believe I had the same issue in my app and this fixed it.

Categories

Resources