'DeferRefresh' is not allowed during an AddNew or EditItem transaction - wpf

I have a tab control in the GUI and there is WPF 4.0 datagrid in one of the tabs. When I click on a cell in the grid and edit something and then switch tabs, I get a Defer Refresh error:
DeferRefresh' is not allowed during an AddNew or EditItem transaction.
So I call datagrid.CancelEdit(DataGridEditingUnit.Row) when tab is switched to cancel any pending edit and the Defer refresh issue is gone.
But what I really want to do is CommitEdit() so that the user doesn't have to reenter the data again.
And datagrid.CommitEdit(DataGridEditingUnit.Row, true) doesn't work for me.
I get the below error on CommitEnd():
Cannot perform this operation while dispatcher processing is
suspended.
PS: I have tried datagrid.CommitEdit() and datagrid.CommitEdit(DataGridEditingUnit.Column, true) and it didnt work.

I solved this by adding this handler for the DataGrid's Unloaded event:
void DataGrid_Unloaded(object sender, RoutedEventArgs e)
{
var grid = (DataGrid)sender;
grid.CommitEdit(DataGridEditingUnit.Row, true);
}

I've run in to this before. WPF only keeps the current tab's view in memory; when you switch tabs, WPF unloads the current view and loads the view of the selected tab. However, DataGrid throws this exception if is currently executing a AddNew or EditItem transaction and WPF tries to unload it.
The solution for me was to keep all the tab views in memory, but only set the current tab's view to visible. This link shows a method of doing this:
WPF TabControl - Preventing Unload on Tab Change?
This change will also make your tabs load more smoothly when you switch between them because the view doesn't have to be regenerated. In my case, the extra memory usage was a reasonable trade-off.

In Xaml :
Loaded="OnUserControlLoaded"
Unloaded="OnUserControlUnloaded"
In Code Behind Inside OnUserControlLoaded and OnUserControlUnloaded Methods:
dataGrid.CommitEdit()
dataGrid.CancelEdit()

I just solved a similar problem by "commiting" changes to the DataTable that is my source of datas.
So if you have a DataTable in the source you can try the following code :
DataTableSource.AcceptChanges();

I've fixed that issue by adding this piece of code:
private void tabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (tabControl.SelectedIndex == 1)
{
WPFdataGrid.CancelEdit(DataGridEditingUnit.Row);
}
}
I think it's a problem of UI threads.

Related

How to change cursor while sorting DataGrid?

When DataGrid (wpf) have many rows sorting can take quite long time (up to 5-10 seconds). How to change cursor to Cursors.Wait while searching?
I need somthing like this:
Xaml:
<DataGrid Name="List" SortStart="List_sortStart" SortComplete="sortComplete" />
Xaml.cs
void List_sortStart(object sender, EventArgs e) {
this.Cursor = Cursors.Wait;
}
void List_sortComplete(object sender, EventArgs e) {
this.Cursor = Cursors.Arrow;
}
But DataGrid does not have SortStart and SortComplete events.
The problem is with the WPF/rendering architecture lacking determinacy when processing user interface updates; in this case, a DataGrid sort change. The DataGrid sort operation is begun with a mouse click on the column, which then updates the CollectionView, which finally is rendered visible in the DataGrid at a point later in time. To implement a mouse cursor change as you wish, you need to change the cursor to your busy cursor at the start of the sort operation, and then defer the change back to the normal cursor to a point in time when the user interface context is finished with its final layout work. LUCKILY THIS IS POSSIBLE!
First, one needs a reference to the main rendering thread's dispatcher. A simple way to get it is to create a class-level data item (in the code-behind .CS file) which is initialized by the main user interface thread:
private static readonly Dispatcher UIDispatcher = Dispatcher.CurrentDispatcher;
Next, (in XAML) reference a code-behind handler for the DataGrid's Sorting event, which is executed whenever a sorting operation begins:
<DataGrid ... Sorting="DataGrid_Sorting">
The handler in the code-behind file looks like this:
private void DataGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
Mouse.OverrideCursor = Cursors.Wait;
UIDispatcher.BeginInvoke((System.Action) (() => { Mouse.OverrideCursor = null; }),
DispatcherPriority.ContextIdle);
}
Note several things in the above code. First, when the sorting operation begins, we override the mouse cursor with the wait animation cursor on the first line. Next, we schedule code to execute on the user interface dispatcher with the DispatcherPriority.ContextIdle priority. This is the secret sauce that defers the code to change the mouse cursor back to normal.
The code to change the mouse cursor back to normal:
Mouse.OverrideCursor = null;
is only executed after the user interface dispatcher is finished with all of the processing of the sorting change/layout update logic and then becomes "idle".
There it is. This "trick" can be useful in many cases when coding for WPF. Put it in your quiver.
DataGrid has Sorting event which occur when the sorting is about to begin. You can attach List_sortStart method to this event.
But then the problem come, as far as I can find, DataGrid doesn't have event that occur when sorting completed. One possible way to workaround this limitation is by creating custom DataGrid with a kind of sorting completed event, see the example in this other SO post :
<local:DataGridExt Name="List" Sorting="List_sortStart" Sorted="List_sortComplete" />

Wpf WebBrowser navigate throws COM exception

This is the code I was using, when I use webbrowser navigate to a page, it throws a COM exception. Is there any help here? Thanks.
Based on your comments in our conversation, your WebBrowser control is not loaded at the time you're trying to navigate. Therefore, you need to create a Loaded event handler and place your navigation code into that.
webBrowser.Loaded += WebBrowser_Loaded; // or in XAML
void WebBrowser_Loaded(object sender, RoutedEventArgs e)
{
// your navigation code here or set a flag
}
If you don't wish to go through the event route, you must, at the very least, check if the control IsLoaded prior to attempting navigation (in your particular scenario).
If the issue persists, create a new instance of the WebBrowser control and navigate through that.
webBrowser = new WebBrowser();

UI freezed instead of showing BusyIndicator in Silverlight

This is a side-related question to this other question:
BackgroundWorker in Silverlight ViewModel
I have a TabControl where I load many TabItems when the user selects menu options. I load this Tabs by binding the TabControl ItemsSource to an ObservableCollection. When I add a new TabItem to this Collection, it is shown perfectly.
The problem is I've realized that since user press a button until the tab is created (ViewModel and View creation takes a couple of seconds), the screen is freezed.
I've tried to set "IsBusy" before calling the "loadTab" but it doesn't shows up... I've tried almost everything with async calls but the UI thread is in use and it throws an exception when I create the new tab control.
Is there any trick I'm loosing??? Any ideas??? Thanks in advance.
have you seen this post?
http://www.dotnetspark.com/kb/3524-doesnt-your-girlfriend-deserves-more-time.aspx
It helps when you avoid heavy stuff in the load event and make Visible=true after you finish to load all your resources, so in that sense you avoid the user feeling tempted to click something that is not ready yet.
Not sure if it helps, but how about this idea?
public void DoStuff(Object values)
{
//your values object could be anything,
//they might even be some objects from your form
//as long as you dont modify them in the other thread
imgLoading.Visible=true;
var client = new Proxy();
client.OnWorkCompletedAsync +=client_OnCompleted() ;
client.Work(values);
}
void client_OnCompletedAsync(object sender, EventArgs e)
{
imgLoading.Visible=false;
//now you can update the UI with other stuff
}

Setting Default Keyboard Focus On Loading A UserControl

I have an MVVM setup with a mainwindow that contains a ContentControl.
I set this to a particular viewmodel which then maps to a view.
A view is a usercontrol.
I want to be able to set the default keyboard focus to a default element in the usercontrol(View) when it loads so the application can eventually be driven just by using up, down, left, right and enter.
Some of my failed attempts are setting
FocusManager.FocusedElement="{Binding ElementName=DefaultElement}"
in my content control tag. This sets the logical focus but not the keyboard focus
I'd rather keep the solution in xaml if possable but have tried placing the following in code behind.
Keyboard.Focus(DefaultElement);
This does not work but if I popup a message box first it does. I'm a little confused as to why.
MessageBox.Show(Keyboard.FocusedElement.ToString());
Keyboard.Focus(DefaultElement);
EDIT::::
I just placed this in my onloaded event of my user control. It seems to work but can anyone see any issues that might arrise at this priority level. I.E a circumstance when the action will never run?
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
Keyboard.Focus(DefaultElement);
}));
It seems that this wpf the you have to implement a workaround on a case by case basis. The solution that seemed to work best, most of the time for me was to insert the focus code inside the dispatcher when OnVisible was changed. This sets the focus not only when the View/Usercontrol loads but also if you a changing Views by way of Visibility. If you Hide and then Show a ContentControl that is mapped to your ViewModels then the Loaded event won't fire and you'll be forced to Mouse input, or tabbing (Not so good if you want to navigate your app with a remote control).
VisibilityChanged will always fire however. This is what I ended up with for my listbox.
private void ItemsFlowListBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
ItemsFlowListBox.Focus();
ItemsFlowListBox.ScrollIntoView(ItemsFlowListBox.SelectedItem);
}));
}
}
I had the same symptom for a WPF UserControl hosted in a Winforms application. Just wanted to note I was about to try this solution when I found a normal TabIndex in the Winforms app fixed it
Per How to set which control gets the focus on application start
"The one with the minimum tab index automatically gets the focus
(assuming the TabStop property is set to true). Just set the tab
indices appropriately."
It's a tricky one with no easy answer. I'm currently doing this, although I'm not sure I like it:
public MyView()
{
InitializeComponent();
// When DataContext changes hook the txtName.TextChanged event so we can give it initial focus
DataContextChanged +=
(sender, args) =>
{
txtName.TextChanged += OnTxtNameOnTextChanged;
};
}
private void OnTxtNameOnTextChanged(object o, TextChangedEventArgs eventArgs)
{
// Setting focus will select all text in the TextBox due to the global class handler on TextBox
txtName.Focus();
// Now unhook the event handler, since it's no longer required
txtName.TextChanged -= OnTxtNameOnTextChanged;
}
And in case you're wondering what the global class handler does, it's this:
protected override void OnStartup(StartupEventArgs e)
{
...
// Register a global handler for this app-domain to select all text in a textBox when
// the textBox receives keyboard focus.
EventManager.RegisterClassHandler(
typeof (TextBox), UIElement.GotKeyboardFocusEvent,
new RoutedEventHandler((sender, args) => ((TextBox) sender).SelectAll()));
which auto selects TextBox text when receiving keyboard focus.

WPF DataGrid how to get when ItemsSource updates

Which event fires when DataGrid's source is updating? I've tried DataContextChanged and SourceUpdated but it never worked out.
Actually I need a simple thing. I want, if there is a new row comes, scroll the GridView's scrollbar down to the bottom to see what it was.
I had the same problem and I manage it this way
DataGrid myGrid = new DataGrid();
CollectionView myCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(myGrid.Items);
((INotifyCollectionChanged)myCollectionView).CollectionChanged += new NotifyCollectionChangedEventHandler(DataGrid_CollectionChanged);
You then need to implement the logic in the event handler DataGrid_CollectionChanged.
Set NotifyOnTargetUpdated = true for the ItemsSource binding and handle TargetUpdated event. If you've multiple bindings, then look for DataTransferEventArgs Property to find out if the target is ItemsSource or not.
If you are trying to have the grid refresh when something is added to the database itself, that's not going to happen. I'm more familiar with WinForms than WPF but I'm assuming there is no magical way to keep a grid in sync with the database without writing some background process that continuously checks for database changes.
If you are updating the actual data source of the grid (ex. Collection) then that will update the grid.
For my part i've used SelectionChange notification which raise each event Del/Add/Edit/Select
It's work very well
private void dataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Console.WriteLine("hi");
}

Resources