ListView item not displayed correctly - c#

I have an ObservableCollection as the ItemsSource for a ListView.
Adding items works fine and the ListView updates, but in the ListView all I can see is this:
What am i missing?
public class MenuItem
{
public string Item { get; set; }
public string Price { get; set; }
}
// ...
public ObservableCollection<MenuItem> MenuItemList = new ObservableCollection<MenuItem>();
// ...
MenuItemList.Add(new MenuItem {Item = MenuData[6].InnerText, Price = PriceData[6].InnerText});
// ...
listViewFood.ItemsSource = MenuItemList;

You created a custom type MenuItem. The ListView has no way of knowing how to display items of this type, that is why it displays the result of ToString, which is the type name here. You have to define the appearance of an item yourself using a DataTemplate as ItemTemplate, e.g.:
<ListView x:Name="listViewFood">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type MenuItem}">
<StackPanel>
<TextBlock Text="{Binding Item}"/>
<TextBlock Text="{Binding Price}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
For more information see the Data Templating Overview, which covers the mechanics in detail.

Related

Add static item to RadComboBox in WPF

I have a set of two cascading RadComboBoxes - when one is set, the other populates. The second combobox has its ItemSource set to a CompositeCollection that's binding to an ObservableCollection in the viewmodel.
I'm attempting to add a static value to the list. The idea is that the CompositeCollection can change, but there should always be one static ComboBoxItem available named Other.
CustomerContact.cs:
public class CustomerContact
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
CustomerContactSource:
<CollectionViewSource x:Key="CustomerContactSource" Source="{Binding CustomerSite.CustomerContacts}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
OtherCustomerContact:
public CustomerContact OtherCustomerContactItem => new CustomerContact
{
Name = "Other",
Email = string.Empty,
PhoneNumber = string.Empty
};
Xaml page:
<telerik:RadComboBox ItemTemplate="{StaticResource ComboBoxItemTemplate}"
SelectedItem="{Binding CustomerContact}"
Text="{Binding Source=CustomerContact, Path=Name}">
<telerik:RadComboBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource CustomerContactSource}}"/>
<TextBlock Text="{Binding Source=OtherCustomerContactItem, Path=Name}"/>
</CompositeCollection>
</telerik:RadComboBox.ItemsSource>
</telerik:RadComboBox>
I keep getting an error stating that no converter can be found for TextBlock to CustomerContact. What am I doing wrong? The RadComboBox has an ItemSource that's a list of CustomerContact and a single, unchanging item that's also of type CustomerContact.
Any help would be greatly appreciated!!!
The RadComboBox has an ItemSource that's a list of CustomerContact and
a single, unchanging item that's also of type CustomerContact.
That last part doesn't hold. You're wrapping the item in a TextBlock and that's
the type XAML sees. Giving an error due to lack of TypeConverter.
You could wrap the item:
in a Collection(Container) of its own.
in a RadComboBoxItem in the lines of what is
done here for a ListBox.

Databinding a nested List property in WPF

I am using the following XAML code to display a list of checked list boxes.
<ListBox x:Name="lbxProjects" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ListBox x:Name="lbxUnits" ItemsSource="{Binding Units}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Content="{Binding unit.Name}" IsChecked="{Binding isSelected}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The data model is as follows
public class ProjectsListBox
{
public Project project { get; set; }
public List<UnitsCheckBox> Units = new List<UnitsCheckBox>();
public ProjectsListBox(Project project)
{
this.project = project;
foreach(var d in project.Documents)
{
Units.Add(new UnitsCheckBox(d));
}
}
}
public class UnitsCheckBox : INotifyPropertyChanged
{
public Document unit { get; set; }
private bool isselected = true;
public bool isSelected
{
get { return isselected; }
set
{
isselected = value;
NotifyPropertyChanged("isSelected");
}
}
public UnitsCheckBox(Document d)
{
unit = d;
}
}
I am assigning the data source for the parent listbox like
lbxProjects.DataContext = projectsList;
The code creates the child list boxes but not the checkboxes inside the child list boxes. What am i missing?
How should WPF resolve unit.Name?
If the type UnitsCheckBox contains a Name property, then the CheckBox's Content should be bound to Name:
Content="{Binding Name}"
You should always specify the type of your DataTemplate:
<DataTemplate DataType="{x:Type local:UnitsCheckBox}" ...>
Those are the probable problems but I can't be sure unless you give us the UnitsCheckBox code.

Databinding ObservableCollection to a ListBox

I have been trying to figure out the proper way to bind an ObservableCollection of a class to a ListBox with a TextBox DataTemplate. I've tried to implement the code in WPF binding: Set Listbox Item text color based on property but that hasn't gotten me very far as of yet. I'm new to WPF DataBinding, having at most programatically set the ItemsSource in simple cases.
I have this class
public class item
{
public string guid;
public bool found;
public bool newItem;
public Brush color;
}
and the following ObservableCollection
public ObservableCollection<item> _items;
public Window()
{
InitializeComponent();
_items = new ObservableCollection<item>();
}
Elsewhere in the code I add items to the collection via
_items.Add(new item() { guid = sdr.GetString(0), found = false, newItem = false, color = Brushes.Red });
Here's simplified XAML for the ListBox
<ListBox x:Name="ListBox_Items">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text=GUID_HERE Foreground=COLOR_HERE/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I've tried several different ways to get this to work properly, yet for none of them the ListBox is updating. Is anyone able to help point me in the right direction here?
Four things:
Your item class needs to use public properties:
public class item
{
public string guid { get; set; }
public bool found { get; set; }
public bool newItem { get; set; }
public Brush color { get; set; }
}
You need to set the ItemsSource to the collection, and set the current DataContext
public Window()
{
InitializeComponent();
DataContext = this;
_items = new ObservableCollection<item>();
ListBox_Items.ItemsSource = _items;
}
You need to update your DataTemplate to use the property names of your POCO
<ListBox x:Name="ListBox_Items">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding guid}" Foreground="{Binding color}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I think you forgot to bind your ListBox to collection itself.
Your XAML should look like:
<ListBox x:Name="ListBox_Items" ItemsSource="{Binding _items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text=GUID_HERE Foreground=COLOR_HERE/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And if you want to change properties of items in your collection (and that changes to appear on UI) you should implement INotifyPropertyChanged (see more at MSDN) interface in "item" class.

WPF Data Binding an ObservableCollection (or LinkedList) to a WrapPanel

Ok. First of all, I've looked at a bunch of other questions and a ton of other places on the internet on how to do this, and none of them are helping, so please don't mark this as a duplicate, and also, heads up cause I probably made a really stupid mistake.
I'm trying to bind an ObservableCollection to a WrapPanel using an ItemsControl and a DataTemplate. The following is my XAML Code:
<ItemsControl x:Name="wPanel">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--<Border BorderBrush="DarkGray" Background="Transparent">-->
<StackPanel MinWidth="250">
<Grid>
<TextBlock Text="{Binding address}" />
</Grid>
<Grid>
</Grid>
</StackPanel>
<!--</Border>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note: I did have (for the ItemsSource property of ItemsControl) {Binding properties}
This is my declaration of properties:
public ObservableCollection<Property> properties = new ObservableCollection<Property>();
And the Property Class is the following plus many more properties:
private string address { get; set; }
private string city { get; set; }
private string postcode { get; set; }
private double price { get; set; }
private LinkedList<Tennant> tennants { get; set; }
...
I thought I had solved the problem with this,
Binding binding = new Binding();
binding.Source = properties;
wPanel.SetBinding(ItemsControl.ItemsSourceProperty, binding);
But, then the line in the xaml: <TextBlock Text="{Binding address}" /> didn't work.
Then I came to the conclusion that it had to do with the properties object, and how it wouldn't bind unless I did it through code.
What am I doing wrong, for it not bind through XAML, etc.? What do I need to change about the properties object, or what do I need to do?
Thanks in advance.
You can bind ItemsControl's ItemsSource to properties just like #AjS answer. But before that, you need to change properties declaration to be property instead of field.
public ObservableCollection<Property> properties { get; set; }
And also address property of your Property class need to be public.
public string address { get; set; }
Isn't the 'properties' a property on the window?
If you are binding in xaml, make sure you use declare 'properties' as 'Property', set datacontext of window to itself and then set binding path:-
<ItemsControl x:Name="wPanel" ItemsSource="{Binding properties}">
this.DataContext=this; //set datacontext on parent window or control
If you are doing it in code, setting the ItemsSource directly on wPanel should work:-
wPanel.ItemsSource=properties;
This is easy trick for check Binding. Every WPF developer must know this. For example:
<TextBlock Text="{Binding}" />
Run and see it.
If TextBlock has any object in DataContext, object class name displayed in TextBlock.
If DataContext has empty. TextBlock is Empty

How to choose which ObservableCollection to bind to a listbox?

Is that even possible? I have two ObservableCollections. I want to bind and populate a listbox with one of them. For example let's say that we have 2 buttons - one for Twitter and one for Facebook. Clicking on a Facebook button it will populate listbox with friend's names from facebook observable collection and it will bind it. Clicking on Twitter it will populate listbox with Twitter followers and populate listbox and bind it.
How to choose which collection will be populated in listbox?
I would just use one observable collection and fill based on the users choice. You could also fill it with the names from both sources and have a filter to filter out one or the other (apparently you need a wrapper object where you can indicate whether the name is a facebook friend or twitter follower).
Edit: Here is some quick code example of how you can do it:
public interface ISocialContact
{
string Name { get; }
}
public class FacebookContact : ISocialContact
{
public string Name { get; set; }
public string FacebookPage { get; set; }
}
public class TwitterContact : ISocialContact
{
public string Name { get; set; }
public string TwitterAccount { get; set; }
}
Then in your data context:
public ObservableCollection<ISocialContact> Contacts { get; set; }
...
Contacts = new ObservableCollection<ISocialContact> {
new FacebookContact { Name = "Face", FacebookPage = "book" },
new TwitterContact { Name = "Twit", TwitterAccount = "ter" }
};
And in your xaml:
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type local:FacebookContact}">
<TextBlock Text="{Binding FacebookPage}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:TwitterContact}">
<TextBlock Text="{Binding TwitterAccount}"/>
</DataTemplate>
</Grid.Resources>
<ListBox ItemsSource="{Binding Contacts}" Width="100" Height="100"/>
</Grid>
This will apply the appropriate template to each object in your collection. So you can have collection with just facebook contacts or just twitter contacts or mixed.
Also note: You do not need the common interface. It will also work if you just make your ObservableCollection of type object. But given that they are being displayed by the same app in the same list box indicates that you can find some kind of common base and either can create a comon interface or base class.
In your ViewModel, create a property that exposes one or the other ObservableCollection, and swap it out when the button is clicked:
private ObservableCollection<string> _twitterFriendList;
private ObservableCollection<string> _facebookFriendList;
private ObservableCollection<string> _selectedFriendList;
public ObservableCollection<string> SelectedFriendList
{
get { return _selectedFriendList; }
set
{
if (value != _selectedFriendList)
{
_selectedFriendList = value;
RaisePropertyChanged("SelectedFriendList");
}
}
}
void TwitterButton_Click(object sender, RoutedEventArgs e)
{
SelectedFriendList = _twitterFriendList;
}
void FacebookButton_Click(object sender, RoutedEventArgs e)
{
SelectedFriendList = _facebookFriendList;
}
Then in your XAML you can just bind to the property:
<ListBox ItemsSource="{Binding SelectedFriendList}"/>
A non-elegant way of accomplishing this is to put 2 listboxes in the same location and bind 1 to the twitter collection and the other to the facebook collection. Bind their visibility to a property that changes based upon the button clicks. Personally, I'd have 2 radio buttons and display the listbox based upon which one is selected.
<ListBox Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=TwitterCollection}" SelectedItem="{Binding Path=SelectedTwitterItem}" Visibility="{Binding Path=IsTwitterSelected, Converter={StaticResource visibilityConverter}}" />
<ListBox Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=FacebookCollection}" SelectedItem="{Binding Path=SelectedFacebookItem}" Visibility="{Binding Path=IsFacebookSelected, Converter={StaticResource visibilityConverter}}" />
<RadioButton Grid.Row="1" Grid.Column="0" GroupName="rdoOptions" Content="{Binding Path=TwitterLabel}" IsChecked="{Binding Path=IsTwitterSelected}" />
<RadioButton Grid.Row="1" Grid.Column="1" GroupName="rdoOptions" Content="{Binding Path=FacebookLabel}" IsChecked="{Binding Path=IsFacebookSelected}" />

Categories