anybody an idea why CommandParameter is always null?
The class TransactionViewModel has the collection property of TransactionCommands to be displayed in the ItemsControl. The items are of type CommandViewModel.
TransactionBrowserViewModel has the command AddJobForSelectedTransactionCommand. The command to be passed as a parameter the CommandViewModel.
View Snipp:
<ItemsControl Grid.Row="4"
ItemsSource="{Binding TransactionCommands}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<telerik:RadButton Content="{Binding DisplayName}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Self}}"
Command="{Binding ViewModel.AddJobForSelectedTransactionCommand, ElementName=userControl}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Codebehind of UserControl:
[Export]
public partial class TransactionBrowserView : UserControl, IView<TransactionBrowserViewModel>
{
[ImportingConstructor]
public TransactionBrowserView()
{
InitializeComponent();
}
[Import]
public TransactionBrowserViewModel ViewModel
{
get { return (TransactionBrowserViewModel)this.DataContext; }
set { this.DataContext = value; }
}
}
OK, sorry I have found the error.
It is located on the RadButton by Telerik. I have tested the scenario with a default button. Here it works without any problems.
You have set the ComandParameter to the path of the DataContext of the RadButton, but I don't see that you have set anything to that DataContext anywhere.
Look into the Output window for information regarding your Binding errors... it should say something like 'There is no DataContext property on object XXX'.
What are you trying to bind to the CommandParameter property?
Try this binding
<ItemsControl x:Name="transactionList" Grid.Row="4" ItemsSource="{Binding TransactionCommands}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<telerik:RadButton Content="{Binding DisplayName}"
CommandParameter="{Binding SelectedItem, ElementName=transactionList}"
Command="{Binding ViewModel.AddJobForSelectedTransactionCommand, ElementName=userControl}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Give your ItemsControl (like transactionList) and set the binding of the CommandParameter to the SelectedItem of your transactionList.
or does this not do what you want.
<telerik:RadButton Content="{Binding DisplayName}"
CommandParameter="{Binding}"
Command="{Binding ViewModel.AddJobForSelectedTransactionCommand, ElementName=userControl}"/>
Related
UWP Application (Important because there is no AncestorType)
I can't bind command (neither other values) of the ViewModel from a DataGridTemplateColumn.
Here is my current code (i have tried, literally everything)
<controls:DataGrid
x:Name="DataGrid"
Grid.Row="2"
Height="Auto"
Margin="12"
AutoGenerateColumns="False"
HorizontalContentAlignment="Center"
ItemsSource="{Binding ProviderOrders}">
<controls:DataGrid.Columns>
<controls:DataGridTemplateColumn Header="Actions" Width="*">
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Modifier" Command="{Binding DataContext.EditOrderCommand, RelativeSource={RelativeSource Mode=Self}}" CommandParameter="{Binding}" Style="{StaticResource PrimaryButton}"/>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
</controls:DataGrid.Columns>
</controls:DataGrid>
I have also tried
<Button Content="Modifier" Command="{Binding ElementName=DataGrid, Path=DataContext.EditOrderCommand}" CommandParameter="{Binding}" Style="{StaticResource PrimaryButton}"/>
There is no error but my Command is not runned and my command is working if i move the Button outside the DataGrid..
The DataGridTemplateColumn DataContext is the ProviderOrder object and so i need to access of the ViewModel (which is obviously not accessible from the ProviderOrder object)
Thanks in advance :)
Great question, this known issue in DataGrid control. Currently, there is a workaroung for this scenario that bind command for button in CellTemplate, please add the command in the datasouce.
public class Item
{
public string ID { get; set; }
public ICommand BtnCommand
{
get
{
return new CommadEventHandler<Item>((s) => BtnClick(s));
}
}
private void BtnClick(Item s)
{
}
}
Xaml Code
<controls:DataGridTemplateColumn>
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding BtnCommand}"
Content="Click"
/>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
Update
If not specific DataGrid, you could use listview to replace, and Binding ElementName will work.
So I am trying to pass the SelectedItem as a parameter so that I can make use of the data that is bound to it.
Essentially I want to open a MessageBox and display the Name property of the User that is bound that item.
This is my xaml
<ItemsControl ItemsSource="{Binding CardViewModel.Users}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserCard>
<controls:UserCard.ContextMenu>
<!-- Bind the DataContext of the CM to the DataContext that's bound to the RootObject-->
<ContextMenu DataContext="{Binding DataContext, Source={local:RootObject}}">
<MenuItem Header="Edit"
Command="{Binding CardViewModel.EditUser}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.SelectedItem}"/>
</ContextMenu>
</controls:UserCard.ContextMenu>
</controls:UserCard>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The command works fine everything is bound just fine besides that when I click the MenuItem and it fires the command, I put a breakpoint where the action is and it shows the parameter as null I am suspecting that it's me who is binding it wrong.
public void DisplayEditUser(object user)
{
if (user != null)
{
MessageBox.Show("Not null");
}
}
The problem is that ContextMenu.PlacementTarget wasn't the ItemsControl but the UserCard, so binding source resolving will absolutely fail. To solve it, you need to bind ItemsControl.SelectedItem to one property of UserCard such as Tag as a relay.
<controls:UserCard Tag="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=ListBox}}">
CommandParameter="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
I have an ItemsControl control that has an ObservableCollection as its ItemsSource. It also has a button located inside of its DataTemplate. The button's Command property is bound to a RelayCommand in the ViewModel (I'm using MVVM Light) and the CommandParameter is bound to the corresponding item in the ItemsSource.
The problem is that the command never fires, for some reason. Code-behind works fine, on the other hand. When debugging the mouse click event handler I can see that the sender (of type Button) has a CommandParameter filled with the correct data whereas Command is null.
What did I miss here?
XAML:
<ItemsControl ItemsSource="{Binding Users}"
Margin="{StaticResource ContentMargin}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="{StaticResource ImageButtonMargin}"
Style="{StaticResource ImageButtonStyle}"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=DataContext.UserSelectedCommand}"
CommandParameter="{Binding}">
<!--...-->
ViewModel:
private ObservableCollection<User> _users;
private RelayCommand<User> _userSelectedCommand;
public ObservableCollection<User> Users
{
get { return _users; }
set
{
_users = value;
RaisePropertyChanged();
}
}
public RelayCommand<User> UserSelectedCommand
{
get { return _userSelectedCommand; }
}
protected override sealed void SetCommands() // called in the constructor which is in turned called by SimpleIoc
{
userSelectedCommand = new RelayCommand<User>((user) => UserSeriesSelected(user));
}
private void UserSelected(User selectedUser)
{
}
Use named Element binding as binding source inside your data template to access commands from root data context. You can use root grid or other containers as named element. ItemsControl iteself can be used too.
<ItemsControl x:Name="MyItems" ItemsSource="{Binding Users}"
Margin="{StaticResource ContentMargin}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="{StaticResource ImageButtonMargin}"
Style="{StaticResource ImageButtonStyle}"
Command="{Binding ElementName=MyItems, Path=DataContext.UserSelectedCommand}"
CommandParameter="{Binding}">
<!--...-->
You need to add "FindAncestor" to your relative source binding:
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}
In my opinion you should change your strategy, put the command in the User class and from that class notify the view-model through an event.
This is going to simplify your xaml code and, in my opinion, making your view-model more coherent.
In my xaml-code i have the following DataGridTemplateColumn
<DataGridTemplateColumn Header="Category">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="categoryButton" Style="{StaticResource Flat}"
Tag="{Binding Category}"
Command="{Binding SelectCategoryCommand,
UpdateSourceTrigger=PropertyChanged}"
CommandParameter="{Binding ElementName=categoryButton,
Path=Tag}">
<Image Source="{Binding Category, Converter={StaticResource
categoryConverter}}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The SelectCategoryCommand-Property in the ViewModel is:
private ICommand selectCategoryCommand;
public ICommand SelectCategoryCommand
{
get { return this.selectCategoryCommand; }
set
{
this.selectCategoryCommand = value;
OnPropertyChanged("SelectCategoryCommand");
}
}
And in the constructor of the ViewModel I have:
...
this.SelectCategoryCommand = new RelayCommand(SelectCategory);
...
And the SelectCategory-Method is just
private void SelectCategory(object parameter)
{
MessageBox.Show("dummy");
}
The connection between the view and the viewmodel works. I have some other properties where the binding works fine.
Why is the SelectCategory-Method is not invoked?
If you use this code Command="{Binding SelectCategoryCommand, command will be searching in row DataContext (in model class). So if your command is in main view model you should use RelativeSource binding.
<DataGridTemplateColumn Header="Category">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="categoryButton" Style="{StaticResource Flat}" Tag="{Binding Category}"
Command="{Binding Path=DataContext.SelectCategoryCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, UpdateSourceTrigger=PropertyChanged}"
CommandParameter="{Binding ElementName=categoryButton, Path=Tag}">
<Image Source="{Binding Category, Converter={StaticResource categoryConverter}}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The WPF DataGrid is a type of ItemsControl. Now, with an ItemsControl, each individual control generated in the view (the ones for individual items) have their DataContext set to that item in the collection. For example:
<DataGrid ItemsSource="{Binding Foos}" />
public ObservableCollection<Foo> Foos { ... }
In this situation, the DataGridRow's DataContext would be set to an instance of a Foo. My guess is that your command is in the same ViewModel that the collection sits at, and not at the level of the individual items. You'll either have to use the RelativeSource to reference back to the DataGrid itself so you can access the DataContext on that level, or items in your collection will need to be ViewModels of their own that contain the command at their level.
I have a NavigateToAccountsCommand RelayCommand property in the ViewModel. When I bind the same to a button on the page anywhere outside the ListView the command binding is working. However as soon as I move this to a ListView's DataTemplate its not working.
I have tried changing the binding from NavigateToAccountsCommand to DataContext.NavigateToAccountsCommand still not working.
Appreciate your help...
<Page
x:Class="FinancePRO.App.Views.AccountsView"
DataContext="{Binding AccountsViewModel, Source={StaticResource MainViewModelLocator}}"
mc:Ignorable="d">
<Grid>
<!--**This one is working**-->
<Button Command="{Binding NavigateToAccountsCommand}" >
<!--**This one is not working**-->
<ListView ItemsSource="{Binding AllAccounts}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch">
<TextBlock Text="{Binding AccountName}"/>
<Button Command="{Binding NavigateToAccountsCommand}">
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When you are inside the DataTemplate of the ListView, your data context is the current item of the ListView's ItemsSource. As there is nothing called "NavigateToAccountsCommand" property within your AllAcounts' each individual element, binding isn't working.
To fix that, you will need to reference something from the outside of DataTemplate; following should work. It changes the binding to reference the root Grid's DataContext which should have the property NavigateToAccountsCommand accessible. To reference the grid, you have to add Name attribute, and then use ElementName binding.
<Grid Name="Root">
<!--**This one is working**-->
<Button Command="{Binding NavigateToAccountsCommand}" >
<!--**This one is not working**-->
<ListView ItemsSource="{Binding AllAccounts}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch">
<TextBlock Text="{Binding AccountName}"/>
<Button Command"{Binding ElementName=Root, Path=DataContext.NavigateToAccountsCommand}">
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
You can use
<Button x:Name="cmdTabItemCloseButton"
Style="{StaticResource TabItemCloseButtonStyle}"
Grid.Column="1" Margin="15,0,0,0"
Command="{Binding RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type ListView}},
Path=DataContext.NavigateToAccountsCommand}"
CommandParameter="{Binding}"/>
I had a similar problem (Win RT) which I solved by just using:
<GridView
x:Name="itemGridView"
ItemClick="ItemView_ItemClick"
IsItemClickEnabled="True"/>
and then in the Page class:
private void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
//e is the object being clicked
}