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.
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.
I have a View that displays a Part. All parts contain a list of identifiers. In my View I display Part Properties and a DataGrid with all the Identifiers of that part.
Now if I change a value of an identifier, I want another value update to the default. But if I change my identifier value and set the default of the other property - my DataGrid does not update. Only if I click on the cell, then it gets updated after losing focus.
How can I update the View automatically?
I guess the problem is that I do not want to update a direct property of the Part, but a Property in a List that is a property of the Part.
View
<DataGrid>
<DataGridTemplateColumn Header="Company">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="CompanyEditComboBox"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Companies}"
SelectedItem="{Binding Company, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionChanged = "CompanyEditComboBox_SelectionChanged" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Company}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="CompanyType">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="CompanyTypeEditComboBox"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.CompanyTypes}"
SelectedItem="{Binding IdentificationCompanyType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding IdentificationCompanyType, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid>
View Code-Behind
private void CompanyEditComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var vm = (PartViewModel)DataContext;
var box = (ComboBox) sender;
var c = (Company) box.SelectedItem;
vm.SetDefaultCompanyType(c);
}
ViewModel
public void SetDefaultCompanyType(Company c)
{
SelectedIdentification.IdentificationCompanyType = c.DefaultCompanyType;
OnPropertyChanged("IdentificationCompanyType");
}
Solved it. I had to add a
OnPropertyChanged("IdentificationCompanyType");
into the setter of the IdentificationCompanyType in the Identification class. After that it got automatically updated in the DataGrid.
I have a WPF application with a DataGrid (from WPF Toolkit) control. The ItemsSouce property is bound to an ObservableCollection in my ViewModel.
The data grid has a column with a TextBox in it:
<dg:DataGrid.Columns>
<dg:DataGridTemplateColumn Header="Name">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding UserName}" Width="300"/>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
...
I also have an "Add" button to create a new user. When I click this, a new row is created. I would like, however, for the above textbox to get the input focus (on the new row of course). I have looked at:
WPF MVVM Focus Field on Load
WPF-MVVM: Setting UI control focus from ViewModel
How to set focus to textbox using MVVM?
Set focus on textbox in WPF from view model (C#)
But all of them seem to rely on same variation of an "ElementName" binding and none look like they would work in an ItemsControl. What is the correct way to get this behavior?
One way I believe you can do this is to have a trigger on the textbox that handles on Loaded and have it set focus. Something like this
<dg:DataGridTemplateColumn Header="Name">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" Text="{Binding UserName}" Width="300">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<local:SetFocusTrigger/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
And the SetFocusTrigger class is :-
public class SetFocusTrigger : TargetedTriggerAction<Control>
{
protected override void Invoke(object parameter)
{
if (Target == null) return;
Target.Focus();
}
}
Note I haven't tried this out.
I tried to do in pure XAML
<DataGrid CanUserAddRows="True"
ItemsSource="{Binding List}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
Width="300" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}"
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
Width="300" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I added two templates 1 for normal and other for edit mode. so once cell enter edit mode it will focus the text box.
if you always want the text box to be visible then it would have a different behavior as the new row is binded to placeholder and the value is text box will persist and move over to every new row but the real row.
ListView Solution (MVVM)
sample XAML
<StackPanel>
<Button Command="{Binding AddItem}"
Content="Add Item"/>
<ListView ItemsSource="{Binding List}"
Grid.IsSharedSizeScope="True">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="name" />
<!--other columns-->
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Name}"
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" />
<TextBox Text="other column" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
sample VM
public ViewModel()
{
AddItem = new SimpleCommand(i => List.Add(new Person()));
List = new ObservableCollection<object>(new[] { new Person() { Name = "a person"} });
}
public ObservableCollection<object> List { get; set; }
class Person
{
public string Name { get; set; }
}
public ICommand AddItem { get; set; }
so upon adding new item the new row will be created by means of adding a new Person to the List and will be displayed in view with the focus on the text box.
run the sample above and see the behavior, no code behind involved.
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}"/>
I'm using .Net 4.0 and have a DataGrid in a view. I have implemeneted this http://www.codeproject.com/Articles/42227/Automatic-WPF-Toolkit-DataGrid-Filtering to provide filtering. The ItemsSource for the DataGrid is an observable collection of my custom objects.
Each row has a Button that when clicked, passes the selected custom object back via the CommandParameter.
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding Path=DataContext.DeleteMyCustomObjectCommand,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}}}" CommandParameter="{Binding}" Width="20">
<Image Source="/MyThing;component/Images/delete.png" Height="16" Width="16"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
I want to be able to save the filter criteria using this solution http://www.codeproject.com/Articles/42227/Automatic-WPF-Toolkit-DataGrid-Filtering?msg=3342202#xx3342202xx but one of the calls requires a reference to the data grid
QueryController queryController= DataGridExtensions.GetDataGridFilterQueryController(myGrid1);
As i'm using MVVM, I don't have a reference to the DataGrid in my ViewModel. My command execution code (in the ViewModel) looks like this at present
public void DeleteMyCustomObject(object param)
{
MyCustomObject m = param as MyCustomObject;
.....Deletion commands go here
Is there a way I can use multibindings on the CommandParameter of my Delete button to pass back the custom object from the current row, and a reference to the actual DataGrid (or is there a better solution).
Many Thanks
Mick
(1) bound the DataGrid.SelectedItem to a Property in you ViewModel .
(2) send the Grid as the CommandParameter .
<DataGrid Grid.Column="2" Name="DG1" ItemsSource="{Binding}" SelectedItem="{Binding SelectedItem}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding Path=DataContext.DeleteMyCustomObjectCommand,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}}" Width="20">
<Image Source="/MyThing;component/Images/delete.png" Height="16" Width="16"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn>
<DataGrid.Columns>
</DataGrid>