How to pass index of an ItemsSource as CommandParameter in WPF - wpf

In WPF:
How can i pass the index of a ItemsSource loop as a CommandParameter?
<ItemsControl ItemsSource="{Binding PageList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, ElementName=Window}"
CommandParameter="INDEX OF ACTUAL ITEM AT ITEMSSOURCE GOES HERE" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
So, what i want is to pass the pushed button number to the Command method.
Thank you!

Simple way to do it.
First, screw indexes. They suck. Bind to SelectedItem
<ItemsControl ItemsSource="{Binding PageList}" SelectedItem="{Binding SelectedPage}">
Now, you don't have to try and pass the index into the parameter, because the selected page is already in your ViewModel.
// set in the ctor
public ObservableCollection<Page> PageList {get;private set;}
// Omitting INPC stuff in the setter
public Page SelectedPage {get;set;}
// Here's the Execute method of the ICommand
private void ExecuteChangePageCommand(object parameter)
{
// lol screw the parameter
var currentPage = SelectedPage;
UpdateSelectedPageOrDoWhateverLolKthx(currentPage);
}

Related

Binding not working inside a <ItemsControl.ItemTemplate>

I can not get the binding of a text property for a DataTemplate in MVVM design pattern.
To show the problem I expose below a simplification of my problem, where I bind two different view properties to the same model property (aka AnObject.Text).
My code in MainWindow.xaml is:
...
<Button Grid.Row="0" Content="{Binding ButtonText}" />
...
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding MyItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel>
<Label Content="aaaaa" />
<TextBlock Text="{Binding ItemText}" />
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
...
My code behind in MainWindow.xaml.cs (which sets the same DataContext for Button and every item in <ItemsControl ItemsSource>):
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
My code in MainWindowViewModel.cs is:
...
public ObservableCollection<object> MyItems => MyConverter.GetCollection(MyData.List);
public string ItemText => "dddd"; // this DOES works
public string ItemText => AnObject.Text; // this does NOT work
...
public string ButtonText => AnObject.Text; // this DOES works (note, same object property!)
...
Any idea why my binding inside the DataTemplate does not work?
Thanks in advance!
There are various things to understand here:
Button control will have the DataContext set to MainWindowViewModel instance. This is the reason why ButtonText variable value is getting reflected in Button control text.
For ItemsControl the DataContext is the the same as for the Button, i.e. the MainWindowViewModel instance.
Each item in the ItemsControl ItemsSource acts as a DataContext for the elements in the ItemTemplate, i.e. the DockPanel and its child elements. This is managed automatically by the framework. So essentially you will need a public property named ItemText in the class which will act as a DataContext for Dockpanel.
In your case the ItemText property is not the part of the objects which are in list.

Unable to get the binding from textbox and bind combobox from another view model

I am farily new to mvvm so bear with me. I have 2 View models which are inherited namely DBViewModel and PersonViewModel. i would like to add the person object in DBViewModel and bind 2 combobox with observablecollection in PersonViewModel.
public class PersonViewModel
{
private ICommand AddCommand ;
public Person PersonI{get;set;}
public ObservableCollection<Person> EmployeeList{ get; set; }
public ObservableCollection<String> OccupationList{ get; set; }
public PersonViewModel()
{
PersonI = new Person();
this.AddCommand = new DelegateCommand(this.Add);
// get OccupationList and EmployeeList
}
......
}
public class DBViewModel : PersonViewModel
{
public PersonViewModel PersonVM { get; set; }
public PersonViewModel()
{
PersonVM = new PersonViewModel();
}
....
}
<DataTemplate DataType='{x:Type viewModel:DBViewModel}'>
<StackPanel>
<TextBox Text="{Binding PersonI.Name}" />
<ComboBox Name="cboccupation" ItemsSource="{Binding OccupationList}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedItem}" SelectedValuePath="Id"/>
<Button Content="Add" Command="{Binding AddCommand}" />
<DataGrid ItemsSource="{Binding EmployeeList}" CanUserAddRows="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Occupation">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding OccupationList}"
DisplayMemberPath="Name" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</DataTemplate>
if you want an easier approach I'm thinking you could set a datastore field using blend and bind both controls to that field.
Your bindings are trying to bind to the properties PersonI and OccupationList on the DBViewModel, however those properties do not exist.
You need to point them to the PersonVM.PersonI and PersonVM.OccupationList instead.
<TextBox Text="{Binding PersonVM.PersonI.Name}" />
<ComboBox ItemsSource="{Binding PersonVM.OccupationList}" ... />
For your ComboBox binding inside the DataGrid, that probably will not work because the DataContext of each row in the Grid is a Person object (specified by the DataGrid.ItemsSource), and I don't think Person has a property called OccupationList.
You need to change the Source of your binding to use the object that has the OccupationList property.
For example, if your DataGrid was named MyDataGrid, the following binding for that ComboBox would work
<ComboBox ItemsSource="{Binding
ElementName=MyDataGrid,
Path=DataContext.PersonVM.OccupationList}" ... />
Alternatively, you could use a RelativeSource binding to have it look for the parent DataGrid object without needing to specify a name
<ComboBox ItemsSource="{Binding
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=DataContext.PersonVM.OccupationList}" ... />
As a side note, you seem to be a bit confused about bindings and the DataContext. I like to blog about beginner WPF topics, and would recommend reading What is this "DataContext" you speak of?. I find it has helped many WPF beginners on this site understand the basic binding concept. :)

How to pass listbox selecteditem as command parameter in a button?

Here is my situation:
<ListBox ItemsSource="{Binding Path=AvailableUsers}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Id}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding Path=Load}" CommandParameter={???? What goes here ????}/>
What I want is to pass the Id that is currently selected in the ListBox. I have a viewmodel behind the scenes that essentially looks like this:
public class ViewModel : DependencyObject
{
ICommand Load { get; set; }
// dependency property but I didn't bother to write it out like one
List<User> AvailableUsers { get; set}
}
How can I send the currently selected item using the xaml?
Try this:
Name your listbox
Update the CommandParameter to:
CommandParameter="{Binding ElementName=listBox1,Path=SelectedItem}"

WPF: ComboBoxes in ListBox and concurrency

I have code like this:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>Some Other Stuff Here</TextBlock>
<ComboBox ItemsSource="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem is, every time the outside ListBox.SelectedItem gets changed, the ComboBoxes inside it would change their SelectedIndex to -1. Which means if I click "Some Other Stuff Here" (unless the ListBoxItem it is in is selected), all the comboboxes' selection get cleared.
How do I overcome this? Thx!
Presumably your combobox is bound to something like an ObservableCollection - try exposing an instance of ICollectionView instead:
class DataSource
{
// ...
public ObservableCollection<string> MyData { get; private set; }
public ICollectionView MyDataView
{
get
{
return CollectionViewSource.GetDefaultView(this.MyData);
}
}
}
You can then bind your combobox with:
<ComboBox ItemsSource="{Binding MyDataView}" IsSynchronizedWithCurrentItem="True" />
This means that the 'selected item' for each data source is stored in the ICollectionView object instead of within the combobox, which should mean that it is persisted when the ListBox SelectedItem changes

How to read dynamical added check box?

I am adding checkboxes dynamically to silverlight stackpanel object as follows:
foreach (String userName in e.Result)
{
CheckBox ch = new CheckBox();
ch.Name = userName;
ch.Content = userName;
ContentStackPanel.Children.Add(ch);
}
How do I read back those controls to detect which of them are checked.
You can use databinding to checkbox list. Something like this:
Use a Listbox to create the check list:
<ListBox x:Name="chkList" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox Content="{Binding userName}" IsChecked="{Binding Checked, Mode=TwoWay}"></CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then in your code just set the chklist itemSource to an ObservableCollection with your object
chkList.ItemsSource = ....
You should probably avoid creating checkboxes in code like this. Something that might be useful for you is a mini "ViewModel" for the checkbox. Something like this:
public class Option
{
public string Text {get; set;}
public bool IsChecked {get; set;}
}
Then, you can have a collection of these options like this:
var options = new ObservableCollection<Option>();
Once this is populated, you can bind the ObservableCollection to an ItemsControl:
<ItemsControl ItemsSource="{Binding options}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
That XAML will create the CheckBoxes for you for ever option you added to your options collection. The really great thing is that you can now ask you options collection which options have been selected:
var selectedNames = from option in options
where option.IsChecked
select option.Text;
Using data binding and templates is a technique you should get familiar with in Silverlight/WPF. It is a really important concept, and it will let you do amazing things in you application.

Resources