XAML nested DataTemplate data-binding - wpf

I have an ItemsControl which has its ItemsSource set with a Binding to an ObservableCollection on my ViewModel. I am using a Button control within its ItemTemplate to display my data and I am using a DataTemplate within the Button and none of the Binding data is being displayed. Below is the XAML in question:
<ItemsControl ItemsSource="{Binding Path=ConnectedInstruments}" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Height="60" VerticalAlignment="Top">
<Button.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=ModelCode}" />
<TextBlock Text="{Binding Path=SerialNumber}" />
</StackPanel>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Neither TextBlock controls within the Button DataTemplate are being populated. I understand this has something to do with the Binding path, however I am at a loss as to what I am doing wrong. Can someone put me on the right track please?
EDIT
public class Instrument
{
public string ModelCode { get; set; }
public string SerialNumber { get; set; }
}
public class ViewModel
{
public ObservableCollection<Instrument> ConnectedInstruments { get; set; }
}
I know that the ItemsControl is Binding correctly with the ObservableCollection as the correct count of Button controls are being displayed, only the templated data is missing.

Any specific reason you need to use ContentTemplate instead of directly setting Button's Content like this? :
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Height="60" VerticalAlignment="Top">
<StackPanel>
<TextBlock Text="{Binding Path=ModelCode}" />
<TextBlock Text="{Binding Path=SerialNumber}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>

Related

Display ItemsControl as a single datagrid row

I have the following ItemsControl that I use to display a collection of viewModels.
<Border>
<ItemsControl ItemsSource="{Binding MyViewModel.MyCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="20"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
And the code behind is:
MyCollection = new ObservableCollection<ViewModel>();
foreach(var model in MyModelList)
{
var myViewModel = new ViewModel(model);
MyCollection.Add(myViewModel);
}
Now this works fine. BUT now I want to not have an ItemsControl because there are only ever two items that are going to be added to MyCollection ObservableCollection.
I want to add each of the two viewModels into their own column in a single row datagrid in order to display the grid columns differently. (For example when user clicks on one viewModel I want to change the datagird background for only that column)
How do I modify the above to do this?
Create a class that holds the two view models:
public class RowOfViewModels
{
public ViewModel ViewModelA { get; set; }
public ViewModel ViewModelB { get; set; }
}
...and set or bind the ItemsSource property of the DataGrid to collection that contains a single instance of this one:
dataGrid.ItemsSource = new List<RowOfViewModels>(1) { ViewModelA = new ViewModel(), ViewModelB = new ViewModel() };
XAML:
<DataGrid x:Name="dataGrid">
<DataGrid.Columns>
<DataGridTemplateColumn Header="1">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding SomePropertyOfViewModelA}" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding SomePropertyOfViewModelB}" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
You can create a template for Cell in specific column (see this question), for example:
<DataGridTemplateColumn Header="...">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

WPF display listview item in a Image control

I have a Listview and next to it an Image control. I have set CollectionOfCapturedImages for my Listview's ItemsSource.
public ObservableCollection<BitmapImage> CollectionOfCapturedImages { get; } = new ObservableCollection<BitmapImage>();
<ListView x:Name="testListView" ItemsSource="{Binding CollectionOfCapturedImages}" MouseLeftButtonDown="testListView_MouseLeftButtonDown" Height="345" Margin="567,10,10,0" VerticalAlignment="Top">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" HorizontalAlignment="Center" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Source="{Binding}" Height="150" Width="150"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
How could I display selected Listview item (BitmapImage) in my Image control?
I have tried to add MouseLeftButtonDown="testListView_MouseLeftButtonDown"
private void testListView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
newlyAddedImage.Source = testListView.SelectedItem;
}
how can I get this working?
Did you try to just bind the Source property of the Image to the SelectedItem property of the ListView?
<Image x:Name="newlyAddedImage" Source="{Binding SelectedItem, ElementName=testListView}" />

WPF binding from ListBox ItemTemplate

I have a problem binding from ListBox Itemtemplate that contains an Expander with its own HeaderTemplate and ContentTemplate.
<ListBox ItemsSource="{Binding Feeds}">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Content="{Binding Text}"
Header="{Binding Title}">
<Expander.HeaderTemplate>
<DataTemplate>
<DockPanel >
<TextBlock Text="{Binding PublishDate}" />
<TextBlock Text="{Binding}" />
</DockPanel>
</DataTemplate>
</Expander.HeaderTemplate>
<Expander.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Source="{Binding ImageURL}" />
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</Expander.ContentTemplate>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The PublishDate and ImageURL bindings don't work and I'd like to bind them to properties in the object Feed:
Public Class Feed
Public Property Title As String
Public Property PublishDate As DateTime
Public Property Text As String
Public Property ImageURL As String
End Class
I tried FindAncestor in the binding to find the ListBoxItem but it didn't help me.
the Content of the Expander serves as the DataContext for the ContentTemplate and the Header for the HeaderTemplate.
Set Content="{Binding}" and Header="{Binding}" instead. This sets the DataContext of both templates to the DataContext of the ListBoxItem (i.e. a Feed).

Multiple levels of binding in WPF ItemsControl

How can i bind multiple levels of data in same time like List of chapters and under each chapter list of pages.
The class structure and xaml i used is shown here
public class ContentsPage
{
public string cname{ get; set; }
public string label { get; set; }
}
public class Chapter
{
public string name { get; set; }
public string id { get; set; }
public List<ContentsPage> pages { get; set; }
}
public class Model
{
public List<Chapter> chapters { get; set; }
}
<ItemsControl x:Name="TopLevelListBox" ItemsSource="{Binding}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander ExpandDirection="Down" Width="175">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=name}" Margin="0,0,5,0"/>
</StackPanel>
</Expander.Header>
<ListBox x:Name="SubListBox" ItemsSource="{Binding Path=enrichments}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=cname}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For binding i used the code
Model data = new Model(); //load data
TopLevelListBox.DataContext = data.chapters;
Only my expander headers are filled with result. What i need to do fill the pages inside the expander ? Any ideas or samples link for doing the same
Your ItemsControl Item template should change a bit. Instead of setting the ListBox as Content of Expander, set it as Content Template.
<Expander ExpandDirection="Down"
Width="175" Content="{Binding}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=name}"
Margin="0,0,5,0" />
</StackPanel>
</Expander.Header>
<Expander.ContentTemplate>
<DataTemplate>
<ListBox x:Name="SubListBox"
ItemsSource="{Binding Path=enrichments}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=cname}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</Expander.ContentTemplate>
</Expander>
And make sure you have sub items to show in ListBox.

Silverlight Data Binding for Collection in Stack Panel

I'm new to Silverlight, so I don't have a complete grasp of all the controls at my disposal. What I would like to do is use databinding and a view model to maintain a collection of items. Here is some mock code for what I'd like to do:
Model
public class MyItem
{
public string DisplayText { get; set; }
public bool Enabled { get; set; }
}
ViewModel
public class MyViewModel : INotifyPropertyChanged
{
private ObservableCollection<MyItem> _myItems = new ObservableCollection<MyItem>();
public ObservableCollection<MyItem> MyItems
{
get { return _myItems; }
set
{
_myItems = value
NotifyPropertyChanged(this, "MyItems");
}
}
}
View
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel ItemsSource="{Binding MyItems}">
<StackPanel Orientation="Horizontal">
<CheckBox "{Binding Enabled, Mode=TwoWay}"></CheckBox>
<TextBlock Text="{Binding DisplayText, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
</Grid>
So my end goal would be that every time I add another MyItem to the MyItems collection it would create a new StackPanel with checkbox and textblock. I don't have to use a stack panel but just thought I'd use that for this sample.
Looks like you want a <ListBox>, then set the <ListBox.ItemTemplate> to your <StackPanel> something like this.....
<ListBox ItemsSource=”{Binding Classes, Source={StaticResource model}}”>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox "{Binding Enabled, Mode=TwoWay}"/>
<TextBlock Text="{Binding DisplayText, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
here is a great example (it's WPF, but should only be minor changes for silverlight)
yes, looks like you want a <ListBox>
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SilverlightApplication4.MainPage"
Width="640" Height="480">
<UserControl.Resources>
<DataTemplate x:Key="ItemTemplate">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Enabled, Mode=TwoWay}"/>
<TextBlock Text="{Binding DisplayText}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource SampleDataSource}}">
<ListBox Margin="0,0,8,0" ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding Collection}"/>
</Grid>
This code will give you a ListBox with all your Data bound to a Checkbox and TextBlock with the Checkbox first and TextBox next to it.

Resources