Reputation: 1955
I have a bunch of different controls (mainly buttons with textblocks inside them) that I just put in an ItemsControl. All the bindings worked correctly before I put the controls in the ItemsControl. Now, none of the commands work, or the Text bindings. Everything just shows up as 0's (I'm binding to doubles). I double-checked to make sure my ObservableCollection
was actually filled with items, and that those items' properties actually had data in them. The ItemsControl does properly create a new row for each item in the collection.
Here's my model:
public class VehicleModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private List<double> _nowTime = new List<double>();
public List<double> NowTime
{
get { return _nowTime; }
set { _nowTime = value; OnPropertyChanged("Nowtime"); }
}
private List<double> _VehLat = new List<double>();
public List<double> VehLat
{
get { return _VehLat; }
set { _VehLat = value; OnPropertyChanged("VehLat"); }
}
private int _currentIteration;
public int CurrentIteration //used to hold current index of the list of data fields
{
get { return _currentIteration; }
set
{
_currentIteration = value;
OnPropertyChanged("CurrentIteration");
OnPropertyChanged("CurrentVehLat");
}
}
private double _currentVehLat;
public double CurrentVehLat
{
get { return _currentVehLat; }
set { _currentVehLat = VehLat[CurrentIteration]; OnPropertyChanged("CurrentVehLat"); }
}
}
//Used to loop through the above list and set the currentVehLat equal to
//the current iteration of the list
public void SetData(int i)
{
CurrentIteration = i;
}
In my viewmodel, I have an ObservableCollection
holding these VehicleModel
s:
private ObservableCollection<VehicleModel> _vehicleCollection = new ObservableCollection<VehicleModel>();
public ObservableCollection<VehicleModel> VehicleCollection
{
get
{
return _vehicleCollection;
}
set
{
if (null != value)
{
_vehicleCollection = value;
OnPropertyChanged("VehicleCollection");
}
}
}
private ICommand showTimeWindowCmd;
public ICommand ShowTimeWindowCmd
{
get
{
return showTimeWindowCmd;
}
set
{
showTimeWindowCmd = value;
}
}
public MainWindowViewModel()
{
ShowTimeWindowCmd = new RelayCommand(ShowTimeWindow, param => this.canExecute);
}
public void ShowTimeWindow(object parameter)
{
//do stuff
}
Finally, the .xaml for my ItemsControl. I'm only showing one because there's a lot of them, but the are all exactly the same, just bound to different properties (all doubles like the one showed in the view model). Note: The controls show up properly, just not the bindings:
<ItemsControl Grid.Row="8"
Grid.ColumnSpan="16"
ItemsSource="{Binding VehicleCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
//bunch here
</Grid.ColumnDefinitions>
<Button Grid.ColumnSpan="4"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding ShowTimeWindowCmd}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="NowTime" />
<Binding Path="VehLat" />
<Binding Source="FISH Latitude" />
<Binding />
</MultiBinding>
</Button.CommandParameter>
<Button.Template>
<ControlTemplate>
<TextBlock FontSize="17"
Text="{Binding Path=CurrentVehLat,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
StringFormat={}{0:F7}}"
Visibility="{Binding IsChecked,
ElementName=FishChkBox,
Converter={StaticResource BoolToVisConverter}}" />
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Edit: I am setting my datacontext:
<Window.DataContext>
<viewmodel:MainWindowViewModel />
</Window.DataContext>
Edit 2: Added the command being called in the viewmodel. The first two command parameters are properties of the model.
Upvotes: 1
Views: 86
Reputation: 132618
The DataContext
for each <ItemTemplate>
in your ItemsControl is set to the individual item.
So what is being rendered is
<ItemsControl ItemsSource="{Binding VehicleCollection}">
<ContentPresenter> <!-- DataContext is VehicleModel[0] -->
<Grid...>
<!-- DataContext is inherited, so still VehicleModel[0] -->
<Button Command="{Binding ShowTimeWindowCmd}" .. />
...
</Grid>
</ContentPresenter>
<ContentPresenter> <!-- DataContext is VehicleModel[1] -->
<Grid...>
<!-- DataContext is inherited, so still VehicleModel[1] -->
<Button Command="{Binding ShowTimeWindowCmd}" .. />
...
</Grid>
</ContentPresenter>
etc...
</ItemsControl>
You need to change the Source of the command bindings so that instead of pointing to the default DataContext.ShowTimeWindowCmd
which results in VehicleModel.ShowTimeWindowCmd
, they point to ItemsControl.DataContext.ShowTimeWindowCmd
which looks from your code like it should result in MainWindowViewModel.ShowTimeWindowCmd
There's many ways to do that, but easiest to understand is by using the ElementName property of the binding.
<ItemsControl x:Name="MyItemsControl"...>
...
<Button Command="{Binding ElementName=MyItemsControl, Path=DataContext.ShowTimeWindowCmd}" .. />
...
</ItemsControl>
A RelativeSource
binding would also work here if you don't want to hardcode a Name like this :
<ItemsControl ...>
...
<Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=DataContext.ShowTimeWindowCmd}" .. />
...
</ItemsControl>
Upvotes: 1
Reputation: 4892
As the DataContext for the ItemsSource is the collection of Model, for the inners controls this will be its DataContext too, so you need to explicitly specify the Path to point to ViewModel properties:
<Button Grid.ColumnSpan="4"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}},
Path=DataContext.ShowTimeWindowCmd}"
>
Upvotes: 1