i am new to mvvm and hope, someone could help me to understand the following:
I have a Model, ViewModel and a View (UserControl), in the MainWindow there are 3 textboxes and the PersonView, which show several persons. This work. Now i want to select one of the persons (binded to SelectedPerson) and show the information in the 3 textboxes.
But i don't know, how to bind the SelectedPerson from PersonViewModel to the textboxes.
I tried to set the datacontext for the textboxes and the usercontrol to the same, and bind the boxes like Text="{Binding SelectedPerson.ID}", but that doesn't work.
Is there a way, to get the selected person from within the UserControl and display the information, or do i need to put the textboxes also in the usercontrol?
Thanks in advance,
Flo
PersonModel.cs:
namespace MVVMExample.Model
{
public class PersonModel: INotifyPropertyChanged
{
public int ID { get; set; }
private string forname;
public string Forname
{
get { return forname; }
set {
forname = value;
RaisePropertyChanged("Forname");
}
}
private string surname;
public string Surname
{
get { return surname; }
set {
surname = value;
RaisePropertyChanged("Surname");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
PersonViewModel.cs:
public class PersonViewModel
{
public PersonViewModel()
{
Persons = new ObservableCollection<PersonModel>();
LoadPersons();
}
public ObservableCollection<PersonModel> Persons
{
get;
set;
}
public PersonModel SelectedPerson { get; set; }
public void LoadPersons()
{
ObservableCollection<PersonModel> persons = new ObservableCollection<PersonModel>();
persons.Add(new PersonModel { ID = 1, Forname = "Thomas", Surname = "Sogen" });
persons.Add(new PersonModel { ID = 2, Forname = "Seth", Surname = "Xu" });
persons.Add(new PersonModel { ID = 3, Forname = "Derik", Surname = "Mayers" });
persons.Add(new PersonModel { ID = 4, Forname = "Daisy", Surname = "Doros" });
Persons = persons;
}
}
PersonView.xaml (UserControl):
<UserControl x:Class="MVVMExample.Views.PersonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MVVMExample.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="400">
<Grid>
<DataGrid Name="PersonDataGrid" Grid.Row="4" AutoGenerateColumns="False" ItemsSource="{Binding Persons, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" CanUserAddRows="False" IsReadOnly="True" HeadersVisibility="Column" FontSize="14" SelectionMode="Single" SelectedItem="{Binding SelectedPerson}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ID}" Width="*"/>
<DataGridTextColumn Header="Forname" Binding="{Binding Forname}" Width="*"/>
<DataGridTextColumn Header="Surname" Binding="{Binding Surname}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
PersonView.xaml.cs:
public partial class PersonView : UserControl
{
public PersonView()
{
InitializeComponent();
this.DataContext = new MVVMExample.ViewModel.PersonViewModel();
}
}
MainWindow.xaml
<Window x:Class="MVVMExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MVVMExample"
xmlns:views="clr-namespace:MVVMExample.Views"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0"/>
<TextBox Grid.Row="1"/>
<TextBox Grid.Row="2"/>
<views:PersonView x:Name="PersonViewControl" Loaded="PersonViewControl_Loaded" Grid.Row="3"/>
</Grid>
</Window>
Related
I'm learning WPF. Thanks in advance for the help.
I have an object, Directory, that acts as a container for a List of Person objects. I can't figure out why I can't bind my ListBox to the list of Person unless I set the DataContext directly. In other words, I can't use dot notation to access the list as a sub-property of the directory.
Observe the the last line of the C# sharp code below: I set the DataContext to this.directory.People and it works great.
But if I set the DataContext simply to this (to refer to the whole Window) and then try to use dot notation to set my binding like <ListBox ItemsSource="{Binding Path=directory.People}" /> my ListBox is blank.
XAML listed below. Observe the last line of the XAML.
CodeBehind:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public class Directory
{
public List<Person> People = new List<Person>();
public Directory()
{
this.People.Add(new Person() { Name = "Joseph", Age = 34 });
this.People.Add(new Person() { Name = "Teresa", Age = 29});
this.People.Add(new Person() { Name = "Kulwant", Age = 66 });
this.People.Add(new Person() { Name = "Hyunh", Age = 61});
this.People.Add(new Person() { Name = "Marcio", Age = 65 });
}
}
public partial class MainWindow : Window
{
public Directory directory { get; } = new Directory();
public MainWindow()
{
InitializeComponent();
this.DataContext = this.directory.People;
}
}
XAML:
<Window x:Class="WtfDataTrigger.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:t="clr-namespace:System.Threading;assembly=mscorlib"
xmlns:local="clr-namespace:LearningWPF"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Person}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Name="NameLabel" Margin="2" Content="Name" Grid.Column="0" Grid.Row="0" VerticalAlignment="Center" FontWeight="Bold"/>
<TextBlock Name="NameText" Margin="2" Text="{Binding Path=Name}" Grid.Column="1" Grid.Row="0" VerticalAlignment="Center" />
<Label Name="AgeLabel" Margin="2" Content="Age" Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" FontWeight="Bold" />
<TextBlock Name="AgeText" Margin="2" Text="{Binding Path=Age}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{Binding}" />
</StackPanel>
</Window>
WPF data binding works with public properties only. While directory is a public property (but should be named Directory), People is a public field.
Change
public List<Person> People = new List<Person>();
to
public List<Person> People { get; } = new List<Person>();
I am writing an application in C#, WPF, XAML using MVVM patterm.
After many examples that I founded online the data that I try to Bind to the UI is not shown in the screen.
My architecture is : In the MainViewModel I have an ObserverList from type family,
in Family class I have an ObserverList from type Child,
in Child class I have Name.
How to Bind the Child Name in to TextBlock?
Some examples that I founded:
https://msdn.microsoft.com/en-us/library/aa970558%28v=vs.110%29.aspxv
<Window x:Class="DataTemplates.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataTemplates"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<DataTemplate x:Key="MyDataTemplate"
DataType="local:MyData">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="First Name: " />
<TextBlock Grid.Column="1"
Text="{Binding Path=FirstName}" />
<TextBlock Grid.Column="2"
Text="Last Name: " />
<TextBlock Grid.Column="3"
Text="{Binding Path=LastName}" />
<CheckBox Grid.Column="4"
Content="Is Lecturer?"
IsEnabled="False"
IsChecked="{Binding Path=IsLecturer}" />
</Grid>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{Binding}"
ItemTemplate="{StaticResource MyDataTemplate}"
HorizontalContentAlignment="Stretch" />
<Button Content="Add"
Click="Button_Click" />
</StackPanel>
</Window>
and the code Behind
using System.Collections.ObjectModel;
using System.Windows;
namespace CollectionDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<MyData> _myCollection =
new ObservableCollection<MyData>();
public MainWindow()
{
InitializeComponent();
DataContext = _myCollection;
_myCollection.Add(
new MyData
{
FirstName = "Arik",
LastName = "Poznanski",
IsLecturer = true
});
_myCollection.Add(
new MyData
{
FirstName = "John",
LastName = "Smith",
IsLecturer = false
});
}
private int counter = 0;
private void Button_Click(object sender, RoutedEventArgs e)
{
++counter;
_myCollection.Add(
new MyData()
{
FirstName = "item " + counter,
LastName = "item " + counter,
IsLecturer = counter % 3 == 0
});
}
}
}
class my data form the example
public class MyData
{
public string User { get; set; }
public string Password { get; set; }
}
I made a custom example which matches your scenario, take a look at this.
MainWindow.xaml
<Window x:Class="SO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock Height="30" Text="{Binding Parent.Child.SampleText}" HorizontalAlignment="Center"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainViewModel obj = new MainViewModel();
this.DataContext = obj;
}
}
MainViewModel.cs
public class MainViewModel
{
private ParentModel _Parent = new ParentModel();
public ParentModel Parent
{
get { return _Parent; }
set { _Parent = value; }
}
public MainViewModel() //Data Load in Constructor
{
ChildModel child = new ChildModel();
child.SampleText = "Hi, I am Child!";
Parent.Child = child;
}
}
ParentModel.cs
public class ParentModel
{
private ChildModel _Child = new ChildModel();
public ChildModel Child
{
get { return _Child; }
set { _Child = value; }
}
}
ChildModel.cs
public class ChildModel
{
private string _SampleText ;
public string SampleText
{
get { return _SampleText; }
set { _SampleText = value; }
}
}
Corrections in your eg:
Your View and Code behind is perfect.
You didn't add properties in model class which is to be binded in UI.
class MyData
{
//public string User { get; set; }
//public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsLecturer { get; set; }
}
I have a text box, which default value I want to bind to Combo box selecteItem, and same time I want my text box to be binded to Mvvm object property?
I checked here but the multibinding confuse me.
I would prefer to have xaml solution for this issue.
Addition:
In combobox I will select an Account, that account contain some values (Amount), I want to display Amount, But need my text box to be bounded to mvvm model object element stAmount. so the user can change the amount selected by combobbox and then this modified or unchanged amount value could be stored to text box binded model-object element (stAmount)
Making use of INotifyPropertyChanged:
XAML
<Window x:Class="INotifyPropertyChangedExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="INotifyPropertyChanged Example" Width="380" Height="100">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Account Name:" />
<Label Grid.Row="1" Grid.Column="0" Content="Account Balance:" />
<ComboBox Grid.Row="0" Grid.Column="1" Width="200" Height="25" ItemsSource="{Binding AccountsCollection}" SelectedItem="{Binding SelectedAccount}" DisplayMemberPath="Name" />
<TextBox Grid.Column="1" Grid.Row="1" Width="200" Height="25" Text="{Binding SelectedAccount.Balance}" />
</Grid>
</Window>
C#
namespace INotifyPropertyChangedExample
{
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<Account> acctountsCollection;
public ObservableCollection<Account> AccountsCollection
{
get
{
return this.acctountsCollection;
}
set
{
this.acctountsCollection = value;
OnPropertyChanged();
}
}
private Account selectedAccount;
public Account SelectedAccount
{
get
{
return this.selectedAccount;
}
set
{
this.selectedAccount = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
this.AccountsCollection = new ObservableCollection<Account>()
{
new Account { Id = 1, Name = "My super account", Balance = 123.45 },
new Account { Id = 2, Name = "My super account 2", Balance = 543.21 },
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
public double Balance { get; set; }
}
}
In this example we bind an ObservableCollection of Account objects to your ComboBox and keep track of which Account is selected through the SelectedItem property. We bind the TextBox text property to the Balance property of the selected Account object. Therefore when then selected Account object changes the value displayed in the TextBox changes to reflect the Balance of the Account.
Additionally if you change the value in the TextBox, the Balance value of the Account object is updated.
It seems to me like you want bind your textbox to the selected value property in your viewmodel not the combo box.
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(MainWindow), new PropertyMetadata(null));
public string SelectedValue
{
get { return (string)GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(string), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<string>();
Items.Add("Value 1");
Items.Add("Value 2");
Items.Add("Value 3");
Items.Add("Value 4");
Items.Add("Value 5");
Items.Add("Value 6");
}
}
}
and the xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedValue}"/>
<TextBox Grid.Row="1" Text="{Binding SelectedValue}"/>
</Grid>
</Window>
I have what I think is a very simple problem, but for some reason the answer is escaping me. I am creating a simple Master/Detail DataGrid in Silverlight. Most of the samples on the web display this by creating an object with a Collection of some kind, and binding the Detail grid to the collection. In my case, I just want to bind the detail grid to the same object as the row acting as the master. I know my example code is simple, but I'm just trying to make the simplest possible demo to recreate it. That being said, let's say I have this data:
public class Customer
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public string FavoriteColor { get; set; }
}
public class CustomerCollection : ObservableCollection<Customer>
{
public CustomerCollection()
{
Add(new Customer() { CustomerId = 101, CustomerName = "Todd", FavoriteColor = "Red" });
Add(new Customer() { CustomerId = 102, CustomerName = "Melissa", FavoriteColor = "White" });
Add(new Customer() { CustomerId = 102, CustomerName = "Alicia", FavoriteColor = "Blue" });
Add(new Customer() { CustomerId = 104, CustomerName = "Matthew", FavoriteColor = "Yellow" });
}
}
OK. Pretty darn simple. Now, I'm going to bind this collection to a datagrid. Each row should show the CustomerId and CustomerName. And when you click the row, I want to display their favorite color in the details datagrid.
So the question is... How do I bind the details grid so that it shows the Favorite Color? Or in other words, how do bind to the parent row as my data source?
<UserControl x:Class="Sample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="419" d:DesignWidth="742"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:src="clr-namespace:Sample">
<UserControl.Resources>
<src:CustomerCollection x:Key="CustDs"></src:CustomerCollection>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource CustDs}}">
<Grid.RowDefinitions>
<RowDefinition Height="56*" />
<RowDefinition Height="363*" />
</Grid.RowDefinitions>
<TextBlock Name="TextBlock1" Text="Customer Information" FontSize="28" TextAlignment="Center" />
<sdk:DataGrid AutoGenerateColumns="False" Grid.Row="1"
Height="301" HorizontalAlignment="Left" Margin="30,22,0,0"
Name="DgCust" VerticalAlignment="Top" Width="681" ItemsSource="{Binding}"
HeadersVisibility="All" ColumnWidth="*">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Customer Id" Binding="{Binding CustomerId}"></sdk:DataGridTextColumn>
<sdk:DataGridTextColumn Header="Customer Name" Binding="{Binding CustomerName}"></sdk:DataGridTextColumn>
</sdk:DataGrid.Columns>
<sdk:DataGrid.RowDetailsTemplate>
<DataTemplate>
<sdk:DataGrid Height="200" Width="600" AutoGenerateColumns="False" ColumnWidth="*"
ItemsSource="{Binding}">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Favorite Color" Binding="{Binding}"></sdk:DataGridTextColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</DataTemplate>
</sdk:DataGrid.RowDetailsTemplate>
</sdk:DataGrid>
</Grid>
</UserControl>
Your data doesn't represent a Master/Detail scenario. If you just want to display the favorite color in a details area, do this in the DataTemplate section:
<sdk:DataGrid.RowDetailsTemplate>
<DataTemplate>
<TextBlock Text="{Binding FavoriteColor}" />
</DataTemplate>
</sdk:DataGrid.RowDetailsTemplate>
If you actually want Master/Details, try this instead:
public class Customer
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public string FavoriteColor { get; set; }
public List<FavoriteShow> Shows { get; set; }
}
public class FavoriteShow
{
public string Name {get;set;}
public int Stars {get;set;}
}
public class CustomerCollection : ObservableCollection<Customer>
{
public CustomerCollection()
{
List<FavoriteShow> showList1 = new List<FavoriteShow>();
showList1.Add(new FavoriteShow { Name="Bugs Bunny", Stars = 4});
showList1.Add(new FavoriteShow { Name="Superman", Stars=2});
showList1.Add(new FavoriteShow { Name="A-Team", Stars=3});
List<FavoriteShow> showList2 = new List<FavoriteShow>();
showList2.Add(new FavoriteShow { Name = "Dallas", Stars = 1 });
showList2.Add(new FavoriteShow { Name = "Loony Tunes", Stars = 3 });
Add(new Customer() { CustomerId = 101, CustomerName = "Todd", FavoriteColor = "Red", Shows = showList1 });
Add(new Customer() { CustomerId = 102, CustomerName = "Melissa", FavoriteColor = "White" });
Add(new Customer() { CustomerId = 102, CustomerName = "Alicia", FavoriteColor = "Blue", Shows = showList2 });
Add(new Customer() { CustomerId = 104, CustomerName = "Matthew", FavoriteColor = "Yellow" });
}
}
And the XAML:
<UserControl x:Class="Sample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="419"
d:DesignWidth="742"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:src="clr-namespace:Sample">
<UserControl.Resources>
<src:CustomerCollection x:Key="CustDs"></src:CustomerCollection>
</UserControl.Resources>
<Grid x:Name="LayoutRoot"
Background="White"
DataContext="{Binding Source={StaticResource CustDs}}">
<Grid.RowDefinitions>
<RowDefinition Height="56*" />
<RowDefinition Height="363*" />
</Grid.RowDefinitions>
<TextBlock Name="TextBlock1"
Text="Customer Information"
FontSize="28"
TextAlignment="Center" />
<sdk:DataGrid AutoGenerateColumns="False"
Grid.Row="1"
Height="301"
HorizontalAlignment="Left"
Margin="30,22,0,0"
Name="DgCust"
VerticalAlignment="Top"
Width="681"
ItemsSource="{Binding}"
HeadersVisibility="All"
ColumnWidth="*">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Customer Id"
Binding="{Binding CustomerId}"></sdk:DataGridTextColumn>
<sdk:DataGridTextColumn Header="Customer Name"
Binding="{Binding CustomerName}"></sdk:DataGridTextColumn>
</sdk:DataGrid.Columns>
<sdk:DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FavoriteColor}" />
<sdk:DataGrid ItemsSource="{Binding Shows}" />
</StackPanel>
</DataTemplate>
</sdk:DataGrid.RowDetailsTemplate>
</sdk:DataGrid>
</Grid>
I have a DataBinding on a ListBox, bound to an ObservableCollection. Debugging at runtime shows the ObservableCollection does have items in it, and they're not null. My code all looks fine, however for some reason nothing is being displayed in my ListBox. It definitely was working previously, however it no longer is - and I can't figure out why. I've examined previous versions of the code and found no differences that would have any effect on this - minor things like Width="Auto" etc.
I based my code off of the example found here:
http://msdn.microsoft.com/en-us/library/hh202876.aspx
So, my code:
XAML:
<phone:PhoneApplicationPage
x:Class="MyNamespace.MyItemsListPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="PageTitle" Text="MyPageTitle" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<!-- Bind the list box to the observable collection. -->
<ListBox x:Name="myItemsListBox" ItemsSource="{Binding MyItemsList}" Margin="12, 0, 12, 0" Width="440">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Width="440">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding MyItemNumber}"
FontSize="{StaticResource PhoneFontSizeLarge}"
Grid.Column="0"
VerticalAlignment="Center"
Margin="0,10"
Tap="TextBlock_Tap"/>
<TextBlock
Text="{Binding MyItemName}"
FontSize="{StaticResource PhoneFontSizeLarge}"
Grid.Column="1"
VerticalAlignment="Center"
Margin="0,10"
Tap="TextBlock_Tap" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
C#:
namespace MyNamespace
{
public partial class MyItemsListPage : PhoneApplicationPage, INotifyPropertyChanged
{
private static ObservableCollection<MyItem> _myItemsList;
private ObservableCollection<MyItem> MyItemsList
{
get
{
return _myItemsList;
}
set
{
if (_myItemsList!= value)
{
_myItemsList= value;
NotifyPropertyChanged("MyItemsList");
}
}
}
public MyItemsListPage ()
{
InitializeComponent();
this.DataContext = this;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
HelperClass helper = new HelperClass();
MyItemsList = helper.GetItems(this.NavigationContext.QueryString["query"]);
base.OnNavigatedTo(e); // Breakpoint here shows "MyItemsList" has MyItem objects in it.
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify Silverlight that a property has changed.
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
The Helper class is a connector to my read-only local database on the device. It returns an ObservableCollection<MyItem>:
public ObservableCollection<MyItem> GetItems(string itemName)
{
// Input validation etc.
// Selecting all items for testing
var itemsInDB =
from MyItem item in db.Items
select item;
return new ObservableCollection<MyItem>(itemsInDB);
}
And finally the MyItem class:
[Table]
public class MyItem: INotifyPropertyChanged, INotifyPropertyChanging
{
private int _myItemId;
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int MyItemId
{
...
}
private string _myItemName;
[Column(CanBeNull = false)]
public string MyItemName
{
get
{
return _myItemName;
}
set
{
if (_myItemName!= value)
{
NotifyPropertyChanging("MyItemName");
_myItemName= value;
NotifyPropertyChanged("MyItemName");
}
}
}
private int _myItemNumber;
[Column]
public int MyItemNumber
{
get
{
return _myItemNumber;
}
set
{
if (_myItemNumber!= value)
{
NotifyPropertyChanging("MyItemNumber");
_myItemNumber= value;
NotifyPropertyChanged("MyItemNumber");
}
}
}
// Other properties, NotifyPropertyChanged method, etc...
}
This is rather frustrating as my DataBinding elsewhere in the application is working perfectly, so I've no idea why I can't get this to work.
The problem was that my ObservableCollection was private. Changing it to have a public access modifier allowed my ListBox to display the contents:
public ObservableCollection<MyItem> MyItemsList
Simply that you're binding to properties named incorrectly:
Text="{Binding ItemName}" should be Text="{Binding MyItemName}"
Notice you left out "My"