I have a custom control ListItem. I need to display five such items in a window and these items could change during runtime; items could be added or deleted, or content could change in ListItem.
ListBox appears to be a good solution to display items. But what I have seen is we can add items and style them, and can handle updates with data trigger.
myListBox.Items.Add(new { FileName = "SomeFile", State="Uploaded" });
But we can not do something like
ListItem curItem = new ListItem();
myListBox.Items.Add(new { curItem });
Even if I do it shows empty item in the list.
So if I want to add my custom control to some listbox, how could that be possible. That is using ListBox just as a container so we can get away from the pain of positioning and all that after list changes. Or is there a better way to do that?
You are in luck - this is the bread and butter of WPF! Set the ItemsSource of your ListBox (possible in XAML or cs):
myListBox.ItemsSource = myEnumerableCollection;
or
<ListBox ItemsSource="{Binding MyItemsProperty}">
Use a DataTemplate (you do not need a UserControl) to style each item in XAML:
<ListBox ItemsSource="{Binding MyItemsProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FileName}"/>
<TextBlock Text="{Binding State}"/>
<!--Whatever you want-->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If your collection is an ObservableCollection<T> changes to that collection (e.g. items added or removed) will be reflected in the ListBox automatically. If T implements INotifyPropertyChanged changes to properties on each item will also automatically show up on the UI.
For more see the WPF Binding Overview.
Don't create or manipulate UI elements in procedural code in WPF.
<ListBox ItemsSource="{Binding SomeCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<my:MyControl/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where my:MyControl is a UserControl with whatever UI you want.
Related
I am trying to bind a ListBox to another ListBox within the same window. The left hand sided Listbox has data in it that one can select. But I want a user to be able to click on the item(s) in the left hand listbox and those same item(s) would be displayed in the other listbox on the right hand side.
EDITED: Of course you can bind a UI property to another UI property (Dependency Property actually) using ElementName, but I recommend to bind the properties to one view model. See a simplified example below.
View model:
public ObservableCollection<ItemObject> Items { get; set; }
public ObservableCollection<ItemObject> SelectedItems { get; set; }
Left:
<ListBox ItemsSource="{Binding Items}" SelectedItems="{Binding SelectedItems}" />
(Note that there is no SelectedItems dependency property actually. See question like: Select multiple items from a DataGrid in an MVVM WPF project)
Right:
<ListBox ItemsSource="{Binding SelectedItems}" />
This works fine. Furthermore, with this approach, the list on the right hand can be customized with ease (eg order, filter, ... by using CollectionView).
private ICollectionView _collectionView;
private ICollectionView _CollectionView {
get { return _collectionView
?? (_collectionView = CollectionViewSource.GetDefaultView(SelectedItems)); }
}
public ICollectionView FilteredItems {
get { _CollecitionView.Filter(...); }
}
<ListBox ItemsSource={"Binding FilteredSelectedItems"} />
Such an MVVM approach is sometimes laborious, but eventually found as beneficial.
You name the first listbox, then any other control on the xaml will bind to that control using it's name in the ElementName attribute of the binding.
For example there are two listboxes and one text box. The top listbox has multiselections and those selection(s) are shown on the lower listbox. While the textbox only gets the first item selected.
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<converters:PathToFilenameConverter x:Key="FilenameConverter" />
<x:Array x:Key="FileNames" Type="system:String">
<system:String>C:\Temp\Alpha.txt</system:String>
<system:String>C:\Temp\Beta.txt</system:String>
<system:String>C:\Temp\Gamma.txt</system:String>
</x:Array>
</StackPanel.Resources>
<ListBox Name="lbFiles"
SelectionMode="Multiple"
ItemsSource="{StaticResource FileNames}"
Margin="10"/>
<ListBox ItemsSource="{Binding SelectedItems, ElementName=lbFiles }" Margin="10" />
<TextBlock Text="{Binding SelectedItem,
ElementName=lbFiles,
Converter={StaticResource FilenameConverter}}"
Margin="10" />
</StackPanel>
Note...the code is binding using the SelectedItems property for the lower list box and not SelectedItem used by the TextBlock.
As an aside, another answer has the use of an ObservableCollection, that is not needed unless the array is dynamically changing; otherwise any array can be used. Depending on loading, say from a VM, it may need to adheres to the INotifyPropertyChanged.
I have a ListBox bound to an ObservableCollection:
<ListBox ItemsSource="{Binding ObservableCollectionOfFoos}" />
The ObservableCollection contains instances of Foo, Foo implements INotifyPropertyChanged.
I'm trying to bind properties of the ListBoxItems to properties of Foo.
Here is what I tried:
<DataTemplate DataType="{x:Type local:Foo}" >
<TextBlock Content="{Binding PropertyOfFoo}" Background="{Binding AnotherPropertyOfFoo}"/>
</DataTemplate>
this works, but, the problem is that I only have access to the properties of the TextBlock and not the containing ListBoxItem, so, for example, Background only changes the color around the text and not the whole entry. I have a feeling I'm using the wrong tool for the job here.
a point in the right direction would be very appreciated.
The DataTemplate specifies the template (UI presentation) of the content of the list box item. What you need to style is the item container itself which can be done via the ItemContainerStyle property of the ListBox.
Here's the situtation:
Data Context of the window is: MainViewModel.
It's built from ObservableCollections of SubViewModel.
Each SubViewModel has its own ObservableCollection of type String.
Now, I have a treeview. The ItemsSource is the ObservableCollection of the MainViewModel.(Means it is the SubViewModel collection).
I want that if an item is selected, then there will be displayed the ObservableCollection(type String) of the selected Item in the treeview.
How can I do that?
Some code:
<TreeView ItemTemplate="{DynamicResource TreeViewDataTemplate}" ItemsSource="{Binding SubViewModelCollection}"/>
I want to display the collection in a stack panel because of some reasons.
So: (TypeCollection is the string ObservableCollection of the item, it is currently not working of course)
<ItemsControl ItemsSource="{Binding TypeCollection}" x:Name="UserList" ItemTemplate="{StaticResource TemplateDataTemplate}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Grid.Column="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
I've been struggling alot with that, how can I achieve my target?
Bind the treeview ItemsSource to the UserList's SelectedItem.
<TreeView ItemTemplate="{DynamicResource TreeViewDataTemplate}"
ItemsSource="{Binding ElementName=UserList Path=SelectedItem.SubViewModelCollection}"/>
Assuming the items in UserList are type SubViewModel which has an IEnumerable<T> property called SubViewModelCollection.
I would recommend taking a look at Prism, particularly the portion pertaining to Event Aggregation. What this will allow you to do is publish an event in your application when an item in the TreeView is selected, consume that event elsewhere in your application and bind the ItemControl to the selected SubViewModel all without having to introduce any unnecessary coupling between these two pieces of your application.
I'm using silverlight framework 4: I'm trying to list my items in a generic list to a listbox control: But the only data a receive is the classname itself.
lsBox => the listbox control
lsTags => generic type
My question is: how can I add my items in the generic list, to the listbox control?
my code is:
lsBox.ItemsSource = lsTags;
You can use DisplayMemberPath and SelectedValuePath properties of your ListBox control to tell ListBox which property's value should be displayed for every item and which property should be used for determening ListBox.SelectedValue property. Or use ListBox.ItemTemplate to display a complex data like this:
<ListBox x:Name="usersInGroupLBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay}" />
<TextBlock Text="{Binding User.UserName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Do not forget, you can use only public properties for binding. Check you class Tag.
The default behavior of ListBox (and most other controls) for displaying user types is to call the ToString() method. The default behavior of that is to display the class name.
What you should do depends on what you want to display, but if it's something simple like displaying the value of the Name property, just set the DisplayMemberPath property:
<ListBox Name="lsBox" DisplayMemberPath="Name" />
I've got an ItemsControl with an ItemsSource of Hours.
I represent each item by a border (in the data template).
Now, each of those border has an hour data, and i want to retrieve that in code behind.
is it even possible?
my code example:
<ItemsControl x:Name="dayHours">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Name="dayHourBorder" Height="30" BorderBrush="#B0B6BE" Width="193" BorderThickness="1,0,1,1" Background="AliceBlue" Tag="{Binding Index}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And in the naive way, I would expect a code like:
(sender as Border).hourTime;
From your code, I am guessing that you are interested in finding the 'hourTime' in an event handler? When an ItemsControl creates an 'instance' of your DataTemplate for each item, it sets the DataContext of the template to the item itself. Therefore the following should work:
Border border = sender as Border;
MyItemType item = border.DataContext as MyItemType;
var hourTime = item.hourTime;