I have a ListBox where items are filtered based on text entered in a textbox (and when enter is pressed):
<TextBox DockPanel.Dock="Top" Margin="0,0,0,20" Width="200" HorizontalAlignment="Left" Text="{Binding Path=FilterText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=FilterSearchCommand}" Key="Enter" />
</TextBox.InputBindings>
</TextBox>
<ListBox DockPanel.Dock="Bottom" Name="lbItems" ItemsSource="{Binding Path=MyList, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Width="{Binding ActualWidth, ElementName=lbItems}" Cursor="Hand" Margin="10,10,0,10" VerticalAlignment="Center" MouseLeftButtonUp="UIElement_OnMouseLeftButtonUp">
<TextBlock Text="{Binding Path=Title}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When ENTER is pressed in the textbox, the following command is executed:
FilterSearchCommand = new RelayCommand(() => {
MyList = new ObservableCollection < MyObject > (MyList.Where(x => x.Title.IndexOf(FilterText, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList());
});
public RelayCommand FilterSearchCommand {
get;
}
public string FilterText {
get;
set;
}
public ObservableCollection < MyObject > MyList {
get;
set;
}
Basically on entering the command, the ObservableCollection is successfully updated, however the items in the list box remain unchanged.
Any ideas?
You are going to have a bit of an issue here - Upon a search, you will overwrite your 'MyList' object. So, once you filter, you cannot un-filter. You should look into using a CollectionViewSource.
You need to implement INotifyPropertyChanged, and in the setter of MyList you will notify the UI that the property is changed. Here's an example:
class MyViewModel : INotifyPropertyChanged
{
private ObservableCollection<MyObject> _myList;
public ObservableCollection<MyObject> MyList
{
get { return _myList; }
set
{
_myList = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Related
My Problem is similar to the one described in here, but goes one step further.
Inside a StackPanel are a ListBox to select between Elements, and a ContentPresenter rendering two RadioButtons. These RadioButtons are bound to the Boolean Properties MemberA and MemberB of the selected Element and are defined inside the ContentTemplate of the ContentPresenter. The GroupName of the RadioButtons are also bound to the GroupName-Property of the Element.
XAML:
<Window x:Class="WpfRadioButtonBugTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfRadioButtonBugTest"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:TestViewModel />
</Window.DataContext>
<StackPanel Orientation="Vertical">
<ListBox ItemsSource="{Binding Path=Elements, Mode=OneWay}"
SelectedItem="{Binding Path=SelectedElement, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:Element}">
<TextBlock Text="{Binding Path=GroupName, Mode=OneWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ContentPresenter Content="{Binding Path=SelectedElement, Mode=OneWay}">
<ContentPresenter.ContentTemplate>
<DataTemplate DataType="{x:Type local:Element}">
<StackPanel Orientation="Vertical">
<RadioButton GroupName="{Binding Path=GroupName, Mode=OneWay}"
IsChecked="{Binding Path=MemberA, Mode=TwoWay}"
Content="MemberA" />
<RadioButton GroupName="{Binding Path=GroupName, Mode=OneWay}"
IsChecked="{Binding Path=MemberB, Mode=TwoWay}"
Content="MemberB" />
</StackPanel>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</StackPanel>
</Window>
ViewModel:
public class TestViewModel : PropertyChangedBase
{
public ObservableCollection<Element> Elements { get; }
= new ObservableCollection<Element>(Enumerable.Range(0, 2).Select(x => new Element(x + 1)));
private Element _selectedElement;
public Element SelectedElement
{
get => _selectedElement ?? (_selectedElement = Elements.First());
set
{
_selectedElement = value;
OnPropertyChanged();
}
}
}
public class Element : PropertyChangedBase
{
private bool _memberA = true;
public bool MemberA
{
get => _memberA;
set
{
_memberA = value;
OnPropertyChanged();
}
}
private bool _memberB;
public bool MemberB
{
get => _memberB;
set
{
_memberB = value;
OnPropertyChanged();
}
}
public string GroupName { get; }
public Element(int i) => GroupName = i.ToString();
}
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
If you press the RadioButton for MemberB of the First Element and switch back and forth between the Elements, the RadioButtons always represent the correct values.
Here comes the fun part: Add a Button to the top of the StackPanel:
...
<StackPanel Orientation="Vertical">
<Button />
<ListBox ItemsSource="{Binding Path=Elements, Mode=OneWay}"
...
This leads to unexpected behavior: Selecting MemberB of Element 1, then Selecting Element 2, then Selecting Element 1 again results in no RadioButtons being selected. Setting a Breakpoint on the Setter of MemberB shows that it is set to false as soon as the other Element is selected in the ListBox.
The problem is not present without the Button, or without the RadioButtons being in a Template. Is there any Reason why an additional Button in a Parent Panel should change the behavior of RadioButtons in a Template?
EDIT: Minimal Application to reproduce uploaded here
I'm relatively fresh to WPF and discovering XAML.
I've got one ListBox Name="EmployeeTitles" in XAML (shown below). For the purpose of demonstrating the problem DataContext = employees which is ObservableCollection<Employee>.
<ListBox Name="EmployeeTitles"
ItemsSource="{Binding}"
SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ToggleButton IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
<!--StackPanel will have more items-->
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</ToggleButton>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My "Code Behind" class snippet looks as follows:
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged();
}
}
private bool _isSelected;
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
OnPropertyChanged();
}
}
I would like to show the selected items in a different control. For example have a list that shows only selected items. Can I somehow mark in XAML that I only only want to display items with IsChecked="True"? I know I could have another ObservableCollection storing only selected items and updating it in "Code Behind" whenever property IsSelected changes, but that seems like an overhead and I suppose there should be a way to do it in XAML?
You can bind the other listbox with the selecteditems of the original listbox. Try the below code.
<Grid>
<StackPanel Orientation="Horizontal">
<ListBox x:Name="Emp" ItemsSource="{Binding EmpCollection}" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ToggleButton IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
<!--StackPanel will have more items-->
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</ToggleButton>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="SelectedEmp" ItemsSource="{Binding ElementName=Emp,Path=SelectedItems}" DisplayMemberPath="Title"/>
</StackPanel>
</Grid>
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
class ViewModel
{
public ObservableCollection<Emp> EmpCollection { get; set; }
public ViewModel()
{
EmpCollection = new ObservableCollection<Emp>();
for (int i = 0; i < 10; i++)
{
EmpCollection.Add(new Emp() {Title = "Title"+i});
}
}
}
class Emp:INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged();
}
}
private bool _isSelected;
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have the following requirements:
Window will show a ListView with multiple items.
User should be able to check (Checkbox) any item.
a) If one item, all items should be unchecked and disabled.
b) If checked item is unchecked, than all items should be enabled.
As of now, I have the following incomplete code.
MainWindow XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="520.149" Width="732.463">
<Window.Resources>
<ResourceDictionary Source="MainWindowResource.xaml" />
</Window.Resources>
<Grid>
<ListView x:Name="myListBox" ItemTemplate="{StaticResource OfferingTemplate}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
</Window>
DataTemplete for ListView:
<DataTemplate x:Key="OfferingTemplate">
<StackPanel>
<Grid IsEnabled="{Binding IsEnabled}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"></ColumnDefinition>
<ColumnDefinition Width="120"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Rectangle Grid.Column="0" Grid.RowSpan="3" Fill="#F4CA16" />
<Label
Grid.Column="1"
Grid.Row="0"
Content="{Binding Title}"
FontSize="18" FontWeight="Bold"
Margin="0,0,0,0" />
<TextBlock
Grid.Column="1"
Grid.Row="1"
FontSize="10"
Text="{Binding Description}"
Foreground="Black"
TextWrapping="WrapWithOverflow"
Margin="5,0,0,0" />
<CheckBox
Grid.Column="1"
Grid.Row="2"
FontSize="14"
IsChecked="{Binding IsSelected}"
VerticalAlignment="Bottom"
Margin="5,0,0,0">
<TextBlock Text="Select" Margin="0,-2,0,0"/>
</CheckBox>
</Grid>
</StackPanel>
</DataTemplate>
Model:
class MyModel
{
public string Title { get; set; }
public string Description { get; set; }
public bool IsSelected { get; set; }
public bool IsEnabled { get; set; }
}
ViewModel:
class MyViewModel : INotifyPropertyChanged
{
private MyModel offering;
public MyViewModel()
{
offering = new MyModel();
}
public int ID { get; set; }
public string Title
{
get { return offering.Title; }
set
{
offering.Title = value;
RaisePropertyChanged("Title");
}
}
public string Description
{
get { return offering.Description; }
set
{
offering.Description = value;
RaisePropertyChanged("Description");
}
}
public bool IsSelected
{
get { return offering.IsSelected; }
set
{
offering.IsSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public bool IsEnabled
{
get { return offering.IsEnabled; }
set
{
offering.IsEnabled = value;
RaisePropertyChanged("IsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This is an interesting question. Since the action you want applies to all items in the list, this logic should in list class level. Your MyViewModel class is fine. You need add some logic in your list class and XAML but thanks to Prism, it is quite easy.
The list class (not shown in your post) Contains:
public ObservableCollection<MyViewModel> MyItems { get; set; } //Binding to ItemsSource
private ICommand _selectCommand;
public ICommand SelectCommand
{
get { return _selectCommand ?? (_selectCommand = new DelegateCommand<MyViewModel>(DoSelect)); }
}
private void DoSelect(MyViewModel myViewModel)
{
foreach(var item in MyItems)
if (item != myViewModel)
{
item.IsSelected = false;
item.IsEnabled = false;
}
}
private ICommand _unselectCommand;
public ICommand UnselectCommand
{
get { return _unselectCommand ?? (_unselectCommand = new DelegateCommand<MyViewModel>(DoUnselect)); }
}
private void DoUnselect(MyViewModel myViewModel)
{
foreach (var item in MyItems)
if (item != myViewModel)
{
item.IsEnabled = true;
}
}
There are two commands, one for selecting and the other for unselecting. The magic is on XAML:
<ListView ItemsSource="{Binding Path=MyItems}" x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected}" IsEnabled="{Binding Path=IsEnabled}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.SelectCommand}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.UnselectCommand}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Using Prism's triggers, you can map CheckBox's Checked and Unchecked event to your list view model's commands and passing the item view model as parameter.
It is working perfectly but one thing is annoying, that setting item's IsSelected is separate. When you check a CheckBox, the item behind is set to true through DataBinding but all others are set through parent view model. If your post is all your requirement, you can remove IsChecked binding and put the logic of setting one IsSelected inside list view model, which looks clenaer and easier to write test code.
My problem:
I have a listbox with owners of dogs, and i have a listbox with dogs. I want to modify the dogs listbox itemtemplate as the following: DogName(textblock)+DogKind(textblock)+Owners(combobox).The first two was successful, but i cant add the existing owners to the combobox. If i give a name to my combobox like :
<ComboBox x:Name="mycombo" />
i cant see the mycombo variable in the c# code.
The XAML:
<Window x:Class="CodeFirst.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sajat="clr-namespace:CodeFirst"
Title="MainWindow" Height="557.638" Width="721.294"
>
<Grid x:Name="grid1">
<ListBox x:Name="listbox2" HorizontalAlignment="Left" Height="313" Margin="338,10,0,0" VerticalAlignment="Top" Width="250">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text=", "/>
<TextBlock Text="{Binding Path=Kind}"/>
<ComboBox />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
How can i give the itemsource to the combobox, or how can i reach to add the owners?
If you use the DataContext, you can set the Binding like this:
<ComboBox ItemsSource="{Binding Path=DataContext.MyItemsSource, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></ComboBox>
First of all, in order to work with WPF or other XAML-based technologies, you must understand that
UI is not Data. Data is Data. UI is UI.
This means that you should not manipulate any ComboBox or any other UI elements in code, in order to populate them with data, but instead create a ViewModel and bind these objects to that.
In this example, the Window itself is used as ViewModel because it's a simple example, but you should consider moving all application logic to a separate class:
<Window x:Class="MiscSamples.UIisNotData"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UIisNotData" Height="300" Width="300">
<UniformGrid Rows="1" Columns="2">
<DockPanel>
<TextBlock Text="Owners:" DockPanel.Dock="Top" FontWeight="Bold" TextAlignment="Center" Margin="2"/>
<Button Content="Add" Width="80" DockPanel.Dock="Bottom" Margin="2" Click="AddOwner"/>
<ListBox ItemsSource="{Binding Owners}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Name}" x:Name="block"/>
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Visibility="Collapsed" x:Name="box"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem}}" Value="True">
<Setter TargetName="block" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="box" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<DockPanel>
<TextBlock Text="Dogs:" DockPanel.Dock="Top" FontWeight="Bold" TextAlignment="Center" Margin="2"/>
<ListBox ItemsSource="{Binding Dogs}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<ComboBox ItemsSource="{Binding DataContext.Owners, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
SelectedItem="{Binding Owner}" DisplayMemberPath="Name"
DockPanel.Dock="Right" Width="100"/>
<TextBlock>
<Run Text="{Binding Name}"/>
<Run Text=", "/>
<Run Text="{Binding Kind}"/>
</TextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</UniformGrid>
</Window>
Code Behind (This code should be placed in a ViewModel):
public partial class UIisNotData : Window
{
public ObservableCollection<Owner> Owners { get; set; }
public ObservableCollection<string> Kinds { get; set; }
public ObservableCollection<Dog> Dogs { get; set; }
public UIisNotData()
{
InitializeComponent();
Owners = new ObservableCollection<Owner>
{
new Owner() {Name = "Jack"},
new Owner() {Name = "Mike"},
new Owner() {Name = "Kirk"},
new Owner() {Name = "John"},
};
Kinds = new ObservableCollection<string>
{
"Affenpinscher",
"Afghan Hound",
"Airedale Terrier",
"Akita"
//.. All the rest of dog Breeds taken from http://www.petmd.com/dog/breeds?breed_list=az#.UVsQKpPcmQo
};
Dogs = new ObservableCollection<Dog>
{
new Dog() {Name = "Bobby", Kind = Kinds[0], Owner = Owners[0]},
new Dog() {Name = "Fido", Kind = Kinds[1], Owner = Owners[1]},
new Dog() {Name = "Toby", Kind = Kinds[2], Owner = Owners[2]}
};
DataContext = this;
}
private void AddOwner(object sender, RoutedEventArgs e)
{
Owners.Add(new Owner(){Name = "New Owner"});
}
}
Data Model:
public class Owner : PropertyChangedBase
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public class Dog: PropertyChangedBase
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
private Owner _owner;
public Owner Owner
{
get { return _owner; }
set
{
_owner = value;
OnPropertyChanged("Owner");
}
}
private string _kind;
public string Kind
{
get { return _kind; }
set
{
_kind = value;
OnPropertyChanged("Kind");
}
}
}
PropertyChangedBase Class:
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Result:
There are 3 important aspects you need to consider about this example:
I am in no way manipulating UI elements in code. That's completely unnecessary most of the time in WPF.
The classes from the Data Model implement INotifyPropertyChanged in order to support 2-way binding in WPF.
The Collections are of type ObservableCollection<T> in order to support automatic notification when elements are added/removed from the collection (in order to automatically update the ListBoxes, etc).
Another thing you may notice is that the XAML elements in my example have no specific size or Margin values. Things like Margin="338,10,0,0" is usually what you get from the Visual Studio designer and indicates a poorly structured layout. I recommend you look at the Layout elements in WPF (DockPanel, StackPanel, Grid, UniformGrid, WrapPanel, etc), and start coding the XAML yourself instead of using the designer. This will allow a much higher level of scalability and will also save you from the nuances of Fixed-position elements.
I posted a similar question earlier, but I was having an issue with getting data from the ViewModel into the View. The issue lies with getting the data out of the object where it is stored when it is time to bind to the View. I created a class that declares 3 items which I use to help populate an ObservableCollection of items that will be bound to a ListBox in the view. I am not sure if I am going about this correctly, so to illustrate I will show below:
ListItem.cs (this is the custom class I defined to help populate the collection of items)
public string Favicon
{
get;
set;
}
public string Name
{
get;
set;
}
public string Address
{
get;
set;
}
MainPage.xaml.cs (here I want to save the data for each item to be added in the ObservableCollection)
void addToFavorites_Click(object sender, EventArgs e)
{
var favoriteItem = new ListItem { Favicon = "/Image/1.jpg", Name = "item1", Address = "some address" };
Settings.FavoritesList.Value.Add(favoriteItem);
}
Settings.cs (the settings class used to store the FavoritesList ObservableCollection)
public class Settings
{
public static Setting<ObservableCollection<ListItem>> FavoritesList = new Setting<ObservableCollection<ListItem>>("Favorites", new ObservableCollection<ListItem>());
}
Now I am attempting to call this stored ObservableCollection FavoritesList in my ViewModel so that I may bind it to a view in another page.
MainViewModel.cs
public ObservableCollection<ListItem> FavoriteItems { get; private set; }
public MainViewModel()
{
FavoriteItems = Settings.FavoritesList.Value;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And then on navigation to my FavoritesPage.xaml, I would like to bind the ViewModel to the View to be displayed in a listbox
FavoritesPage.xaml
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}" SelectionChanged="FavoritesListBox_SelectionChanged">
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}" Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress" Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</ListBox>
FavoritesPage.xaml.cs
public FavoritesPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
DataContext = App.ViewModel;
}
Now for some reason I cannot set DataContext = App.ViewModel;. I believe I narrowed the problem to when I initially saved the values in the MainPage.xaml.cs using the ListItem class. I am unsure of how to populate the ListPicker from here? Am I doing something wrong somewhere, or should I do something different to set the datacontext correctly?
The setting of the DataContext doesn't look wrong, as long as App.ViewModel is correctly set to an instance of your MainViewModel class.
However, you are defining your ListBox XAML incorrectly.
In order to define how your items will be displayed in a ListBox, you must use the ItemsControl.ItemTemplate property.
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}" SelectionChanged="FavoritesListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}" Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress" Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In App.xaml.cs do:
private static MainViewModel viewModel = null;
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
{
viewModel = new MainViewModel();
}
return viewModel;
}
}
In your Xaml do as Daniel recommended:
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}" SelectionChanged="FavoritesListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}" Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress" Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In your MainViewModel.cs do:
public ObservableCollection<ListItem> FavoriteItems
{
get;
private set;
}
Now your DataContext = App.ViewModel should work.
Implement the IS settings like shown here