Bind control to specific item in list of observable collections - c#

I have a scenario where I have multiple lists of data that is constructed programmatically in a loop. I want to display these lists side by side on the UI.
I setup a List of ObservableCollections of strings to contain the data. I am using the ListBox way of binding to the lists as shown here: Bind textbox list inside listbox in wpf
with this XMAL:
<ListBox Name="ListTwo" ItemsSource="{Binding Source=obs}" ... >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="TextBoxList" Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
The problem is how do you bind a specific listbox to an specific index in the List? Specifically I want the 0th list to bind to List[0], 1st to bind to List[1], etc.
So took the below suggestion and tried to make things into a class. This is what I got, but the UI isn't showing the updates.
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
x:Class="NS.ClassMainWindow"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<ListView Grid.Row="1" Grid.Column="0" x:Name="RawBox" ItemsSource="{Binding Foo.Raw}" Background="LightGray" BorderThickness="2" BorderBrush="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Name="RawItemsTextBoxList" Text="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
DataClass:
public class FooClass
{
public List<ObservableCollection<string>> items;
public ObservableCollection<string> Raw { get => this.items[0]; set => this.items[0] = value; }
public ObservableCollection<string> Tier1 { get => this.items[1]; set => this.items[1] = value; }
public ObservableCollection<string> Tier2 { get => this.items[2]; set => this.items[2] = value; }
public ObservableCollection<string> Tier3 { get => this.items[3]; set => this.items[3] = value; }
public ObservableCollection<string> Tier4 { get => this.items[4]; set => this.items[4] = value; }
public FooClass()
{
this.items = new List<ObservableCollection<string>>();
for (int i = 0; i <= 4; i++)
{
this.items.Add(new ObservableCollection<string>());
}
}
}
And Assignment:
this.Foo.Raw = new ObservableCollection<string>(itemNames); // itemNames is a List<string>
I am very obviously missing something, but for the life of me can't see it. Fairly new to WPF so probably is a noob thing.

If you set the DataContext to an instance of a FooClass and then set the Raw property a non-empty collection like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var foo = new FooClass();
foo.Raw = new ObservableCollection<string>(new List<string> { "1", "2", "3" });
DataContext = foo;
}
}
...you should see the strings provided that you bind to the Raw property and set the Mode of the TextBox to either OneTime or OneWay:
<ListView Grid.Row="1" Grid.Column="0" x:Name="RawBox" ItemsSource="{Binding Raw}"
Background="LightGray" BorderThickness="2" BorderBrush="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Name="RawItemsTextBoxList" Text="{Binding Mode=OneTime}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Related

WPF: access ElementName in a list within a list on the XAML side

I'm having a hard time solving a potential newbie problem: I've got a ObservableCollection<TopItem> MyTopItems that I display in a ListView. The type TopItem contains a string TopName and an ObservableCollection<NestedItem> NestedItems. The type NestedItem contains only a string NestedName.
My problematic is quite simple: I want to retrieve information on the nested item that I select, on the XAML side.
Right now, I can retrieve the selected item of TopItems quite easily, but I can't retrieve the selected item of NestedItems.
I know that I can bind the selected item (for TopItems and NestedItems) in the view model, but in my case it's almost pointless because I've got no use for it in the view model. Plus, I'd really like to know how to do it on the XAML side!
Enough talk, now comes the code.
A class to implement to INotifyPropertyChanged interface that I'm gonna use in my models and view model; not the cleanest way of doing, but it's for the sake of the demo. This class is just there to see the big picture, just know that it works well:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfSelectItemInDoubleList.Utils
{
public abstract class INPCBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisedPropertyChanged([CallerMemberName]string propertyName = null)
{
if (this.PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
else
{
storage = value;
RaisedPropertyChanged(propertyName);
return true;
}
}
}
}
Comes the NestedItem type:
using WpfSelectItemInDoubleList.Utils;
namespace WpfSelectItemInDoubleList.Model
{
public class NestedItem : INPCBase
{
private string _NestedName;
public string NestedName
{
get { return this._NestedName; }
set
{
SetProperty(ref this._NestedName, value);
}
}
public NestedItem(string nestedName)
{
NestedName = nestedName;
}
}
}
The TopItem type:
using System.Collections.ObjectModel;
using WpfSelectItemInDoubleList.Utils;
namespace WpfSelectItemInDoubleList.Model
{
public class TopItem : INPCBase
{
private string _TopName;
public string TopName
{
get { return this._TopName; }
set
{
SetProperty(ref this._TopName, value);
}
}
private ObservableCollection<NestedItem> _NestedItems;
public ObservableCollection<NestedItem> NestedItems
{
get { return this._NestedItems; }
set
{
SetProperty(ref this._NestedItems, value);
}
}
public TopItem(string topName)
{
TopName = topName;
}
}
}
The view model:
using System.Collections.ObjectModel;
using WpfSelectItemInDoubleList.Model;
using WpfSelectItemInDoubleList.Utils;
namespace WpfSelectItemInDoubleList.ViewModel
{
public class MainWindowViewModel : INPCBase
{
private ObservableCollection<TopItem> _TopItems;
public ObservableCollection<TopItem> TopItems
{
get { return this._TopItems; }
set
{
SetProperty(ref this._TopItems, value);
}
}
public MainWindowViewModel()
{
TopItems = new ObservableCollection<TopItem>();
for (int i = 0; i < 5; i++)
{
var topItem = new TopItem($"top item {i}")
{
NestedItems = new ObservableCollection<NestedItem>()
};
for (int j = 0; j < 5; j++)
{
var nestedItem = new NestedItem($"NI {j}");
topItem.NestedItems.Add(nestedItem);
}
TopItems.Add(topItem);
}
}
}
}
Finally, the most important part: the XAML!:
<Window x:Class="WpfSelectItemInDoubleList.View.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:WpfSelectItemInDoubleList"
xmlns:vm="clr-namespace:WpfSelectItemInDoubleList.ViewModel"
mc:Ignorable="d"
Title="List in list" Height="350" Width="525">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<ListView x:Name="TopItemsLV" Grid.Row="0" Margin="10" HorizontalContentAlignment="Stretch" ItemsSource="{Binding TopItems, Mode=TwoWay}" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="TopNameTB" Grid.Column="0" Text="{Binding TopName}" TextWrapping="Wrap" VerticalAlignment="Center" />
<StackPanel Grid.Column="1">
<ListView x:Name="NestedItemsLV" ItemsSource="{Binding NestedItems}" BorderThickness="0" HorizontalAlignment="Center" SelectionMode="Single">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="Transparent" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding NestedName}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<ContentControl Grid.Row="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ContentControl Margin="10" Grid.Row="1" Content="{Binding ElementName=TopItemsLV, Path=SelectedItem.TopName}" />
<ContentControl Margin="10" Grid.Row="2" Content="{Binding ElementName=NestedItemsLV, Path=SelectedItem.NestedName}" />
</Grid>
</Window>
The interesting part is the second ContentControl. The first one is working well, but the second doesn't: nothing is showing when I select a nested item. A hint is given to me by intellisense: it sees the TopItemsLV, but not the NestedItemsLV.
Prepare for the most beautiful UI ever. Please don't stole it from me, I'm planning to make millions out of it! Just kidding.
As you can see, the selected item from TopItems is showing, but not the selected item from NestedItems. Any idea why?
Thanks :)
EDIT: Skip the first solution. It's more appropriate for really simple views. Scroll down to my second solution instead.
If you are only binding a single ItemsControl (e.g., ListView) to this list of TopItem instances, then you could just the default collection view manage the selected items for you. That's probably the simplest way to do this.
First, set IsSynchronizedWithCurrentItem="True" on both TopItemsLV and NestedItemsLV.
Then, change your content control bindings as follows:
<ContentControl Content="{Binding Path=TopItems/TopName}" />
<ContentControl Content="{Binding Path=TopItems/NestedItems/NestedName}" />
The / separator in a binding path means "drill down into the currently selected item". The selected item is maintained by the default collection view for both your TopItems collection and each NestedItems collection. The default collection view is what you would get if you called CollectionViewSource.GetDefaultView.
Better Solution
The conventional MVVM approach would be to add a SelectedItem property alongside your TopItems and NestedItems collections. Make sure they fire property change events. The property type should match the corresponding collection's element type. If these properties start out with a null value, then nothing will be selected initially, which is what you want.
On both list views, set SelectedItem="{Binding SelectedItem, Mode=TwoWay}". Remove the IsSynchronizedWithCurrentItem settings from my original answer.
Adjust your content control bindings as follows:
<ContentControl Content="{Binding Path=SelectedItem.TopName}" />
<ContentControl Content="{Binding Path=SelectedItem.SelectedItem.NestedName}" />
Attach a new event handler to NestedItemsLV:
<ListView x:Name="NestedItemsLV"
GotFocus="OnNestedItemsLVGotFocus"
... />
In your view's code-behind, implement the handler as follows:
private void OnNestedItemsLVGotFocus(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as MainWindowViewModel;
var parentItem = (sender as FrameworkElement)?.DataContext as TopItem;
if (viewModel != null && parentItem != null)
viewModel.SelectedItem = parentItem;
}
I think you'll agree that this solution works better.

C# Wpf Binding to Collection of Userdefined Class not working as expected

Currently i have an ObservableCollection of MyClass in my Viewmodel. I use the getter of this Collection to fill it from an other Datasource. I can now Display this Data in a Window(Grid) and the correct Data is shown, but when i change the Data the set is not fired(I think it is because not the Collection is changed, only a Element in the Collection). Should i create a Property for every Property of MyClass, so i can react to the changes of a single Value, the Questions i ask myself are:
How do i know what Element is selected at the moment
How to fill the Collection correct when i have a binding to every single item
I also thought of a Event when my Collection is changed, but i am not sure how to implement it right.
public ObservableCollection<MyClass<string>> SelectedParams
{
get
{
//Fill the Collection
}
set
{
//I think here i want to react to changed Textboxvalues in my View
}
}
public class MyClass<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private T _curValue;
private string _value1;
private string _value2;
public string Value1
{
get
{
return _value1;
}
set
{
if (_value1 != value)
{
_value1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value1)));
}
}
}
public string Value2
{
get
{
return _value2;
}
set
{
if (_value2 != value)
{
_value2 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value2)));
}
}
}
public T curValue
{
get
{
return _curValue;
}
set
{
_curValue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(curValue)));
}
}
public MyClass()
{
}
public MyClass(string val1, string val2, T curVal)
{
Value1 = val1;
Value2 = val2;
curValue = curVal;
}
}
The xaml Code looks something like this
<ItemsControl ItemsSource="{Binding SelectedParams}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Value1}"/>
<Label Grid.Column="1" Content="{Binding Value2}"/>
<TextBox Grid.Column="2" Text="{Binding curValue, Mode=TwoWay}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Edit1: Changed MyClass to INotifyPropertyChanged now the Collection changes internal Values but the Setter is still not called on change of a Value
You need to implement INotifyPropertChanged interface for MyClass and raise the PropertyChanged in setter to notify UI that the property value changed.
How do i know what Element is selected at the moment
If you want support for item selection you have to use an other control. ItemsControl does not support selection.
Use ListView for example. Bind ItemsSource and SelectedItem to your class. Now every time you click on an item, SelectedValue is updated. And if you change SelectedValue from code the UI updates the selected item in the list. You can also bind other controls to SelectedValue like I did with the TextBlock outside the ListView.
View
<StackPanel>
<ListView ItemsSource="{Binding Values}" SelectedItem="{Binding SelectedValue}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Item1}" />
<TextBlock Text="=" />
<TextBlock Text="{Binding Path=Item2}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Horizontal">
<TextBox Text="Selected:" Background="DarkGray" />
<TextBox Text="{Binding SelectedValue.Item1, Mode=OneWay}" Background="DarkGray" />
</StackPanel>
</StackPanel>
Data
public class ListViewBindingViewModel : INotifyPropertyChanged
{
private Tuple<string,int> _selectedValue;
public ObservableCollection<Tuple<string,int>> Values { get; }
public Tuple<string, int> SelectedValue
{
get { return _selectedValue; }
set
{
_selectedValue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedValue)));
}
}
public ListViewBindingViewModel()
{
Values = new ObservableCollection<Tuple<string, int>> {Tuple.Create("Dog", 3), Tuple.Create("Cat", 5), Tuple.Create("Rat",1)};
}
}

C# wpf listbox not updating from ObservableCollection

I'm trying to get the databinding I need to work with a ListBox.
I've parsed some data from a text file to a ObservableCollection<ViewModel> but the data isn't updating in the ListBox.
Here's some information:
The data which is written to from the parser:
class MainData
{
private static ObservableCollection<GroupViewModel> groupModelList = new ObservableCollection<GroupViewModel>();
public static ObservableCollection<GroupViewModel> GroupModelList
{
get { return groupModelList; }
}
}
What GroupViewModel holds (not everything but it's all the same):
class GroupViewModel : INotifyPropertyChanged
{
private GroupModel groupModel;
public event PropertyChangedEventHandler PropertyChanged;
public GroupViewModel()
{
groupModel = new GroupModel();
}
public string Name
{
get { return groupModel.name; }
set
{
if (groupModel.name != value)
{
groupModel.name = value;
InvokePropertyChanged("Name");
}
}
}
...
}
And what GroupModel Holds:
class GroupModel
{
public string name { get; set; }
}
This is how the parser adds new items to the GroupModelView:
if (split[0] == "group")
{
currentGroup = new GroupViewModel();
currentGroup.Name = split[1];
MainData.GroupModelList.Add(currentGroup);
}
I created a ListBox in my WPF application with these XAML options:
<Window x:Class="SoundManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:SoundManager.ViewModels"
xmlns:vm2="clr-namespace:SoundManager.Code"
Title="MainWindow" Height="720" Width="1280">
<Window.Resources>
<vm:MainViewModel x:Key="MainViewModel" />
<vm2:MainData x:Key="MainData" />
</Window.Resources>
<ListBox Grid.Row="2" Height="484" HorizontalAlignment="Left" Margin="12,0,0,0" Name="lbFoundItems" VerticalAlignment="Top" Width="201" ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList/Name}" />
but for some reason the data isn't updating in the UI (new items aren't added visibly in the UI).
I've been just getting started with the MVVM pattern and databinding and I can't figure out what I'm doing wrong.
Thanks in advance!
GroupModelList/Name is not a valid property path here. Setting it like that does not make the ListBox show the Name property of the data items in the GroupModelList collection.
You would instead have to set the ListBox's DisplayMemberPath property:
<ListBox ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList}"
DisplayMemberPath="Name"/>
or set the ItemTemplate property:
<ListBox ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Moreover, the GroupModelList property should not be static:
class MainData
{
private ObservableCollection<GroupViewModel> groupModelList =
new ObservableCollection<GroupViewModel>();
public ObservableCollection<GroupViewModel> GroupModelList
{
get { return groupModelList; }
}
}
Then you might have MainData as a property in your view model, and bind the ListBox like this:
<ListBox ItemsSource="{Binding Source={StaticResource MainViewModel},
Path=MainData.GroupModelList}" .../>

How to do Data Binding for Textblock within a LongListselector in windows phone app?

Hi, I am trying to bind the data for text block within a LongListSelector. But I am not getting any Output for it, kindly help me.
This is my XAML code:
<phone:LongListSelector ItemsSource="{Binding ''}" x:Name="longListSelector" HorizontalAlignment="Left" Height="680" VerticalAlignment="Top" Width="446" >
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="name" Text="{Binding DataContext.TextContent,ElementName=page,Mode=OneWay}" Height="100" Width="100" HorizontalAlignment="Center">
</TextBlock>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
In the C# code I have parsed data which i need to display in the windows phone, in a menu format.
Part of C# code is shown below:
XDocument document = XDocument.Parse(e.Result);
var data1 = from query in document.Descendants("location")
select new Data
{
Lat = (string)query.Element("lat"),
Lag = (string)query.Element("lng")
};
foreach (var d in data1)
{
JsonParsing(d.Lat, d.Lag);
}
data1 = from query in document.Descendants("result")
select new Data
{
Country = (string)query.Element("formatted_address")
};
foreach (var d in data1)
{
// ob.JsonParsing(d.Lat, d.Lag);
//XmlParsing(d.Lat, d.Lag);
val = d.Country;
//listbox.Items.Add(val);
//StringsList.Add(val);
TextContent=val;
I want the value of the country to be shown inside the textblock, kindly help me figure this out as I am pretty new to this field, thanks.
try like this
a good reference
<DataTemplate>
<StackPanel VerticalAlignment="Top">
<TextBlock Text="{Binding Value}" />
</StackPanel>
</LongListSelector>
CodeBehind
**Add a public property only public property can be participate in databinding**
#region Public Properties
private ObservableCollection<YourModel> _collectionofValue;
public ObservableCollection<YourModel> CollectionofValues
{
get
{
return _collectionofValue;
}
set
{
_collectionofValue=value;
raisepropertyChanged("CollectionofValues");
}
}
private string _value;
public string Value
{
get
{
return _errorMessage;
}
set
{
_errorMessage = value;
RaisePropertyChanged("Value");
}
}
#endregion
**Set value to this public property when you get value**
// for single values
public void getValue()
{
value =GetXmlValue(); // your method that will return the value;
}
// as it is a collection
public void getValuestoCollection()
{
Collection.Add(new YourModel(value="SampleValue1");
Collection.Add(new YourModel(value="SampleValue1");
Collection.Add(new YourModel(value="SampleValue1");
Collection.Add(new YourModel(value="SampleValue1");
}
YourModel
// the collection of this model is binded to the LongListSelector.
public class ModelName
{
public string Values {get;set;}
}
reference
<phone:LongListSelector ItemsSource="{Binding Items}" x:Name="longListSelector" HorizontalAlignment="Left" Height="680" VerticalAlignment="Top" Width="446" >
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="name" Text="{Binding Path=TextContent}" Height="100" Width="100" HorizontalAlignment="Center">
</TextBlock>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
Your C# algm should be:
i) Have a viewmodel class
public class MyViewModel
{
public ObservableCollection<MyDataItem> Items {get; set;}
public MyViewModel()
{
Items=new ObservableCollection<MyDataItem>();
loop //add your items to your 'Items' property so that you can bind this with LongListSelector ItemsSource
{
Items.Add(new MyDataItem("mystring"));
}
}
}
public class MyDataItem
{
public MyDataItem(string s)
{
TextContent=s;
}
public string TextContent {get;set;}
}
ii) Create an instance to ViewModel class and set DataContext
// write this in the constructor of the page which contains the LongListSelector
public MyViewModel vm;
constructor()
{
vm=new MyViewModel();
this.DataContext=vm;
}

Binding to collections in WPF

So I finally decided to move from WinForms to WPF, and I'm having quite an interesting journey. I have a simple application in which I bind an ObservableCollection to a ListBox.
I have an Animal entity:
namespace MyTestApp
{
public class Animal
{
public string animalName;
public string species;
public Animal()
{
}
public string AnimalName { get { return animalName; } set { animalName = value; } }
public string Species { get { return species; } set { species = value; } }
}
}
And an AnimalList entity:
namespace MyTestApp
{
public class AnimalList : ObservableCollection<Animal>
{
public AnimalList() : base()
{
}
}
}
And finally here's my main window:
<Window x:Class="MyTestApp.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyTestApp"
Title="Window3" Height="478" Width="563">
<Window.Resources>
<local:AnimalList x:Key="animalList">
<local:Animal AnimalName="Dog" Species="Dog"/>
<local:Animal AnimalName="Wolf" Species="Dog"/>
<local:Animal AnimalName="Cat" Species="Cat"/>
</local:AnimalList>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical" Margin="10,0,0,0">
<TextBlock FontWeight="ExtraBold">List of Animals</TextBlock>
<ListBox ItemsSource="{Binding Source={StaticResource animalList}, Path=AnimalName}"></ListBox>
</StackPanel>
</Grid>
Now when I run the application, I see the listbox populated with three items: "D", "o", and "g" instead of "Dog", "Wolf", and "Cat":
I have a strong feeling that I'm doing something stupid somewhere (AnimalList constructor maybe?) but I can't figure out what it is. Any help is appreciated.
You need to set the DisplayMemberPath (as opposed to the Path property in the binding).
<Grid>
<StackPanel Orientation="Vertical" Margin="10,0,0,0">
<TextBlock FontWeight="ExtraBold">List of Animals</TextBlock>
<ListBox ItemsSource="{Binding Source={StaticResource animalList}}" DisplayMemberPath="AnimalName"></ListBox>
</StackPanel>
</Grid>
Since you are binding to a list of Animal objects, DisplayMemberPath specifies the name of the property in the Animal class that you want to show up as a list item.
If the property is itself an object, you can use dot notation to specify the full path to the property you want displayed ie..
<ListBox ItemsSource="{Binding Source={StaticResource animalList}}" DisplayMemberPath="PropertyInAnimalClass.PropertyInTheChildObject.PropertyToDisplay" />
You're binding your listbox to the animalname. Instead you should bind your listbox to your collection:
<ListBox ItemsSource="{Binding Source={StaticResource animalList}}"></ListBox>
Notice that I've removed the path=AnimalName from the binding.
Now you will see the class name, since the ListBox doesn't know how to display an Animal and therefore it calls its ToString-method.
You can solve this by giving it an ItemTemplate like so:
<ListBox ItemsSource="{Binding Source={StaticResource animalList}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding AnimalName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Inside the itemtemplate your DataContext is an instance of Animaland you can then bind to the properties on that instance. In my example I have bound the AnimalName, but you basically construct any template you want using normal XAML-controls and binding to the different properties of your bound object.

Categories