have a XamDataGrid with a DataTable as a source:
<dataPresenter:XamDataGrid Grid.Row="1" DataSource="{Binding TestData}" GroupByAreaLocation="None"/>
I'm using MVVM Light to handle commands. In my view model I have:
public class MainViewModel: ViewModelBase {
public RelayCommand OnCancel { get; set; }
p
private DataTable _testData;
public DataTable TestData { get { return _testData; }
set { Set(() => TestData, ref _testData, value); } }
public MainViewModel() { OnCancel = new RelayCommand(Cancel); }
private void Cancel() { TestData.RejectChanges(); } }
To handle the commands I want to use the XamRibbon:
<igWPF:XamRibbon > <igWPF:XamRibbon.Tabs> <igWPF:RibbonTabItem Header="Logs"> <igWPF:RibbonGroup Caption="Data Controls"> <igWPF:ToolHorizontalWrapPanel> <igWPF:ButtonTool Caption="Get Data" Command="{Binding OnGetData}" /> <igWPF:ButtonTool Caption="Cancel" Command="{Binding OnCancel}" /> </igWPF:ToolHorizontalWrapPanel> </igWPF:RibbonGroup> </igWPF:RibbonTabItem> </igWPF:XamRibbon.Tabs> </igWPF:XamRibbon>
<!--<StackPanel Orientation="Horizontal">
<Button Content="Get Data" Command="{Binding OnGetData}" Margin="0,0,20,0" Height="40"/>
<Button Content="Cancel" Command="{Binding OnCancel}" Height="40"/>
</StackPanel>-->
I'm getting different behavior when using the ButtonTool in XamRibbon vs just the Button. What I want is to reject any changes made in the XamDataGrid. When I use the Button, the changes are rejected in the DataTable and the XamDataGrid updates accordingly. When I change the code so the ButtonTool executes the Cancel method this does not happen. The behavior seems sporadic. The XamDataGrid is not properly updated even though I can see changes have been reject in the DataTable. Why would this be happening?
I've attached a sample project to illustrate my issue.
1. Click "Get Data"
2. Make some random numerical entry in the Value column of the first row.
3. WITHOUT clicking outside of the cell the changes were made in, click the "Cancel" button.
Notice that the changes are rejected in the DataTable but not updated in the XamDataGrid. This is contrary to when simply changing the buttons from the XamRibbon to plain WPF Button control. Pressing "Cancel" rejects the changes and the XamDataGrid is updated.
Hi Kris,
When are you pressing the Cancel button? If I change the value and press the Enter key and then press the Cancel button the change is reverted back. If, however, I change the value and then immediately press the Cancel button then the change does not revert. Which one are you doing? The only difference is that I pressed the Enter key after changing the value which is what commits the value to the datasource.
If you are performing the first action then this is expected. When you click on a button inside the XamRibbon, because of how focus works with ribbon controls, the XamDataGrid never exits edit mode so the typed value remains in place. Actually, you will see this behavior with other controls as well such as a Menu or ToolBar. It's not exclusive to the XamRibbon. The reason a normal Button appears to work is because clicking on it causes the XamDataGrid to exit edit mode which throws away the typed value because it was never committed. So in actuality the changes are not being rejected because of the DataTable.RejectChanges() call but because the value was never committed and edit mode was forced to exit.
For this action you will need to force the XamDataGrid to exit edit mode when the button is clicked. The XamDataGrid has an ExecuteCommand method that you can call and pass in DataPresenterCommands.EndEditModeAndDiscardChanges.
With the second action I listed, pressing the Enter key commits the change to your DataTable and exits edit mode. So pressing the XamRibbon button to cancel the change works because it will call RejectChanges() and undo the change that was made in the DataTable.
Thanks for taking the time to answer this question. I do have another one though. Is there a way to fire the ExecuteCommand command from inside a ViewModel? The problem is that I'm using ContentControl.
MainWindow.xaml:
<Window> <Window.Resources>
<DataTemplate DataType="{x:Type viewModels:TestViewModel}">
<testViews:TestView/> </DataTemplate>
</Window.Resources>
<XamRibbon> <RibbonButton x:Name="CancelButton" Click="CancelButton_OnClick"> </XamRibbon>
<ContentControl Name="MasterContentControl" Content="{Binding CurrentViewModel}" />
</Window>
TestView.xaml:
<UserControl>
<XamDataGrid Name="DataGridInTestView">
</UserControl>
Pressing the cancel button would fire the corresponding method in
MainWindow.xaml.cs but from there
I dont' have access to UI elements in TestView.xaml. Is there a way to
execute ExecuteCommand from within TestViewModel?
That's how I would have done it. Either that or a Blend Behavior<T> that does basically the same thing. It's pretty much the only way to trigger a UI command from your view model.
Actually to answer my own question... you can do it through a dependency property. Not sure if this is the best way but it works.
public class DataGridBehaviors {
public static bool GetEndEditModeAndDiscardChanges(DependencyObject obj) { return (bool)obj.GetValue(EndEditModeAndDiscardChanges); } public static void SetEndEditModeAndDiscardChanges(DependencyObject obj, bool value) { obj.SetValue(EndEditModeAndDiscardChanges, value); }
public static readonly DependencyProperty EndEditModeAndDiscardChanges = DependencyProperty.RegisterAttached("EndEditModeAndDiscardChanges", typeof(bool), typeof(DataGridBehaviors), new PropertyMetadata(false, OnEndEditModeAndDiscardChanges));
private static void OnEndEditModeAndDiscardChanges(DependencyObject d, DependencyPropertyChangedEventArgs e) { var dataGrid = d as XamDataGrid; if ((bool) e.NewValue) { dataGrid.ExecuteCommand(DataPresenterCommands.EndEditModeAndDiscardChanges); } } }
<dataPresenter:XamDataGrid main:DataGridBehaviors.EndEditModeAndDiscardChanges="{Binding EndEditModeAndDiscardChanges}" />
In ViewModel:
private void Cancel() { EndEditModeAndDiscardChanges = true; TestData.RejectChanges(); EndEditModeAndDiscardChanges = false; }