Binding Command to element in DataGrid - c#

I have a table with a with a column containing a bool value that I have put a checkbox in. I am trying to bind a command to the checkbox to where when I check it, it runs a command in the View Model. I'm using an MVVM structure. Here is what I have attempted so far.
<DataGrid
IsReadOnly="True"
Margin="0,10,0,0"
ItemsSource="{Binding Diary.Diaries}"
AutoGenerateColumns="False"
MinHeight="200"
SelectionMode="Single"
SelectionUnit="FullRow"
IsSynchronizedWithCurrentItem = "True"
>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Flagged">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid >
<CheckBox IsChecked="{Binding Flagged}" Command="{Binding Diary.FlagDiary}">
</CheckBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn >
<DataGridTextColumn Header="Diary ID" Binding="{Binding DiaryID}" />
</DataGrid.Columns>
</DataGrid>
And here is the RelayCommand in the ViewModel.
FlagDiary = new RelayCommand(() =>
{
Debug.WriteLine("Test");
});
I have been unable to get it to run the RelayCommand. Any idea what I'm doing wrong?

The Checkbox's DataContext would be set to one of the objects in the DataGrid's ItemSource (one of the Diary.Diaries entries). That makes the command binding invalid.
You will need to do a relative source binding so you can get to Diary.FlagDiary. Here is one way of doing that (given the xaml you posted above).
<CheckBox IsChecked="{Binding Flagged}"
Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.Diary.FlagDiary}" />
Once the command fires, you will probably want to know which Diary entry the checkbox was for... right? To do that, add a CommandParameter binding to the Checkbox. Now, the binding looks like this:
<CheckBox IsChecked="{Binding Flagged}"
Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.Diary.FlagDiary}"
CommandParameter="{Binding}" />
Your relay command code might have to change too. Probably something like this?
FlagDiary = new RelayCommand((parameter) =>
{
Debug.WriteLine(parameter.ToString());
});

Related

Realm Update from WPF EventTrigger

I have a WPF datagrid that is bound to a Realm backed object. The grid has an autocomplete box that allows the user to enter/select an account number which should then update the source object and the UI. Instead, when a selection is made, the UI box goes back to empty and the stored object is not updated. The Realm documentation states Modifying the collection (e.g. adding or removing items) must happen in a write transaction, so somehow, when the user makes a selection I need to trigger a write transaction on that specific row object. This is further confirmed by the error in the output window
System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=SAccount; DataItem='TransactionDetails' (HashCode=24381833); target element is 'DataGridRow' (Name=''); target property is 'NoTarget' (type 'Object') RealmInvalidTransactionException:'Realms.Exceptions.RealmInvalidTransactionException: Cannot modify managed objects outside of a write transaction.
I am trying to use an EventTrigger bound to a command in my VM but the trigger never fires. Can someone explain (or maybe even quickly demonstrate) how to make this work?
In my ViewModel I'm retreiving the object like this:
Transaction = realm.All<Transaction>().Where( t => t.ID == tid ).First();
and then binding it in my XAML view
<DataGrid x:Name="TransactionDataGrid"
DataGridCell.Selected="TransactionDataGrid_GotFocus"
AutoGenerateColumns="False"
CanUserSortColumns="True"
CanUserReorderColumns="False"
HorizontalAlignment="Left"
Margin="10,0,0,0"
VerticalAlignment="Top"
Height="556"
Width="1012"
CanUserAddRows="False"
ItemsSource="{Binding Transaction.Rows}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Source" Header="Source Account" Width="Auto" Binding="{Binding Description}" IsReadOnly="True" />
<DataGridTemplateColumn x:Name="AccountNum" Header="Account Number" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBox BorderThickness="0" Text="{Binding SAccount.RawAccountNumber}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel>
<toolkit:AutoCompleteBox
Background="AliceBlue"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="2"
ValueMemberPath="RawAccountNumber"
PreviewTextInput="AutoCompleteBox_PreviewTextInput"
SelectedItem="{Binding SAccount, Mode=TwoWay}"
Text="{Binding Path=SAccount.RawAccountNumber}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Accounts}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding UpdateRowCommand}"
CommandParameter="{Binding Path=ID}"/><!-- the command parameter should be the ID of the row we are updating? -->
</i:EventTrigger>
</i:Interaction.Triggers>
<toolkit:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Path=RawAccountNumber}" FontWeight="Bold" Width="100"/>
<TextBlock Text="{Binding Path=Description}" />
</StackPanel>
</DataTemplate>
</toolkit:AutoCompleteBox.ItemTemplate>
</toolkit:AutoCompleteBox>
</StackPanel>
The command in my ViewModel
public ICommand UpdateRowCommand
{
set
{
MessageBox.Show( "Hello UpdateRowCommand" );
}
}

Button enbale/disable (inside Datagrid view ) based on value from Database -WPF

In WPF window, Data grid view will load from database.
This is the design code for Data grid view.(It contains 2 columns - Name, Action)
<DataGrid x:Name="dgrid" HorizontalAlignment="Left" Margin="0,65,0,0" VerticalAlignment="Top" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="160"></DataGridTextColumn>
<DataGridTemplateColumn Header="Action" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Name="btnEdit" Content="Edit" />
<Button Name="btnDelete" Content="Delete" />
<Button Name="btnActivate" Content="Activate" />
<Button Name="btnDeactivate" Content="Deactivate" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Name column bind the value from data base value, and following column contain 4 buttons!
what I need to do this, based on another column value of database (for example Activation Status) , the btnActivate, btnDeactivate should set their
is Enable property!
how can I accomplish this?
Thanks in Advance
You need to initiate binding to appropriate property from your button:
<Button Name="btnEdit"
Content="Edit"
IsEnabled="{Binding DataContext.ActivationStatus, Converter={StaticResource MyStatusToBooleanConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridRow}}}" />
You should (providing your value in the DB is a boolean), use bindings to set the states.
For example:
<Button Name="btnActivate" Content="Activate" IsEnabled="{Binding ClassPropertyType}" />
And then when you set your ItemSource with a list of your class, it will bind to the set boolean.

WPF DataBinding in a DataGrid Header

I'm trying to bind a ViewModel property to a Checkbox in the header of a DataGrid.
The checkbox binds just fine if I stick it randomly in the window, but if its in the header of the datagrid, it doesn't bind in either direction.
The data in the DataGrid binds fine as well.
The issue seems to be that the HeaderTemplate isn't binding to the main view model. I assume its binding ItemSource.
How do I bind to the view model in the header?
<DataGrid ItemsSource="{Binding Channels}" AlternationCount="2" Grid.IsSharedSizeScope="True" AutoGenerateColumns="False" AlternatingRowBackground="{StaticResource GroupBackgroundBrush}" SelectedIndex="{Binding Path=CursorChannelInt}" >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=Test}">Test Chkbox</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=stuff}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
This works, by going to the Window, getting its DataContext, and going from there. Is there a better way?
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Test}">Test Chkbox</CheckBox>
The above answers were not working in my case, so I solved using Initialized event trigger
.xaml
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Initialized="CheckBox_Initialized" IsChecked="False">Test Chkbox</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
.xaml.cs
private CheckBox cb_All = null;
private void CheckBox_Initialized(object sender, EventArgs e)
{
cb_All = (CheckBox)sender;
}
private void Function()
{
if(cb_All != null)
cb_All.IsChecked = true; //or false
}
It is sooo late, spending several years.. but I hope this may help to anyone:)
if the property is not in the collection, you have perhaps a nice answer, other rewrite for the same would be to use the ElementName to shorten the binding syntax
sample
<DataGrid ItemsSource="{Binding Channels}" AlternationCount="2" Grid.IsSharedSizeScope="True" AutoGenerateColumns="False" AlternatingRowBackground="{StaticResource GroupBackgroundBrush}" SelectedIndex="{Binding Path=CursorChannelInt}"
x:Name="dGrid">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding DataContext.Test, ElementName=dGrid}">Test Chkbox</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=stuff}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
above sample is based on the assumption that the property Test is in the same VM as Channels property.

RelayCommand doesn't work in some computers

In DataGrid I have a column defined like this.
<DataGridTemplateColumn Width="Auto">
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
APPROACH
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
First approach
<Button Template="{StaticResource CloseTabButton}" Command="{Binding DataContext.DeleteRangeCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" CommandParameter="{Binding}"/>
Second approach
<Button Template="{StaticResource CloseTabButton}" Command="{Binding ElementName=CurrentWindow, Path=DataContext.DeleteRangeCommand}" CommandParameter="{Binding}"/>
In ViewModel:
public RelayCommand<Range> DeleteRangeCommand
{
get { return _deleteRangeCommand ?? (_deleteRangeCommand = new RelayCommand<Range>((x) => ReportGroup.Ranges.Remove(x))); }
}
Both approaches should be identical. Problem is that, on some computers - I guess these without Visual Studio Installed, second approach doesn't work. Nothing is firing, I tried to attach with Remote Debugger and Command is not fired. Why is this possible?
OfCourse CurrentWindow is x:Name of Window with DataContext set to ViewModel

DataGrid Repeated Column

I'm creating a WPF View with a DataGrid.
The DataGrid is bound to the field Properties on the ViewModel.
However, for one of the columns I want each row to have the same value bound to some other property on the View model.
Specificically, the table is showing named monetary values and the repeated column will show a currency code (which is the same for each row).
I can't figure out how to do this, I've tried to use the following:
<DataGrid ItemsSource="{Binding Properties}">
<DataGrid.Columns>
<DataGridTextColumn Header="Target" Binding="{Binding Target}"/>
<DataGridTextColumn Header="Value" Binding="{Binding Value}"/>
<DataGridTemplateColumn Header="Currency">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Properties.NodeCurrency}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I'm using the Caliburn framework with no typed DataContext in the View. I'm not sure if this matters to the question though.
You can try to do it via RelativeSource of Binding. For example:
<TextBlock Text="{Binding Property.NodeCurrency, RelativeSource={RelativeSource AncestorType=Window}}"/>
You can use RelativeSource/FindAncestor on your binding to bind to an other DataContext than the current one :
<TextBlock
Text="{Binding Property.NodeCurrency, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
With assistance from both answerers the actual binding I needed was:
<TextBlock Text="{Binding DataContext.Property.NodeCurrency, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
The missing bit was specifying that I'm binding to the DataContext of the UserControl and then delving down into the properties on the ViewModel.

Categories

Resources