Okay, sorry for my previous mess.
The situation is this:
I have two custom objects defined as follows:
MainObject:
public class MainObject
{
private string mainObjectName;
public string MainObjectName { get { return mainObjectName; } }
private List<SubObject> subObjectData;
public List<SubObject> SubObjectData { get { return subObjectData; } }
public MainObject(string name, List<SubObject> objectData)
{
mainObjectName = name;
subObjectData = objectData;
}
}
SubObject:
public class SubObject
{
private string subObjectName;
public string SubObjectName { get { return subObjectName; } }
private List<int> integerData;
public List<int> IntegerData { get { return integerData; } }
public SubObject(string name, List<int> data)
{
subObjectName = name;
integerData = data;
}
}
I also have a viewmodel which for simplicity defines some data using those two objects as follows:VM
public List<Model.MainObject> VMList = new List<Model.MainObject>()
{
new Model.MainObject("MainItem1", new List<Model.SubObject>()
{
new Model.SubObject("SubItem1", new List<int>() { 1,6,3}),
new Model.SubObject("SubItem2", new List<int>() { 5,2,9})
}),
new Model.MainObject("MainItem2", new List<Model.SubObject>()
{
new Model.SubObject("SubItem1", new List<int>() { 0,3,1}),
new Model.SubObject("SubItem2", new List<int>() { 7,5,2})
})
};
now I have the following UI
<Grid>
<ItemsControl Name="MainObjectIC">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding MainObjectName}"/>
<ItemsControl Name="SubObjectIC">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SubObjectName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I assign the ItemsSource of the MainObjectIC in the code behind like so:
ViewModel.VM dc = new ViewModel.VM();
public MainWindow()
{
InitializeComponent();
DataContext = dc;
MainObjectIC.ItemsSource = dc.VMList;
}
I also want to assign the ItemsSource to the SubObjectIC but to do that I have to get that ItemsControl object. And this is what I am trying to achieve.
From what I understood it may be a very very bad and useless to assign the ItemsSource property from the code behind.
Thank you for improving your code example. It still wasn't quite complete, but it's close enough to be able to provide an answer.
In your example, the main thing missing is to simply add the necessary {Binding} expression. In particular:
<ItemsControl Name="SubObjectIC" Grid.Column="1"
ItemsSource="{Binding SubObjectData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SubObjectName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The context for the item already is an object of type MainObject (which is why your TextBlock binding works). So all that remains to be done is bind the ItemsSource property to the MainObject.SubObjectData property.
(I had to add the Grid.Column assignment, which appeared to be missing from your example above.)
The above change is completely sufficient to get your example to work as you desire. However, you can also improve the code by using the same basic approach to the top-level control as well. To do so, your VM.VMList field needs to be changed to be a property (WPF only binds to properties, not fields):
class VM
{
public List<MainObject> VMList { get { return _vmList; } }
private readonly List<MainObject> _vmList = new List<MainObject>()
{
new MainObject("MainItem1", new List<SubObject>()
{
new SubObject("SubItem1", new List<int>() { 1,6,3}),
new SubObject("SubItem2", new List<int>() { 5,2,9})
}),
new MainObject("MainItem2", new List<SubObject>()
{
new SubObject("SubItem1", new List<int>() { 0,3,1}),
new SubObject("SubItem2", new List<int>() { 7,5,2})
})
};
}
Then you can just remove the explicit assignment that's in your constructor:
public MainWindow()
{
InitializeComponent();
DataContext = dc;
}
With those changes, your XAML no longer needs to give any of the controls names, and you can bind directly to the relevant properties:
<Window x:Class="TestSO42929995WpfNestedData.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:TestSO42929995WpfNestedData"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ItemsControl ItemsSource="{Binding VMList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding MainObjectName}"/>
<ItemsControl Grid.Column="1"
ItemsSource="{Binding SubObjectData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SubObjectName}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
A key point that might not be obvious in the above is that every control has a DataContext. When you using the {Binding} syntax, by default the property path is relative to that context. In the top-level control, the context is what you set it to be in the constructor. But in the individual list item template, the context is the individual data object for that list item, which in your case is the MainObject object. So in that context, you just bind to the SubObjectData property, just like you bind to the MainObjectName. It works exactly the same, and for the same reason.
Related
I'm trying to bind List<> to ListView. When i'm updating i'm going to clear the list. Clear on ObservableCollection was kind of slow.
Problem is that in the view things are not updated correctly.
XAML
<StackPanel.Resources>
<ResourceDictionary>
<common:BoolToBackgroundConverter x:Key="BoolToBackground"/>
<tb:StringInlineCollectionConvertor x:Key="InlineConvert"/>
</ResourceDictionary>
</StackPanel.Resources>
<ListView ItemsSource="{Binding NotificationsCollection, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
ScrollViewer.CanContentScroll="False">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<Grid Background="{Binding NotSeen,Converter={StaticResource BoolToBackground},UpdateSourceTrigger=PropertyChanged,NotifyOnSourceUpdated=True}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Thumb}"/>
<tb:BindableTextBlock InlineCollection="{Binding Text, Converter={StaticResource InlineConvert}}"/>
<TextBlock Text="{Binding Created}"/>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C#
ViewModel
public List<NotificationDataModel> notificationsCollection;
public List<NotificationDataModel> NotificationsCollection
{
get
{
if (notificationsCollection == null)
{
notificationsCollection = new List<NotificationDataModel>();
}
return notificationsCollection;
}
set
{
if (notificationsCollection == null)
{
notificationsCollection = new List<NotificationDataModel>();
}
notificationsCollection.Clear();
foreach (var item in value)
{
notificationsCollection.Add(item);
}
this.OnPropertyChanged("NotificationsCollection");
}
}
public void UpdateNotifications1()
{
List<NotificationDataModel> newCollection = new List<NotificationDataModel>();
newCollection.Add(item1);
newCollection.Add(item2);
newCollection.Add(item3);
newCollection.Add(item4);
newCollection.Add(item5);
newCollection.Add(item6);
this.NotificationsCollection = newCollection;
}
public void UpdateNotifications2()
{
List<NotificationDataModel> newCollection = new List<NotificationDataModel>();
newCollection.Add(item1);
newCollection.Add(item2);
newCollection.Add(item6);
this.NotificationsCollection = newCollection;
}
When i call UpdateNotifications1 elements are show accordingly but after that when i call UpdateNotifications2 i see item1, item2 and item3 instead of item6.
Also items are getting new value for the NotSeen(black for example, and initial is white) property after closing the view and on reopening those items should have white but they are still with black background.
The problem is that raising the PropertyChanged event in your NotificationsCollection setter is ineffective when the actual value of the underlying field hasn't changed. The target ItemsSource property receives the same List instance (from the binding) and therefore does not trigger a UI update.
So do not clear and copy to an existing collection, but use the one passed to the setter instead:
public List<NotificationDataModel> NotificationsCollection
{
get { return notificationsCollection; }
set
{
notificationsCollection = value;
OnPropertyChanged("NotificationsCollection");
}
}
well either create a new list every time or use something like ObservableCollection<>
Sorry if I replicate, but I'm having a hard time wording this for a decent query.
I'm trying to filter a set of complex objects through an ICollectionView and then display each object that made it through, represented by one of their properties. In this particular example, the collection is a list of ComplexClass objects, each of which containing a Name and Number property. I wish to represent each in a ListBox, sitting within a Popup, by their Name.
Here's the XAML code for the MainWindow:
<Window
x:Name="mainWindow"
x:Class="TestWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:TestWPF"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
</Window.Resources>
<Grid x:Name="mainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="buttonStack">
<Button x:Name="popupButton" Click="popupButton_Click" Content="Pop-up!" VerticalAlignment="Top"/>
<Button x:Name="randomInsertButton" Click="randomInsertButton_Click" Content="Random Addition!"/>
</StackPanel>
<Popup x:Name="testPopup" PlacementTarget="{Binding ElementName=popupButton}" PopupAnimation="Scroll" Placement="Left"
AllowsTransparency="True" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid>
<Button x:Name="popupsButton" Content="Button" Width="75" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ListBox x:Name="testListBox" Height="100" DataContext="{Binding ElementName=mainWindow}" ItemsSource="{Binding Source=strings}" ScrollViewer.VerticalScrollBarVisibility="Visible" SelectionChanged="testListBox_Selected"/>
</Grid>
</Popup>
</Grid>
</Window>
The MainWindow's code-behind:
namespace TestWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
static List<ComplexClass> stringList = new List<ComplexClass>();
public ObservableCollection<ComplexClass> strings;
public MainWindow()
{
InitializeComponent();
stringList.Add(new ComplexClass("apple",0));
stringList.Add(new ComplexClass("bat",2));
stringList.Add(new ComplexClass("cattle",6));
stringList.Add(new ComplexClass("dogma",5));
strings = new ObservableCollection<ComplexClass>(stringList);
Binding binding = new Binding();
binding.Source = strings;
testListBox.SetBinding(ListBox.ItemsSourceProperty, binding);
}
private void popupButton_Click(object sender, RoutedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(strings);
view.Filter =
//null;
(o) =>
{
return (o as ComplexClass).Name!=string.Empty;
};
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Descending));
testPopup.IsOpen = !testPopup.IsOpen;
}
private void randomInsertButton_Click(object sender, RoutedEventArgs e)
{
Random r = new Random();
stringList.Add(stringList[r.Next(0, stringList.Count)]);
strings.Add(stringList.Last());
}
private void testListBox_Selected(object sender, RoutedEventArgs e)
{
ComplexClass selected =(ComplexClass)(sender as ListBox).SelectedItem;
stringList.Add(selected);
strings.Add(selected);
}
}
}
And finally, the ComplexClass code:
namespace TestWPF
{
public class ComplexClass
{
public string Name { get; private set; }
public int Number { get; private set; }
public ComplexClass(string name, int number)
{
Name = name;
Number = number;
}
}
}
What it's currently doing is displaying each of the objects as if they've been ToString()-ed: "TestWPF.ComplexClass".
I actually want them to be displayed as:
dogma
cat
bat
apple
in that order.
Can I get some help with this?
A couple of issues here.
First, you are binding to the list in your XAML:
<ListBox ... ItemsSource="{Binding Source=strings}" ... />
but then also setting the binding in code:
Binding binding = new Binding();
binding.Source = strings;
testListBox.SetBinding(ListBox.ItemsSourceProperty, binding);
You need to do one or the other: I'd suggest binding in XAML, and removing the binding stuff from the code-behind.
Second, the ComplexClass items are being ToString-ified because that's the default behaviour of a ListBoxItem when supplied with content (the items in a WPF ListBox are wrapped inside ListBoxItem elements). The easiest way for you to fix this is to set the DisplayMemberPath property on the ListBox:
<ListBox ... ItemsSource="{Binding Source=strings}" DisplayMemberPath="Name" ... />
I´m trying to bind a ListBox to a ObservableCollection. I wan´t to bind the Text Properties of the ListBox entrys and the Background of the ListBox entrys.
The ListBox is defined in an loaded loose xaml file:
<TextBox Margin="0,5,5,5" Text="{Binding Path=TB9P}" Background="LightBlue" Name="DetailsviewTB9" Height="20">
<TextBox.ToolTip>
<StackPanel>
<Label FontWeight="Bold" Background="Blue" Foreground="White">Daten</Label>
<ListBox ItemsSource="{Binding Source={StaticResource res_LB1P}}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemP, Converter={StaticResource c_SelectedItemToBackgroundConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</TextBox.ToolTip>
</TextBox>
The DataContext is set on class DetailsViewText
public class LBEntry
{
bool DetailsViewLBSelectedItem = true;
string DetailsViewLB = "test";
public LBEntry(bool selcected, string str)
{
DetailsViewLB = str;
DetailsViewLBSelectedItem = selcected;
}
public bool SelectedItemP
{
get { return DetailsViewLBSelectedItem; }
set { DetailsViewLBSelectedItem = value; }
}
public string StringP
{
get { return DetailsViewLB; }
set { DetailsViewLB = value; }
}
}
public class LBEntrysCollection : System.Collections.ObjectModel.ObservableCollection<LBEntry>
{
//
}
public class DetailsViewText
{
string[] DetailsViewTB1_Text = new string[20];
bool[] fDetailsViewCB = new bool[20];
LBEntrysCollection[] LBEntrys = new LBEntrysCollection[]{
new LBEntrysCollection{ new LBEntry(false, "test"), new LBEntry(true, "test") },
new LBEntrysCollection{ new LBEntry(true, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") }
};
public LBEntrysCollection LB1P
{
get { return LBEntrys[0]; }
set { LBEntrys[0] = value; }
}
public string TB9P
{
get { return DetailsViewTB1_Text[8]; }
set { DetailsViewTB1_Text[8] = value; }
}
...
}
The resource res_LB1P is set in the mainWindow constructor:
// Resources
this.Resources.Add("res_LB1P", detailsViewFrameHandling.DetailsViewTextP.LB1P);
Basicly I just want to bind the ListBox to a LBEntrysCollection with SelectedItemP as switch for the background Color and StringP as the Text Property. But I need the DataContext on DetailsViewText for other Propertys.
I´m getting an Exception when the xaml File is loading the StaticResource res_LB1P.
How do I have to set my Binding on ListBox and TextBlock to get it right?
EDIT:
With this
<ListBox ItemsSource="{Binding Path=LB1P}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=LB1P.StringP}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Items are added, but there is no Text shown in the TextBox
Now I´m really confused. It does work like this:
<ListBox ItemsSource="{Binding Path=LB1P}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemBrushP}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Simple enough, but I thought i had tried this before and it didn´t work...
Is it possible, that if one Binding does fail (the Background Binding) the other Binding (Text Property) does also not work?
I have always considered the ViewModel (the object the DataContext points to) to be just that: a Model of the View.
So to solve this, you need either one object that will be the ViewModel because there is only one DataContext property or you will need to add an extra DataContext-like property.
The first option (one ViewModel) can be realized by creating a new class that contains both the ObservableCollection and the DetailsViewText:
class ComposedViewModel: INotifyPropertyChanged
{
public LBEntrysCollection LBEntries
{
get { ... }
set { ... }
}
public DetailsViewText Details
{
get { ... }
set { ... }
}
}
The second option (extra DataContext-like property) can be realized by sub-classing the ListBox and adding another property.
Why not do this ?
<ListBox ItemsSource="{Binding ElementName=<TextBox's Name>, Path=DataContext">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemP, Converter={StaticResource c_SelectedItemToBackgroundConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Correct me if I'm wrong with understanding your question. You want to bind the listbox's itemssource to the textbox's datacontext?
I don't know according to MVVM show data on control.
I have a collection of cars.
I want group their by type (eg. Sedan, Combi, Hatchback) and depends of number of types print grids.
So :
5 cars:
2 x sedan, 2 x Combi, 1 x sportcar.
So I want to print 3 grids.
How do it to be ok with MVVM.
Below is some sample code. If your lists of cars can change you should use ObservableCollections instead or implement INotifyPropertyChanged on your viewmodel.
XAML:
<Window x:Class="TestApp.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<ListBox ItemsSource="{Binding Path=CarTypes}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Key}" />
<ListBox ItemsSource="{Binding Path=Value}" DisplayMemberPath="Name" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</Window>
Code behind:
using System.Collections.Generic;
using System.Windows;
namespace TestApp
{
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
DataContext = new CarsVM();
}
}
public class CarsVM
{
public CarsVM()
{
CarTypes = new Dictionary<string, List<Car>>();
// You want to populate CarTypes from some model.
CarTypes["sedan"] = new List<Car>() {new Car("Honda Accord"), new Car("Toyota Camry")};
CarTypes["musclecar"] = new List<Car>() { new Car("Chevy Camaro"), new Car("Dodge Challenger") };
CarTypes["suv"] = new List<Car>() { new Car("Chevy Tahoe") };
}
public Dictionary<string, List<Car>> CarTypes { get; private set; }
}
public class Car
{
public Car(string name)
{
Name = name;
}
public string Name { get; set; }
}
}
I'm databinding a listbox to an object that contains two array of strings. Each listbox item is set to a data template made up of a textbox and a combo box. The first string array is bound to the list, and the second string array is bound to the combo box. Well, at least that's I'm trying to achieve. The problem is that I can't figure out the binding syntax to set the second array to the combo box. Here's what I have:
The first thing is my class with my two string arrays. Pretty straightforward. Please note that the string array content is there for simplicity.
public class JobAssignments
{
public JobAssignments()
{
m_people.Add("John");
m_people.Add("Bill");
m_people.Add("Frank");
m_people.Add("Steve");
m_jobs.Add("Architect");
m_jobs.Add("Teacher");
m_jobs.Add("Carpenter");
m_jobs.Add("Plumber");
}
private List<string> m_people = new List<string>();
public List<string> People { get { return m_people; } set { m_people = value; } }
private List<string> m_jobs = new List<string>();
public List<string> Jobs { get { return m_jobs; } set { m_jobs = value; } }
};
In code, I set an instance of this class as the datacontext of this listbox:
<ListBox x:Name="listBox"
Grid.Row="0"
HorizontalContentAlignment="Stretch"
DataContext="{Binding}"
ItemsSource="{Binding People}"
ItemTemplate="{StaticResource JobAssignmentDataTemplate}">
</ListBox>
With a data template that looks like this:
<DataTemplate x:Key="JobAssignmentDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding}"/>
<ComboBox Grid.Column="2"
SelectedIndex="0"
ItemsSource="{Binding Jobs ???? }"/>
</Grid>
</DataTemplate>
What I usually get out my experiments is a list box of People, but the combo box of each list item is empty.
I can get it to work if I use
ItemsSource="{Binding ElementName=listBox, Path=DataContext.Jobs }"/>
but I don't want to use ElementName as it hardcodes the source of the array a specific listbox which I'd like to avoid.
Trying something like
ItemsSource="{Binding RelativeSource={RelativeSource Self}, Path=Parent.Jobs}"/>
Doesn't seem to work either as it's looking for Jobs inside the Grid.
A little push in the right direction would help greatly.
Thanks!
I had roughly the same problem as you except I had a listbox within a tabcontrol. I was able to solve it by using getting the tab control that contained the listbox. Sample code as follows:
<TabControl ItemsSource="{Binding Groups, Mode=TwoWay}" SelectedItem="{Binding SelectedGroup, Mode=TwoWay}">
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=TabControl, AncestorLevel=1}, Path=DataContext.ItemsInGroup, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemGroup}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
When you are at DataTemplate that represents just one People your JobAssignments instance is not there. Try the below
ItemsSource="{Binding ElementName= listBox, Path=Jobs}"/>
But I dont recommend the above step. You need to refactor your ViewModel logic. I guess 'Jobs' can be a static instance so that you can do a x:Static binding
ItemsSource="{Binding Source={x:Static JobAssignments}, Path=Jobs}"
You probably need to modify your object structure to work like this:
public class Person
{
public string Name;
public IEnumerable<string> Jobs;
}
public class JobAssignments
{
public JobAssignments()
{
Jobs.Add("Architect");
...
People.Add(new Person() { Name = "Bob", Jobs = Jobs });
...
}
private List<Person> m_people = new List<Person>();
public List<Person> People { get { return m_people; } }
private List<string> m_jobs = new List<string>();
public List<string> Jobs { get { return m_jobs; } }
}
Then, you can remove the question marks, and things should work.