Windows Phone: ListBox cannot show added items - c#

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);
}
}

Related

Retrieve object from tapped stackpanel element

As the title suggests, I am struggling with retrieving an element from a stackpanel list when tapping it in a simple UWP application. The stackpanel has its itemsource connected to a list of "Customers" which I am then displaying
ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
// Create a new ListView (or GridView) for the UI, add content by setting ItemsSource
ListView customersLV = new ListView();
customersLV.ItemsSource = customers;
// Add the ListView to a parent container in the visual tree (that you created in the corresponding XAML file)
customerPanel.Children.Add(customersLV);
The XAML-code looks like this (Added a scrollviewer for longer lists):
<ScrollViewer VerticalScrollBarVisibility="auto" HorizontalScrollBarVisibility="auto" Margin="63,341,1043,368" >
<StackPanel x:Name="customerPanel" Height="441" Width="394" DoubleTapped="customerPanel_DoubleTapped" ></StackPanel>
</ScrollViewer>
While adding and removing items from the list works great, I cannot seem to access any particular Customer-object from the listpanel when double tapping it.
Here is my doubletap event function:
private void customerPanel_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
testText.Text = e.OriginalSource.ToString();
}
This seems to only print a reference to the whole stackpanel and not to the specific object that I double-tapped. How can I access the tapped Customer-object if I, for example, wanted to call its ToString-method?
Thank you for your time :)
It seems that you just want to get the item value when double click the ListViewItem, so you just need to write a ListView, instead of using ScrollViewer, StackPanel, etc.
So we can write xaml code like:
<Grid>
<ListView x:Name="listView" DoubleTapped="listView_DoubleTapped">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Customer">
<StackPanel Margin="0 10" Orientation="Horizontal">
<Image Width="50" Height="50" Source="{x:Bind Head, Mode=OneWay}"/>
<TextBlock Margin="30 0 0 0" FontSize="25" Text="{x:Bind Name, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Then we define a Customer class.
public class Customer
{
public string Head { get; set; }
public string Name { get; set; }
}
OK, we have done half. Next we are going to create a data collection, and give it to ListView's ItemSource.
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
ObservableCollection<Customer> list = new ObservableCollection<Customer>();
for (int i = 0; i < 100; i++)
{
Customer p = new Customer()
{
Head = "https://learn.microsoft.com/zh-cn/visualstudio/releases/2019/media/2019_rc_logo.png",
Name = i.ToString()
};
list.Add(p);
}
listView.ItemsSource = list;
}
Final step is to complete the DoubleTapped event.
private void listView_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
var customer = listView.SelectedItem as Customer;
Debug.WriteLine(customer.Name);
}
Done!!!
Solved it now I think!
I simply put a listView within my StackPanel like this.
<ScrollViewer VerticalScrollBarVisibility="auto" HorizontalScrollBarVisibility="auto" Margin="63,341,1043,368" >
<StackPanel x:Name="customerPanel" Height="441" Width="394">
<ListView x:Name="customersLV" Tapped="listView_Tapped">
</ListView>
</StackPanel>
</ScrollViewer>
The CS code looked like this:
ObservableCollection<Customer> customers;
public MainPage()
{
this.InitializeComponent();
customers = new ObservableCollection<Customer>();
customersLV.ItemsSource = customers;
}
private void listView_Tapped(object sender, TappedRoutedEventArgs e)
{
testBox.Text = customersLV.SelectedItem.ToString();
}
This will print out the string representation of the actual Customer object in my testBox textblock :)

How to have a button and a combo box together

I want to have a combo box with a button that looks like this:
As I want to use this so that items can be selected and added to a ListView.
Issues:
I don't know how to get and icon in the button like shown
How do you get them to line up really well or is there a way to combine the two elements that I am unaware of?
Here's a working example.
Let's suppose your user control has two controls; a ComboBox and a Button. You want to be able to bind something from your main (parent) to the user control. Then upon selecting something and clicking the button, you want user control to notify to the parent of the event occurrence, and also pass the selected value.
The UserControl XAML:
<UserControl ...
d:DesignHeight="40" d:DesignWidth="200">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" Margin="4" Name="ItemsComboBox"
ItemsSource="{Binding Source, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Button Grid.Column="1" Margin="4" Content="+"
Click="Button_Click"/>
</Grid>
</UserControl>
The following binding will allow you to bind a list of data to the combo box, form the parent:
ItemsSource="{Binding Source, RelativeSource={RelativeSource AncestorType=UserControl}}"
From your MainWindow, you'll use the control like so:
<Grid>
<local:UCComboButton Grid.Row="0" Width="200" Height="40" x:Name="MyUC"
Source="{Binding Names}"/>
</Grid>
And in the UserControls code behind:
public partial class UCComboButton : UserControl
{
public UCComboButton()
{
InitializeComponent();
}
// We use this dependency property to bind a list to the combo box.
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(IEnumerable), typeof(UCComboButton), new PropertyMetadata(null));
public IEnumerable Source
{
get { return (IEnumerable)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
// This is to send the occurred event, in this case button click, to the parent, along with the selected data.
public class SelectedItemEventArgs : EventArgs
{
public string SelectedChoice { get; set; }
}
public event EventHandler<SelectedItemEventArgs> ItemHasBeenSelected;
private void Button_Click(object sender, RoutedEventArgs e)
{
var selected = ItemsComboBox.SelectedValue;
ItemHasBeenSelected?.Invoke(this, new SelectedItemEventArgs { SelectedChoice = selected.ToString() });
}
}
Now in the MainWindow.xaml.cs:
public MainWindow()
{
InitializeComponent();
// Subscribe to the item selected event
MyUC.ItemHasBeenSelected += UCButtonClicked;
Names = new List<string>
{
"A",
"B",
"C"
};
DataContext = this;
}
void UCButtonClicked(object sender, UCComboButton.SelectedItemEventArgs e)
{
var value = e.SelectedChoice;
// Do something with the value
}
Note that the above Names list is what's bound to the user control from the main window XAML.

C# UWP - Update UI to display object values that have been read in by the user

I have been tasked with creating my first UWP App in C#.
The basic idea is to read in an XML file and create objects based on the data read in, then display the properties stored in the object to users in the IU.
Lets say a Person object that has a name, age, and height. I want to display the Person fields after I have read in the data but I can't get anything to show up in the UI after creating the Person object.
I have created a Person class that holds the name, age, height. I have another class that extends ObservableCollection<> and a ItemTemplate that looks for the observable class but currently nothing is showing up on the UI.
Has anyone been through a similar process or know of the correct documentation to read?
Thanks.
First of all in UWP you can choose between two types of binding:
{x:Bind }, is slightly faster at compile time, binds to your Framework Element code-behind class, but it is not as flexible as the other type of binding.
The default mode for this type of binding is OneTime, therefore you will only have your data actually propagated onto your UI, when you construct your object.
{Binding }, in this type of binding where you can only reference variables which exists inside the DataContext of a parent element. The default mode is OneWay.
With that in mind, first of all dealing with a ViewModel which is just a bunch of properties, is different from actually dealing with a Collection, since I don't think the Collection can actually detect alterations on the items itself, but rather on its structure.
Therefore during the Add/Remove process of items in your Collection, you have to actually subscribe/unsubscribe those items to the PropertyChanged EventHandler.
Nevertheless with the following code, i think you should be able to start visualizing updates onto your UI:
VIEWMODEL
public class PersonsObservable<T> : ObservableCollection<Person> where T : INotifyPropertyChanged
{
private PersonsObservable<Person> _personslist;
public PersonsObservable<Person> personslist
{
get { return _personslist; }
set
{
_personslist = value;
_personslist.CollectionChanged += OnObservableCollectionChanged;
}
}
public void OnObservableCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if(e.NewItems != null)
{
foreach (object item in e.NewItems)
((INotifyPropertyChanged)item).PropertyChanged += OnItemPropertyChanged;
}
if(e.OldItems != null)
{
foreach (object item in e.OldItems)
((INotifyPropertyChanged)item).PropertyChanged -= OnItemPropertyChanged;
}
}
public void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((Person)sender));
OnCollectionChanged(args);
}
}
public class Person : INotifyPropertyChanged
{
public Person()
{
_name = "Walter White";
_age = 40;
_height = 180;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private string _name;
public string name
{
get
{
return _name;
}
set
{
_name = value;
this.OnPropertyChanged();
}
}
private int _age;
public int age
{
get
{
return _age;
}
set
{
_age = value;
this.OnPropertyChanged();
}
}
private int _height;
public int height
{
get
{
return _height;
}
set
{
_height = value;
this.OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Add Items
PersonsList.Add(new Person());
}
}
XAML
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Vertical">
<TextBlock Text="DataBinding" Foreground="DarkBlue" FontSize="18" FontWeight="Bold"/>
<ItemsControl ItemsSource="{Binding Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name: "/>
<TextBlock Text="{Binding name, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Age: "/>
<TextBlock Text="{Binding age, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Height: "/>
<TextBlock Text="{Binding height, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Add Items" Click="Button_Click" Background="Blue" VerticalAlignment="Bottom"/>
</StackPanel>
</Grid>
*Test adding items *
private void Button_Click(object sender, RoutedEventArgs e)
{
// Add Items
PersonsList.Add(new Person());
}
Expose your property and set it to the DataContext of your page (with x:Bind you wouldn't need to do this, but instead you would have to perform a cast for your code to actually compile).
public MainPage()
{
InitializeComponent();
PersonsList = new PersonsObservable<Person>();
this.DataContext = PersonsList;
PersonsList.Add(new Person());
PersonsList.Add(new Person());
}
PersonsObservable<Person> PersonsList { get; set; }
I haven't tested for the situation where one of the items is altered, but you can easily do that, by adding another button (and click event) and actually test if changing one of the items's properties update in your UI.
Anything else, feel free to ask, will be glad to help!

wp7 Displaying and Linking the Correct Item

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.

listbox is not updated by dependency property observablecollection

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);
}
}

Categories