I am trying to figure out how to display only specific content on a new page and I am wondering how to retrieve that data. For instance I have buttons that are generated from parsed data from an xml sheet and when I click on the button I want the button to direct to a new xaml page I have created and have the data associated with that button displayed on the new xaml page.
First I will link some of the code I am using to store my data from my xml page.
`public int countElements = 0;
public MainViewModel()
{
this.Items = new ObservableCollection<ItemViewModel>();
}
public ObservableCollection<ItemViewModel> Items { get; private set; }
public void LoadData()
{
var elements = from p in unmXdoc.Descendants(dataNamspace + "vevent").Elements(dataNamspace + "properties")
select new ItemViewModel
{
summary = this.GetElementValue(p, "summary"),
description = this.GetElementValue(p, "description"),
categories = this.GetElementValue(p, "dtstamp"),
};
foreach (var element in elements)
{
this.Items.Add(new ItemViewModel()
{
LineOne = element.summary,
LineTwo = element.categories,
LineThree = element.description
});
countElements++;
}
this.IsDataLoaded = true;`
So LineOne is the name of my button and when I click on the button I want LineTwo and LineThree to be loaded on my xaml page that I named LineThreePage.xaml. I will link the xaml code where the buttons are being generated now.
<Grid x:Name="LayoutRoot" Background="Transparent" >
<!--Pivot Control-->
<controls:Pivot Title="" Margin="0,64,0,-63">
<!--Pivot item one-->
<controls:PivotItem>
<!-- Header="Events"-->
<controls:PivotItem.Header>
<TextBlock Text="Events" FontSize="48" ></TextBlock>
</controls:PivotItem.Header>
<!--Double line list with text wrapping-->
<ListBox x:Name="FirstListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="78">
<Button Margin="8,0,10,0"
Padding="0,0"
HorizontalContentAlignment="Left"
BorderThickness="0.8"
BorderBrush="Gray"
Background="White"
Width="420"
Click="Button_Click">
<TextBlock Text="{Binding LineOne}"
TextWrapping="Wrap"
Foreground="#8f1020"
Style="{StaticResource PhoneTextNormalStyle}"/>
</Button>
TextWrapping="Wrap" Margin="12,-10,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PivotItem>'
So basically when I click button1 I want to navigate to my lineThreePage.xaml and see the LineTwo and LineThree associated with the LineOne on that page.
Finally I have my button click code below!
private void Button_Click(object sender, RoutedEventArgs e)
{
this.NavigationService.Navigate(new Uri("/lineThreePage.xaml", UriKind.Relative));
}
Essentially, what you are looking for is a way to preserve "state" within an application. There are several ways to do this, a couple of them being App.Current.ApplicationLifetimeObjects and Isolated storage.
One of the first things I do when I setup a new WP project is to sort out a service that I would used to preserve state within the app. Assuming that your FirstListBox in the code above binds to an entity of type "ItemViewModel".
1) Setup a generic Isolated storage class service.... bear in mind that you can adapt this to whatever suites your requirement, I've made a few assumptions such as returning nulls when a value is not found in this code,
public class IsolateStorageStore
{
/// <summary>
/// The iosolated settings store.
/// </summary>
private readonly IsolatedStorageSettings isolatedStorageSettings = IsolatedStorageSettings.ApplicationSettings;
public T ReadValue<T>(string key)
{
return isolatedStorageSettings.Contains(key) ? (T)isolatedStorageSettings[key] : default(T);
}
public void WriteValue<T>(string key, T value)
{
if (isolatedStorageSettings.Contains(key))
{
isolatedStorageSettings[key] = value;
}
else
{
isolatedStorageSettings.Add(key, value);
}
}
}
2) Setup a mechanism for reading/writing a stored value
public static class IsolatedStorageManager
{
private static IsolateStorageStore IsolateStorageStore = new IsolateStorageStore();
public static ItemViewModel FeedItemViewModel
{
get
{
return IsolateStorageStore.ReadValue<ItemViewModel>("ItemFeedsKey");
}
set
{
IsolateStorageStore.WriteValue("ItemFeedsKey", value);
}
}
public static object AnotherItem
{
get
{
return IsolateStorageStore.ReadValue<object>("AnotherItemKey");
}
set
{
IsolateStorageStore.WriteValue("AnotherItemKey", value);
}
}
}
Now that you have effectively a service for reading/writing objects to storage, tweak your code to use it.
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = (sender as Button);
if (button != null)
{
var data = button.DataContext as ItemViewModel;
if (data != null)
{
//Save to isolated storage
IsolatedStorageManager.FeedItemViewModel = data;
//redirect to next Page.
this.NavigationService.Navigate(new Uri("/lineThreePage.xaml", UriKind.Relative));
}
else
{
MessageBox.Show("An error occured, either the sender is not a button or the data context is not of type ItemViewModel");
}
}
else
{
MessageBox.Show("An error occured, either the sender is not a button or the data context is not of type ItemViewModel");
}
}
And finally on your lineThreePage.xaml page, you need to read the values that you have stored in the isolated storage
public lineThreePage()
{
InitializeComponent();
BindData();
}
private void BindData()
{
var data = IsolatedStorageManager.FeedItemViewModel;
if (data != null)
{
//Bind the data to a text box in your xaml named "txtDescription"
txtDescription.Text = data.LineTwo;
}
}
Do a search for Isolated storage on here to find several resources of how to use it.
Related
I'm extremely new to develop WP8 in C#. I'm trying to write a simple app which will fetch data from a web page, and then update a TextBlock continuously, possibly every second.
Currently, I am able to fetch data from the web page, but cannot update the TextBlock, even though I have used INotifyPropertyChanged. The TextBlock is only updated once when the application started.
The cs file of MachineData:
public class MachineData : INotifyPropertyChanged
{
public string CurrentValue
{
get
{
return _currentValue;
}
set
{
_currentValue = value;
NotifyPropertyChanged("CurrentValue");
}
}
private string _currentValue;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the code that I used for fetching data:
foreach (MachineData md in Items)
{
var wc = new WebClient();
wc.DownloadStringCompleted += (sender, e) =>
{
string data = (string)e.Result;
md.CurrentValue = data; // Update the value of machine
wc.DownloadStringAsync(new Uri(address));
};
wc.DownloadStringAsync(new Uri(address));
}
The xaml snippet:
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding CurrentValue}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
I'm assuming you are using some list like control, such as ListBox, GridView, etc...
What you need to do is bind your Items to ItemSource to the control then you'll get an update.
I don't currently see how you are binding the MachineData to a control so that might be my first guess is to why it doesn't work for you.
Here is some working code.
XAML:
<Grid Background="White">
<ListBox x:Name="MyListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding CurrentValue}" TextWrapping="Wrap"/>
<Button Click="Button_Click">click</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Code behind:
public sealed partial class MainPage : Page
{
List<MachineData> Items = new List<MachineData>();
public MainPage()
{
this.InitializeComponent();
if (System.Diagnostics.Debugger.IsAttached)
{
Application.Current.DebugSettings.EnableFrameRateCounter = false;
}
Items.Add(new MachineData());
MyListBox.ItemsSource = Items;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (MachineData md in Items)
{
md.CurrentValue = "Update Text";
}
}
}
Here, if you press the button, the MachineData is set with "Update Text" and you should see the text update.
I'm trying to display added elements in a ObservableCollection to show on a ListBox in a Page (MenuPage).
This collection is fed by another page, called AddActivityAdvancedPage. In the AddActivityAdvancedPage, the user fill the form and save the informations that I send it as a object (pmaActivity) to the MenuPage. The MenuPage receive the object and add on the ObservableCollection.
The problem is that my ObservableCollection not hold the the added itens! The itens are not showed on the ListBox.
I debug the code and everytime the application hit the line ListActivitiesAdvanced.Add(pmaActivity); on the MenuPage, the ListActivitiesAdvanced is empty. I need to set the ListActivitiesAdvanced as static in some way, but I don't know how is the right way to do this.
AddActivityAdvancedPage class:
public partial class AddActivityAdvancedPage : PhoneApplicationPage
{
//method called to pass the object pmaActivity as parameter to the MenuPage
private void btnSave_Click(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
PhoneApplicationService.Current.State.Remove("pmaActivity");
PhoneApplicationService.Current.State["pmaActivity"] = pmaActivity;
NavigationService.Navigate(new Uri("/MenuPage.xaml", UriKind.Relative));
});
}
}
MenuPage class:
public partial class MenuPage : PhoneApplicationPage
{
public ObservableCollection<PmaActivity> ListActivitiesAdvanced { get; set; }
public MenuPage()
{
InitializeComponent();
ListActivitiesAdvanced = new ObservableCollection<PmaActivity>();
}
//Method called to receive the pmaActivity and add in the collection
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (PhoneApplicationService.Current.State.ContainsKey("pmaActivity"))
{
PmaActivity pmaActivity = PhoneApplicationService.Current.State["pmaActivity"] as PmaActivity;
PhoneApplicationService.Current.State.Remove("pmaActivity");
ListActivitiesAdvanced.Add(pmaActivity);
}
}
}
ListBox in the MenuPage:
<ListBox ItemsSource="{Binding ListActivitiesAdvanced}" Margin="0,0,12,0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="105" >
<Border BorderThickness="1" Width="73" Height="73" BorderBrush="#FF005DFF" Background="#FF005DFF" Margin="0,10,8,0" VerticalAlignment="Top"/>
<StackPanel Width="370">
<TextBlock Text="{Binding clientName}" TextWrapping="NoWrap"
Margin="12,0,0,0" Style="{StaticResource PhoneTextLargeStyle}"/>
<TextBlock Text="{Binding projectName}" TextWrapping="NoWrap"
Margin="12,-6,0,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I try to remove the ListActivitiesAdvanced from MenuPage and add a x:Name to the ListBox element with the same name: ListActivitiesAdvanced:
<ListBox x:Name="ListActivitiesAdvanced" Margin="0,0,12,0"/>
But in this case, the problem is that this list not hold the previous added itens! Every time I add an item, only the last item added is showed on the ObservableCollection.
Thanks for any help! I'm really have problems with that, there are a lot of ways to bind lists in ListBox (as StaticResource, Source, Binding, List, ObservableCollection, IEnumerable...) and I cannot understand all the differences.
If you want to persist the item list, then why not just put the full list into the application state?
//method called to pass the object pmaActivity as parameter to the MenuPage
private void btnSave_Click(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
List<PmaActivity> activities;
if (PhoneApplicationService.Current.State.ContainsKey("pmaActivities"))
activities = PhoneApplicationService.Current.State["pmaActivities"];
else
activities = new List<PmaActivity>();
activities.Add(pmaActivity);
PhoneApplicationService.Current.State["pmaActivities"] = pmaActivities;
NavigationService.Navigate(new Uri("/MenuPage.xaml", UriKind.Relative));
});
}
Then in the main page, populate from the list:
//Method called to receive the pmaActivity and add in the collection
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (PhoneApplicationService.Current.State.ContainsKey("pmaActivity"))
{
if (PhoneApplicationService.Current.State.ContainsKey("pmaActivities"))
{
var pmaActivities = PhoneApplicationService.Current.State["pmaActivities"] as List<PmaActivity>;
foreach (var activity in pmaActivities)
ListActivitiesAdvanced.Add(activity);
}
}
I am using MVVM light in conjunction with EF4 and SQL CE 4, but I am having issues with my observable collection. My application doesn't neccessarily need to use the mvvm pattern, but since I need the benefits of an observablecollection I have decided to learn how to integrate it. I can successfully link my database of property entitites to my listbox and display them, I can also link some properties of these entities to textboxes, but where I am stuck is when I try to update these properties by typing in the textbox. Here is my xaml code for a textbox and the listbox:
<TextBox Text="{Binding SaleTitle, ValidatesOnDataErrors=true, Mode=TwoWay}"
<ListBox Height="424"
Margin="24,80,0,0"
x:Name="listBoxProperties"
VerticalAlignment="Top"
ItemTemplate="{StaticResource propertySummaryTemplate}"
IsSynchronizedWithCurrentItem="True"
Width="216" BorderThickness="0" Background="{x:Null}"
FontFamily="Segoe UI"
ItemsSource="{Binding PropertyList}"
SelectedItem="{Binding CurrentProperty, Mode=TwoWay}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
UseLayoutRounding="True"
HorizontalAlignment="Left"
ScrollViewer.VerticalScrollBarVisibility="Disabled" >
</ListBox>
Here is the code of part of my MainViewModel:
private string _SaleTitle;
public string SaleTitle
{
get
{
if (CurrentProperty != null)
return CurrentProperty.SaleTitle;
else
return "";
}
set
{
_SaleTitle = value;
RaisePropertyChanged("SaleTitle");
}
}
private RelayCommand loadCommand;
public ICommand LoadCommand
{
get
{
if (loadCommand == null)
loadCommand = new RelayCommand(() => Load());
return loadCommand;
}
}
private void Load()
{
PropertyList = new ObservableCollection<Property>((from property in entities.Properties.Include("Images")
select property));
propertyView = CollectionViewSource.GetDefaultView(PropertyList);
if (propertyView != null)
propertyView.CurrentChanged += new System.EventHandler(propertyView_CurrentChanged);
RaisePropertyChanged("CurrentContact");
RaisePropertyChanged("SaleTitle");
RaisePropertyChanged("Address");
RaisePropertyChanged("AuctioneerName");
RaisePropertyChanged("AgentName");
RaisePropertyChanged("Price");
RaisePropertyChanged("NextBid");
RaisePropertyChanged("Status");
}
void propertyView_CurrentChanged(object sender, System.EventArgs e)
{
RaisePropertyChanged("CurrentContact");
RaisePropertyChanged("SaleTitle");
RaisePropertyChanged("Address");
RaisePropertyChanged("AuctioneerName");
RaisePropertyChanged("AgentName");
RaisePropertyChanged("Price");
RaisePropertyChanged("NextBid");
RaisePropertyChanged("Status");
}
private Property _CurrentProperty;
public Property CurrentProperty
{
get
{
if (propertyView != null)
return propertyView.CurrentItem as Property;
return null;
}
set
{
_CurrentProperty = value;
RaisePropertyChanged("CurrentProperty");
}
}
public ObservableCollection<Property> PropertyList
{
get
{
return propertyList;
}
set
{
if (propertyList == value)
{
return;
}
var oldValue = propertyList;
propertyList = value;
// Update bindings, no broadcast
RaisePropertyChanged(PropertiesPropertyName);
}
}
public MainViewModel()
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
// Code runs "for real"
entities = new Model1Container1();
}
}
////public override void Cleanup()
////{
//// // Clean up if needed
//// base.Cleanup();
////}
}
}
The listbox is populated successfully with the content from current selected item, but when I type in it and click out of it or do anything to lose focus it simply goes back to what was there before.
Take a look at your SaleTitle property definition. It Reads value from CurrentProperty.Saletitle but sets value to local field which is not used anythere.
The feature I'm working on is autocomplete for keyword search. As soon as the user inputs something into search bar, the view model calls the autocomplete api with keyword parameter to get the autocomplete suggestions back and put them into a observablecollection container. This observablecollection is a dependency property, it's bound with the list box to show the autocomplete suggestions. My problem is the dependency property is populated correctly but the list box doesn't display anything. Following are some code pieces:
data binding in xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
searchBar.Focus();
_searchViewModel = new SearchViewModel();
DataContext = _searchViewModel;
}
invoke a method in view model to call the autocomplete api:
private void searchBar_TextChanged(object sender, TextChangedEventArgs e)
{
_searchViewModel.getTypeaheadListFromServer(searchBar.Text);
}
dependency property in view model, it's populated successfully:
public ObservableCollection<TypeaheadElement> TypeaheadList
{
get { return (ObservableCollection<TypeaheadElement>)GetValue(TypeaheadListProperty); }
set { SetValue(TypeaheadListProperty, value); }
}
// Using a DependencyProperty as the backing store for TypeaheadList. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TypeaheadListProperty =
DependencyProperty.Register("TypeaheadList", typeof(ObservableCollection<TypeaheadElement>), typeof(SearchViewModel), new PropertyMetadata(null));
data binding in xaml:
<ListBox Name="typeahead" Grid.Row="1" ItemsSource="{Binding TypeaheadList}" Height="518" Margin="0,0,0,-518" SelectionChanged="typeahead_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding TypeaheadElementStr}" FontSize="{StaticResource ListItemFontSize}" FontFamily="Segoe WP" Margin="10,0,0,0" VerticalAlignment="Top">
<TextBlock.Foreground>
<SolidColorBrush Color="{StaticResource ListItemFontColor}"/>
</TextBlock.Foreground>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thank you very much for your help!
try this
<ListBox Name="typeahead" Grid.Row="1" ItemsSource="{Binding TypeaheadList, UpdateSourceTrigger=PropertyChanged}" Height="518" Margin="0,0,0,-518" SelectionChanged="typeahead_SelectionChanged">
I don't understand why you try to implement DependencyProperty is this situations. TypeaheadList is a source of Binding, not a target, right? So it can be a simple property on your ViewModel.
Have you tried using the AutoCompleteBox from the toolkit? If the list of possibilities are not large, you can pre-populate the ItemsSource of the AutoCompleteBox. If you cannot pre-populate it, you could issue an async request to the server to get all of the possibilities when the app starts.
Here are some blogs about using the AutoCompleteBox:
http://www.jeff.wilcox.name/2011/03/acb-in-pivot/
If that is not possible, then you can do something like the following:
Xaml:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<toolkit:AutoCompleteBox ItemsSource="{Binding People}" Populating="AutoCompleteBox_Populating" />
</Grid>
Code:
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
People = new ObservableCollection<string> {"Shawn", "steve", "Bob", "randy", "mike"};
DataContext = this;
InitializeComponent();
}
public ObservableCollection<string> People { get; set; }
private void AutoCompleteBox_Populating(object sender, PopulatingEventArgs e)
{
// Have we already populated with this text?
if(People.Any(person => person.ToLower().StartsWith(e.Parameter.ToLower()))) return;
Completer c = new Completer();
c.Completed += new EventHandler<EventArgs>(c_Completed);
c.Complete(e.Parameter);
}
void c_Completed(object sender, EventArgs e)
{
Completer c = sender as Completer;
foreach (var name in c.Names)
{
People.Add(name);
}
}
}
internal class Completer
{
public event EventHandler<EventArgs> Completed;
public IEnumerable<string> Names { get; set; }
public void Complete(string parameter)
{
if (parameter.StartsWith("d"))
{
Names = new List<string>() { "Dick", "Dave" };
}
else if (parameter.StartsWith("j"))
{
Names = new List<string>() { "Jane", "Joe" };
}
OnCompleted();
}
protected virtual void OnCompleted()
{
var handler = Completed;
if (handler != null) handler(this, EventArgs.Empty);
}
}
I am having an absolute headache figuring this out. I badly need some help with this.
I have a listbox populated with items called with a public static void RSS feed class. Once the listbox populates with the databound items, I click on an item and it passes it through to my pivot page. However, when I flick left or right, all I get is the same image. That is my problem, and what I would like to have happen is if the user flicks left, it loads the previous RSS image. I would like it to also go to the next picture if the If the user scrolls right.
The community has been helpful in providing links to some things, or saying to not use the listbox, etc. However while I am new to all of this, I would just like concrete help with the code i have to achieve what I have in mind. It's nothing personal -- I just need to take babysteps with this before I get worked up with other things I have no clue about.
Here is all my relevant code.
Page 1 Xaml:
<ListBox x:Name="listbox" HorizontalContentAlignment="Stretch" ItemsSource="{Binding items}" SelectionChanged="listbox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Stretch="Fill" Height="60" Width="85" Source="{Binding Url}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Page1 C# Code Behind:
namespace Imaged
{
public partial class UserSubmitted : PhoneApplicationPage
{
private const string Myrssfeed = "http://feeds.bbci.co.uk/news/rss.xml";
public UserSubmitted()
{
InitializeComponent();
//This next function calls the RSS service, and returns the (items) and binds it to
//{listbox.ItemsSource = items;}. I am unable to reference the count of the items, or
//the array of it for some reason? The images load once the page loads.
RssService.GetRssItems(Myrssfeed, (items) => { listbox.ItemsSource = items; }, (exception) => { MessageBox.Show(exception.Message); }, null);
}
}
}
Once the listbox fills I am now trying to pass the selection by the user to a pivot page. I want that same image to show up in the pivot, and when the user pivots left or right, it shows the previous image or next image in the collection.
The Pivot Page I am trying to pass this to, XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<!--Pivot Control-->
<controls:Pivot Title="{Binding Title}">
<!--Pivot item one-->
<controls:PivotItem x:Name="item1">
<Image Source="{Binding Url}"/> <!--I take it this is causing the pics to be the same?-->
</controls:PivotItem>
<!--Pivot item two-->
<controls:PivotItem x:Name="item2">
<Image Source="{Binding Url}"/>
</controls:PivotItem>
<!--Pivot item three-->
<controls:PivotItem x:Name="item3">
<Image Source="{Binding Url}"/>
</controls:PivotItem>
</controls:Pivot>
</Grid>
The RSS Service Class being called:
namespace WindowsPhone.Helpers
{
public class RssService
{
public static void GetRssItems(string rssFeed, Action<IList<RssItem>> onGetRssItemsCompleted = null, Action<Exception> onError = null, Action onFinally = null)
{
WebClient webClient = new WebClient();
// register on download complete event
webClient.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
{
try
{
// convert rss result to model
IList<RssItem> rssItems = new List<RssItem>();
Stream stream = e.Result;
XmlReader response = XmlReader.Create(stream);
{
SyndicationFeed feeds = SyndicationFeed.Load(response);
foreach (SyndicationItem f in feeds.Items)
{
RssItem rssItem = new RssItem(f.Title.Text, f.Summary.Text, f.PublishDate.ToString(), f.Links[0].Uri.AbsoluteUri);
rssItems.Add(rssItem);
}
}
// notify completed callback
if (onGetRssItemsCompleted != null)
{
onGetRssItemsCompleted(rssItems);
}
}
finally
{
// notify finally callback
if (onFinally != null)
{
onFinally();
}
}
};
webClient.OpenReadAsync(new Uri(rssFeed));
}
}
}
and finally the RSSItem Class:
namespace WindowsPhone.Helpers
{
public class RssItem
{
public RssItem(string title, string summary, string publishedDate, string url)
{
Title = title;
Summary = summary;
PublishedDate = publishedDate;
Url = url;
// Get plain text from html
PlainSummary = HttpUtility.HtmlDecode(Regex.Replace(summary, "<[^>]+?>", ""));
}
public string Title { get; set; }
public string Summary { get; set; }
public string PublishedDate { get; set; }
public string Url { get; set; }
public string PlainSummary { get; set; }
}
}
Disclaimer: I don't think that binding this many items to a Pivot control is necessarily the right thing to do. Your mileage may vary, but I think a more virtualized solution would be more efficient. For my tests, it seemed to perform OK, but my little voice tells me that there be dragons here...
I recreated your project to the best of my ability and made some enhancements to get it to do what you wanted. Basically, the trick was using a ViewModel that was shared between both the main list page (UserSubmitted.xaml) and the page with the Pivot items on it (PivotPage1.xaml). By setting both page's DataContext property to the same object, we were able to bind both lists to the same source, thus eliminating the need to pass anything around.
In App.xaml.cs:
public static ViewData ViewModel { get; private set; }
private void Application_Launching(object sender, LaunchingEventArgs e)
{
// note: you should properly Tombstone this data to prevent unnecessary network access
ViewModel = new ViewData();
}
Here is how ViewData is defined:
public class ViewData : INotifyPropertyChanged
{
private string _FeedTitle;
private RssItem _SelectedItem = null;
private ObservableCollection<RssItem> _feedItems = new ObservableCollection<RssItem>();
private const string MyRssfeed = "http://feeds.bbci.co.uk/news/rss.xml";
public ViewData()
{
RssService.GetRssItems(
MyRssfeed,
(title, items) =>
{
App.Current.RootVisual.Dispatcher.BeginInvoke(() =>
{
FeedTitle = title;
FeedItems = new ObservableCollection<RssItem>(items);
});
},
(exception) =>
{
MessageBox.Show(exception.Message);
},
null);
}
public ObservableCollection<RssItem> FeedItems
{
get { return _feedItems; }
set
{
if (_feedItems == value)
return;
_feedItems = value;
NotifyPropertyChanged(this, new PropertyChangedEventArgs("FeedItems"));
}
}
public string FeedTitle
{
get { return _FeedTitle; }
set
{
if (_FeedTitle == value)
return;
_FeedTitle = value;
NotifyPropertyChanged(this, new PropertyChangedEventArgs("FeedTitle"));
}
}
public RssItem SelectedItem
{
get { return _SelectedItem; }
set
{
if (_SelectedItem == value)
return;
_SelectedItem = value;
NotifyPropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (PropertyChanged != null)
PropertyChanged(sender, args);
}
}
Once this is established, it's relatively easy to wire up both page's data context properties to App.ViewModel.
Last item was the scrolling and positioning of the selected item when navigating. When you select an item from the list page, the SelectedItem property of the shared ViewModel is bound to the SelectedItem property on the ListBox. After navigation to the details page, we have to find the selected item in the pivot and make it visible:
public PivotPage1()
{
InitializeComponent();
Loaded += (sender, e) =>
{
this.DataContext = App.ViewModel;
var selectedItem = App.ViewModel.SelectedItem;
var pi = ItemPivot.Items.First(p => p == selectedItem);
ItemPivot.SelectedItem = pi;
};
}
Setting the SelectedItem property of the Pivot control scrolls the pivot to the proper item and makes it visible.
The full sample is posted at http://chriskoenig.net/upload/imaged.zip if you want to see it in action.
If I got you correctly, you need to bind listbox in following way:
<ListBox ItemsSource="{Binding items}" SelectedItem="{Binding SelectedFeed, Mode=TwoWay}" />
And then bind Pivot in same way:
<Pivot ItemsSource="{Binding items}" SelectedItem="{Binding SelectedFeed, Mode=TwoWay}" />
Try the following for the pivot (based on Alex's code)
<Pivot ItemsSource="{Binding items}" SelectedItem="{Binding SelectedFeed, Mode=TwoWay}">
<Pivot.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Url}"/>
</DataTemplate>
</Pivot.ItemTemplate>
</Pivot>
It assumes on the pivot page DataContext there is the same object "items" providing access to all the feeditems, and a property SelectedFeed which (as Alex mentioned) supports INotifyPropertyChanged