Silverlight Combobox Databinding race condition - wcf

In my quest to develop a pretty data-driven silverlight app, I seem to continually come up against some sort of race condition that needs to be worked around. The latest one is below. Any help would be appreciated.
You have two tables on the back end: one is Components and one is Manufacturers. Every Component has ONE Manufacturer. Not at all an unusual, foreign key lookup-relationship.
I Silverlight, I access data via WCF service. I will make a call to Components_Get(id) to get the Current component (to view or edit) and a call to Manufacturers_GetAll() to get the complete list of manufacturers to populate the possible selections for a ComboBox. I then Bind the SelectedItem on the ComboBox to the Manufacturer for the Current Component and the ItemSource on the ComboBox to the list of possible Manufacturers. like this:
<UserControl.Resources>
<data:WebServiceDataManager x:Key="WebService" />
</UserControl.Resources>
<Grid DataContext={Binding Components.Current, mode=OneWay, Source={StaticResource WebService}}>
<ComboBox Grid.Row="2" Grid.Column="2" Style="{StaticResource ComboBoxStyle}" Margin="3"
ItemsSource="{Binding Manufacturers.All, Mode=OneWay, Source={StaticResource WebService}}"
SelectedItem="{Binding Manufacturer, Mode=TwoWay}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Name}" Style="{StaticResource DefaultTextStyle}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
This worked great for the longest time, until I got clever and did a little client side caching of the Component (which I planned turn on for the Manufacturers as well). When I turned on caching for the Component and I got a cache hit, all of the data would be there in the objects correctly, but the SelectedItem would fail to Bind. The reason for this, is that the calls are Asynchronous in Silverlight and with the benefit of the caching, the Component is not being returned prior to the Manufacturers. So when the SelectedItem tries to find the Components.Current.Manufacturer in the ItemsSource list, it is not there, because this list is still empty because Manufacturers.All has not loaded from the WCF service yet. Again, if I turn off the Component caching, it works again, but it feels WRONG - like I am just getting lucky that the timing is working out. The correct fix IMHO is for MS to fix the ComboBox/ ItemsControl control to understand that this WILL happen with Asynch calls being the norm. But until then, I need a need a way yo fix it...
Here are some options that I have thought of:
Eliminate the caching or turn it on across the board to once again mask the problem. Not Good IMHO, because this will fail again. Not really willing to sweep it back under the rug.
Create an intermediary object that would do the synchronization for me (that should be done in the ItemsControl itself). It would accept and Item and an ItemsList and then output and ItemWithItemsList property when both have a arrived. I would Bind the ComboBox to the resulting output so that it would never get one item before the other. My problem is that this seems like a pain but it will make certain that the race condition does not re-occur.
Any thougnts/Comments?
FWIW: I will post my solution here for the benefit of others.
#Joe: Thanks so much for the response. I am aware of the need to update the UI only from the UI thread. It is my understanding and I think I have confirmed this through the debugger that in SL2, that the code generated by the the Service Reference takes care of this for you. i.e. when I call Manufacturers_GetAll_Asynch(), I get the Result through the Manufacturers_GetAll_Completed event. If you look inside the Service Reference code that is generated, it ensures that the *Completed event handler is called from the UI thread. My problem is not this, it is that I make two different calls (one for the manufacturers list and one for the component that references an id of a manufacturer) and then Bind both of these results to a single ComboBox. They both Bind on the UI thread, the problem is that if the list does not get there before the selection, the selection is ignored.
Also note that this is still a problem if you just set the ItemSource and the SelectedItem in the wrong order!!!
Another Update:
While there is still the combobox race condition, I discovered something else interesting. You should NEVER genrate a PropertyChanged event from within the "getter" for that property. Example: in my SL data object of type ManufacturerData, I have a property called "All". In the Get{} it checks to see if it has been loaded, if not it loads it like this:
public class ManufacturersData : DataServiceAccessbase
{
public ObservableCollection<Web.Manufacturer> All
{
get
{
if (!AllLoaded)
LoadAllManufacturersAsync();
return mAll;
}
private set
{
mAll = value;
OnPropertyChanged("All");
}
}
private void LoadAllManufacturersAsync()
{
if (!mCurrentlyLoadingAll)
{
mCurrentlyLoadingAll = true;
// check to see if this component is loaded in local Isolated Storage, if not get it from the webservice
ObservableCollection<Web.Manufacturer> all = IsoStorageManager.GetDataTransferObjectFromCache<ObservableCollection<Web.Manufacturer>>(mAllManufacturersIsoStoreFilename);
if (null != all)
{
UpdateAll(all);
mCurrentlyLoadingAll = false;
}
else
{
Web.SystemBuilderClient sbc = GetSystemBuilderClient();
sbc.Manufacturers_GetAllCompleted += new EventHandler<hookitupright.com.silverlight.data.Web.Manufacturers_GetAllCompletedEventArgs>(sbc_Manufacturers_GetAllCompleted);
sbc.Manufacturers_GetAllAsync(); ;
}
}
}
private void UpdateAll(ObservableCollection<Web.Manufacturer> all)
{
All = all;
AllLoaded = true;
}
private void sbc_Manufacturers_GetAllCompleted(object sender, hookitupright.com.silverlight.data.Web.Manufacturers_GetAllCompletedEventArgs e)
{
if (e.Error == null)
{
UpdateAll(e.Result.Records);
IsoStorageManager.CacheDataTransferObject<ObservableCollection<Web.Manufacturer>>(e.Result.Records, mAllManufacturersIsoStoreFilename);
}
else
OnWebServiceError(e.Error);
mCurrentlyLoadingAll = false;
}
}
Note that this code FAILS on a "cache hit" because it will generate an PropertyChanged event for "All" from within the All { Get {}} method which would normally cause the Binding System to call All {get{}} again...I copied this pattern of creating bindable silverlight data objects from a ScottGu blog posting way back and it has served me well overall, but stuff like this makes it pretty tricky. Luckily the fix is simple. Hope this helps someone else.

Ok I have found the answer (using a lot of Reflector to figure out how the ComboBox works).
The problem exists when the ItemSource is set after the SelectedItem is set. When this happens the Combobx sees it as a complete Reset of the selection and clears the SelectedItem/SelectedIndex. You can see this here in the System.Windows.Controls.Primitives.Selector (the base class for the ComboBox):
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
int selectedIndex = this.SelectedIndex;
bool flag = this.IsInit && this._initializingData.IsIndexSet;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (!this.AddedWithSelectionSet(e.NewStartingIndex, e.NewStartingIndex + e.NewItems.Count))
{
if ((e.NewStartingIndex <= selectedIndex) && !flag)
{
this._processingSelectionPropertyChange = true;
this.SelectedIndex += e.NewItems.Count;
this._processingSelectionPropertyChange = false;
}
if (e.NewStartingIndex > this._focusedIndex)
{
return;
}
this.SetFocusedItem(this._focusedIndex + e.NewItems.Count, false);
}
return;
case NotifyCollectionChangedAction.Remove:
if (((e.OldStartingIndex > selectedIndex) || (selectedIndex >= (e.OldStartingIndex + e.OldItems.Count))) && (e.OldStartingIndex < selectedIndex))
{
this._processingSelectionPropertyChange = true;
this.SelectedIndex -= e.OldItems.Count;
this._processingSelectionPropertyChange = false;
}
if ((e.OldStartingIndex <= this._focusedIndex) && (this._focusedIndex < (e.OldStartingIndex + e.OldItems.Count)))
{
this.SetFocusedItem(-1, false);
return;
}
if (e.OldStartingIndex < selectedIndex)
{
this.SetFocusedItem(this._focusedIndex - e.OldItems.Count, false);
}
return;
case NotifyCollectionChangedAction.Replace:
if (!this.AddedWithSelectionSet(e.NewStartingIndex, e.NewStartingIndex + e.NewItems.Count))
{
if ((e.OldStartingIndex <= selectedIndex) && (selectedIndex < (e.OldStartingIndex + e.OldItems.Count)))
{
this.SelectedIndex = -1;
}
if ((e.OldStartingIndex > this._focusedIndex) || (this._focusedIndex >= (e.OldStartingIndex + e.OldItems.Count)))
{
return;
}
this.SetFocusedItem(-1, false);
}
return;
case NotifyCollectionChangedAction.Reset:
if (!this.AddedWithSelectionSet(0, base.Items.Count) && !flag)
{
this.SelectedIndex = -1;
this.SetFocusedItem(-1, false);
}
return;
}
throw new InvalidOperationException();
}
Note the last case - the reset...When you load a new ItemSource you end up here and any SelectedItem/SelectedIndex gets blown away?!?!
Well the solution was pretty simple in the end. i just subclassed the errant ComboBox and provided and override for this method as follows. Though I did have to add a :
public class FixedComboBox : ComboBox
{
public FixedComboBox()
: base()
{
// This is here to sync the dep properties (OnSelectedItemChanged is private is the base class - thanks M$)
base.SelectionChanged += (s, e) => { FixedSelectedItem = SelectedItem; };
}
// need to add a safe dependency property here to bind to - this will store off the "requested selectedItem"
// this whole this is a kludgy wrapper because the OnSelectedItemChanged is private in the base class
public readonly static DependencyProperty FixedSelectedItemProperty = DependencyProperty.Register("FixedSelectedItem", typeof(object), typeof(FixedComboBox), new PropertyMetadata(null, new PropertyChangedCallback(FixedSelectedItemPropertyChanged)));
private static void FixedSelectedItemPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FixedComboBox fcb = obj as FixedComboBox;
fcb.mLastSelection = e.NewValue;
fcb.SelectedItem = e.NewValue;
}
public object FixedSelectedItem
{
get { return GetValue(FixedSelectedItemProperty); }
set { SetValue(FixedSelectedItemProperty, value);}
}
protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
if (-1 == SelectedIndex)
{
// if after the base class is called, there is no selection, try
if (null != mLastSelection && Items.Contains(mLastSelection))
SelectedItem = mLastSelection;
}
}
protected object mLastSelection = null;
}
All that this does is (a) save off the old SelectedItem and then (b) check that if after the ItemsChanged, if we have no selection made and the old SelectedItem exists in the new list...well...Selected It!

I was incensed when I first ran into this problem, but I figured there had to be a way around it. My best effort so far is detailed in the post.
Link
I was pretty happy as it narrowed the syntax to something like the following.
<ComboBox Name="AComboBox"
ItemsSource="{Binding Data, ElementName=ASource}"
SelectedItem="{Binding A, Mode=TwoWay}"
ex:ComboBox.Mode="Async" />
Kyle

I struggled through this same issue while building cascading comboboxes, and stumbled across a blog post of someone who found an easy but surprising fix. Call UpdateLayout() after setting the .ItemsSource but before setting the SelectedItem. This must force the code to block until the databinding is complete. I'm not exactly sure why it fixes it but I've not experienced the race condition again since...
Source of this info: http://compiledexperience.com/Blog/post/Gotcha-when-databinding-a-ComboBox-in-Silverlight.aspx

It is not clear from your post whether you are aware that you must modify UI elements on the UI thread - or you will have problems. Here is a brief example which creates a background thread which modifies a TextBox with the current time.
The key is MyTextBox.Dispather.BeginInvoke in Page.xaml.cs.
Page.xaml:
<UserControl x:Class="App.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300"
Loaded="UserControl_Loaded">
<Grid x:Name="LayoutRoot">
<TextBox FontSize="36" Text="Just getting started." x:Name="MyTextBox">
</TextBox>
</Grid>
</UserControl>
Page.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace App
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
// Create our own thread because it runs forever.
new System.Threading.Thread(new System.Threading.ThreadStart(RunForever)).Start();
}
void RunForever()
{
System.Random rand = new Random();
while (true)
{
// We want to get the text on the background thread. The idea
// is to do as much work as possible on the background thread
// so that we do as little work as possible on the UI thread.
// Obviously this matters more for accessing a web service or
// database or doing complex computations - we do this to make
// the point.
var now = System.DateTime.Now;
string text = string.Format("{0}.{1}.{2}.{3}", now.Hour, now.Minute, now.Second, now.Millisecond);
// We must dispatch this work to the UI thread. If we try to
// set MyTextBox.Text from this background thread, an exception
// will be thrown.
MyTextBox.Dispatcher.BeginInvoke(delegate()
{
// This code is executed asynchronously on the
// Silverlight UI Thread.
MyTextBox.Text = text;
});
//
// This code is running on the background thread. If we executed this
// code on the UI thread, the UI would be unresponsive.
//
// Sleep between 0 and 2500 millisends.
System.Threading.Thread.Sleep(rand.Next(2500));
}
}
}
}
So, if you want to get things asynchronously, you will have to use Control.Dispatcher.BeginInvoke to notify the UI element that you have some new data.

Rather than rebinding the ItemsSource each time it would have been easier for you to bind it to an ObservableCollection<> and then call Clear() on it and Add(...) all elements. This way the binding isn't reset.
Another gotcha is that the selected item MUST be an instance of the objects in the list. I made a mistake once when I thought the queried list for the default item was fixed but was regenerated on each call. Thus the current was different though it had a DisplayPath property that was the same as an item of the list.
You could still get the current item's ID (or whatever uniquely defines it), rebind the control and then find in the bound list the item with the same ID and bind that item as the current.

In case you arrive here because you have a Combobox selection problem, meaning, nothing happens when you click on your item in the list. Note that the following hints might also help you:
1/ make sure you do not notify something in case you select an item
public string SelectedItem
{
get
{
return this.selectedItem;
}
set
{
if (this.selectedItem != value)
{
this.selectedItem = value;
//this.OnPropertyChanged("SelectedItem");
}
}
}
2/ make sure the item you select is still in the underlying datasource in case you delete it by accident
I made both errors ;)

Related

Monogame 3.5: Mouse Click Not Detected

My monogame game has stopped responding to mouse clicks. Prior to version 3.5, this was working fine. Here's how I'm currently getting the input:
protected override void Update (GameTime game_time)
{
Mouse_Input (game_time);
}
void Mouse_Input(GameTime game_time)
{
mouse_current = Mouse.GetState();
if (mouse_current.LeftButton == ButtonState.Pressed)
{
// click
}
}
Setting breakpoints in the function reveals all the code is being hit, but LeftButton is always ButtonState.Released.
I've tried with both a wired mouse and the trackpad. Keyboard input is working fine. Anyone else running into this?
I always use this way.
MouseState currentMouseState;
MouseState oldMouseState;
public bool checkClick()
{
oldMouseState = currentMouseState;
currentMouseState = Mouse.GetState();
if (Visible)
{
if (currentMouseState.LeftButton == ButtonState.Pressed && oldMouseState.LeftButton == ButtonState.Released)
{
return true;
}
}
}
If you want to check if the Mouse clicks on a Rectangle (Hud elements for example)
public bool checkClickRectangle(Rectangle rec)
{
oldMouseState = currentMouseState;
currentMouseState = Mouse.GetState();
if (Visible)
{
if (rec.Contains(new Vector2(Mouse.GetState().X, Mouse.GetState().Y)) && currentMouseState.LeftButton == ButtonState.Pressed && oldMouseState.LeftButton == ButtonState.Released)
{
return true;
}
}
}
This was actually a not a problem with Monogame, but a problem in my game logic that was very difficult to track down.
After upgrading to 3.5, I had to reconfigure how my Texture2D's were being loaded, which also meant refactoring some classes. I ended up with a class within a class which were both inheriting from Game.
public class Brush_Control : Game
{
public class Tile : Game
{
Process of elimination narrowed the search to this class. I believe this caused an infinite loop that interfered with the input somehow, but without throwing an error or causing an obvious freeze.
Removing the inner Game reference as a parent fixed the problem, and it turns out I no longer need to have it there anyway.

Disable copy/paste on Xamarin forms input field i.e. Entry

I am working on disabling copy/paste option menus on xamarin forms Entry, I am able to disable copy option using IsPassword=true attribute but this attribute also converts the normal input field to password field, which is not a requirement.
<Entry IsPassword="true" Placeholder="Password" TextColor="Green" BackgroundColor="#2c3e50" />
Thanks in advance.
This has to do with how Forms functions. Using iOS as the example here, the CanPerform override referred to in the other answer's Bugzilla issue is using the UIMenuController as the withSender and not the UITextField itself that might otherwise be expected. This is because the EntryRenderer class is a ViewRenderer<TView, TNativeView> type and subsequently is using whatever TNativeView (in this case, the UITextView) has in its CanPerform. Because nothing is going to be overridden by default, one still sees all of the cut/copy/paste options in the UIMenuController.
As a result, there would be a couple options. You could first make the modification where if you don't want copy/paste but are fine with getting rid of everything else, you can use UIMenuController.SharedMenuController.SetMenuVisible(false, false) in a custom renderer inheriting from EntryRenderer. If you look around on SO, there are similar questions where this is a possible route.
Alternatively, you can create a "true" custom renderer inheriting from ViewRenderer<TView, TNativeView> as ViewRenderer<Entry, YourNoCopyPasteUITextFieldClassName>. The class inheriting from UITextField can then override CanPerform as something like follows:
public override bool CanPerform(Selector action, NSObject withSender)
{
if(action.Name == "paste:" || action.Name == "copy:" || action.Name == "cut:")
return false;
return base.CanPerform(action, withSender);
}
This will require more effort because the custom renderer will not have the same behavior as the EntryRenderer, but as Xamarin.Forms is now open source, you could look to it for some ideas as to how the EntryRenderer functions normally. Something similar would likely have to be done for Android.
Edit: For Android, you can probably use this SO answer as a starting point: How to disable copy/paste from/to EditText
Another custom renderer, this time inheriting from ViewRenderer<Entry, EditText>, and create a class inside of it like this (in the most basic form):
class Callback : Java.Lang.Object, ActionMode.ICallback
{
public bool OnActionItemClicked(ActionMode mode, IMenuItem item)
{
return false;
}
public bool OnCreateActionMode(ActionMode mode, IMenu menu)
{
return false;
}
public void OnDestroyActionMode(ActionMode mode)
{
}
public bool OnPrepareActionMode(ActionMode mode, IMenu menu)
{
return false;
}
}
Then, in your OnElementChanged method, you can set the native control and the CustomSelectionActionModeCallback value:
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.CustomSelectionActionModeCallback = new Callback();
}
}
Doing something like the following appears to disable all of the copy/paste/cut functionality on the custom entry as far as the toolbar goes. However, you can still long click to show the paste button, to which I've poked around a bit hadn't found an answer yet beyond setting LongClickable to false. If I do find anything else in that regard, I'd make sure to update this.

Timers generated from a ListView [duplicate]

I have a scenario. (Windows Forms, C#, .NET)
There is a main form which hosts some user control.
The user control does some heavy data operation, such that if I directly call the UserControl_Load method the UI become nonresponsive for the duration for load method execution.
To overcome this I load data on different thread (trying to change existing code as little as I can)
I used a background worker thread which will be loading the data and when done will notify the application that it has done its work.
Now came a real problem. All the UI (main form and its child usercontrols) was created on the primary main thread. In the LOAD method of the usercontrol I'm fetching data based on the values of some control (like textbox) on userControl.
The pseudocode would look like this:
CODE 1
UserContrl1_LoadDataMethod()
{
if (textbox1.text == "MyName") // This gives exception
{
//Load data corresponding to "MyName".
//Populate a globale variable List<string> which will be binded to grid at some later stage.
}
}
The Exception it gave was
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.
To know more about this I did some googling and a suggestion came up like using the following code
CODE 2
UserContrl1_LoadDataMethod()
{
if (InvokeRequired) // Line #1
{
this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
return;
}
if (textbox1.text == "MyName") // Now it won't give an exception
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be binded to grid at some later stage
}
}
But it still seems that I've come back to square one. The Application again
becomes unresponsive. It seems to be due to the execution of line #1 if condition. The loading task is again done by the parent thread and not the third that I spawned.
I don't know whether I perceived this right or wrong.
How do I resolve this and also what is the effect of execution of Line#1 if block?
The situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.
So only accessing the value so that the corresponding data can be fetched from the database.
As per Prerak K's update comment (since deleted):
I guess I have not presented the question properly.
Situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.
So only accessing the value so that corresponding data can be fetched from the database.
The solution you want then should look like:
UserContrl1_LOadDataMethod()
{
string name = "";
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
}
if(name == "MyName")
{
// do whatever
}
}
Do your serious processing in the separate thread before you attempt to switch back to the control's thread. For example:
UserContrl1_LOadDataMethod()
{
if(textbox1.text=="MyName") //<<======Now it wont give exception**
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be
//bound to grid at some later stage
if(InvokeRequired)
{
// after we've done all the processing,
this.Invoke(new MethodInvoker(delegate {
// load the control with the appropriate data
}));
return;
}
}
}
Threading Model in UI
Please read the Threading Model in UI applications (old VB link is here) in order to understand basic concepts. The link navigates to page that describes the WPF threading model. However, Windows Forms utilizes the same idea.
The UI Thread
There is only one thread (UI thread), that is allowed to access System.Windows.Forms.Control and its subclasses members.
Attempt to access member of System.Windows.Forms.Control from different thread than UI thread will cause cross-thread exception.
Since there is only one thread, all UI operations are queued as work items into that thread:
If there is no work for UI thread, then there are idle gaps that can be used by a not-UI related computing.
In order to use mentioned gaps use System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke methods:
BeginInvoke and Invoke methods
The computing overhead of method being invoked should be small as well as computing overhead of event handler methods because the UI thread is used there - the same that is responsible for handling user input. Regardless if this is System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke.
To perform computing expensive operation always use separate thread. Since .NET 2.0 BackgroundWorker is dedicated to performing computing expensive operations in Windows Forms. However in new solutions you should use the async-await pattern as described here.
Use System.Windows.Forms.Control.Invoke or System.Windows.Forms.Control.BeginInvoke methods only to update a user interface. If you use them for heavy computations, your application will block:
Invoke
System.Windows.Forms.Control.Invoke causes separate thread to wait till invoked method is completed:
BeginInvoke
System.Windows.Forms.Control.BeginInvoke doesn't cause the separate thread to wait till invoked method is completed:
Code solution
Read answers on question How to update the GUI from another thread in C#?.
For C# 5.0 and .NET 4.5 the recommended solution is here.
You only want to use Invoke or BeginInvoke for the bare minimum piece of work required to change the UI. Your "heavy" method should execute on another thread (e.g. via BackgroundWorker) but then using Control.Invoke/Control.BeginInvoke just to update the UI. That way your UI thread will be free to handle UI events etc.
See my threading article for a WinForms example - although the article was written before BackgroundWorker arrived on the scene, and I'm afraid I haven't updated it in that respect. BackgroundWorker merely simplifies the callback a bit.
I know its too late now. However even today if you are having trouble accessing cross thread controls? This is the shortest answer till date :P
Invoke(new Action(() =>
{
label1.Text = "WooHoo!!!";
}));
This is how i access any form control from a thread.
I have had this problem with the FileSystemWatcher and found that the following code solved the problem:
fsw.SynchronizingObject = this
The control then uses the current form object to deal with the events, and will therefore be on the same thread.
I find the check-and-invoke code which needs to be littered within all methods related to forms to be way too verbose and unneeded. Here's a simple extension method which lets you do away with it completely:
public static class Extensions
{
public static void Invoke<TControlType>(this TControlType control, Action<TControlType> del)
where TControlType : Control
{
if (control.InvokeRequired)
control.Invoke(new Action(() => del(control)));
else
del(control);
}
}
And then you can simply do this:
textbox1.Invoke(t => t.Text = "A");
No more messing around - simple.
Controls in .NET are not generally thread safe. That means you shouldn't access a control from a thread other than the one where it lives. To get around this, you need to invoke the control, which is what your 2nd sample is attempting.
However, in your case all you've done is pass the long-running method back to the main thread. Of course, that's not really what you want to do. You need to rethink this a little so that all you're doing on the main thread is setting a quick property here and there.
The cleanest (and proper) solution for UI cross-threading issues is to use SynchronizationContext, see Synchronizing calls to the UI in a multi-threaded application article, it explains it very nicely.
Follow the simplest (in my opinion) way to modify objects from another thread:
using System.Threading.Tasks;
using System.Threading;
namespace TESTE
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Action<string> DelegateTeste_ModifyText = THREAD_MOD;
Invoke(DelegateTeste_ModifyText, "MODIFY BY THREAD");
}
private void THREAD_MOD(string teste)
{
textBox1.Text = teste;
}
}
}
A new look using Async/Await and callbacks. You only need one line of code if you keep the extension method in your project.
/// <summary>
/// A new way to use Tasks for Asynchronous calls
/// </summary>
public class Example
{
/// <summary>
/// No more delegates, background workers etc. just one line of code as shown below
/// Note it is dependent on the XTask class shown next.
/// </summary>
public async void ExampleMethod()
{
//Still on GUI/Original Thread here
//Do your updates before the next line of code
await XTask.RunAsync(() =>
{
//Running an asynchronous task here
//Cannot update GUI Thread here, but can do lots of work
});
//Can update GUI/Original thread on this line
}
}
/// <summary>
/// A class containing extension methods for the Task class
/// Put this file in folder named Extensions
/// Use prefix of X for the class it Extends
/// </summary>
public static class XTask
{
/// <summary>
/// RunAsync is an extension method that encapsulates the Task.Run using a callback
/// </summary>
/// <param name="Code">The caller is called back on the new Task (on a different thread)</param>
/// <returns></returns>
public async static Task RunAsync(Action Code)
{
await Task.Run(() =>
{
Code();
});
return;
}
}
You can add other things to the Extension method such as wrapping it in a Try/Catch statement, allowing caller to tell it what type to return after completion, an exception callback to caller:
Adding Try Catch, Auto Exception Logging and CallBack
/// <summary>
/// Run Async
/// </summary>
/// <typeparam name="T">The type to return</typeparam>
/// <param name="Code">The callback to the code</param>
/// <param name="Error">The handled and logged exception if one occurs</param>
/// <returns>The type expected as a competed task</returns>
public async static Task<T> RunAsync<T>(Func<string,T> Code, Action<Exception> Error)
{
var done = await Task<T>.Run(() =>
{
T result = default(T);
try
{
result = Code("Code Here");
}
catch (Exception ex)
{
Console.WriteLine("Unhandled Exception: " + ex.Message);
Console.WriteLine(ex.StackTrace);
Error(ex);
}
return result;
});
return done;
}
public async void HowToUse()
{
//We now inject the type we want the async routine to return!
var result = await RunAsync<bool>((code) => {
//write code here, all exceptions are logged via the wrapped try catch.
//return what is needed
return someBoolValue;
},
error => {
//exceptions are already handled but are sent back here for further processing
});
if (result)
{
//we can now process the result because the code above awaited for the completion before
//moving to this statement
}
}
This is not the recommended way to solve this error but you can suppress it quickly, it will do the job . I prefer this for prototypes or demos . add
CheckForIllegalCrossThreadCalls = false
in Form1() constructor .
You need to look at the Backgroundworker example:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Especially how it interacts with the UI layer. Based on your posting, this seems to answer your issues.
Here is an alternative way if the object you are working with doesn't have
(InvokeRequired)
This is useful if you are working with the main form in a class other than the main form with an object that is in the main form, but doesn't have InvokeRequired
delegate void updateMainFormObject(FormObjectType objectWithoutInvoke, string text);
private void updateFormObjectType(FormObjectType objectWithoutInvoke, string text)
{
MainForm.Invoke(new updateMainFormObject(UpdateObject), objectWithoutInvoke, text);
}
public void UpdateObject(ToolStripStatusLabel objectWithoutInvoke, string text)
{
objectWithoutInvoke.Text = text;
}
It works the same as above, but it is a different approach if you don't have an object with invokerequired, but do have access to the MainForm
I found a need for this while programming an iOS-Phone monotouch app controller in a visual studio winforms prototype project outside of xamarin stuidio. Preferring to program in VS over xamarin studio as much as possible, I wanted the controller to be completely decoupled from the phone framework. This way implementing this for other frameworks like Android and Windows Phone would be much easier for future uses.
I wanted a solution where the GUI could respond to events without the burden of dealing with the cross threading switching code behind every button click. Basically let the class controller handle that to keep the client code simple. You could possibly have many events on the GUI where as if you could handle it in one place in the class would be cleaner. I am not a multi theading expert, let me know if this is flawed.
public partial class Form1 : Form
{
private ExampleController.MyController controller;
public Form1()
{
InitializeComponent();
controller = new ExampleController.MyController((ISynchronizeInvoke) this);
controller.Finished += controller_Finished;
}
void controller_Finished(string returnValue)
{
label1.Text = returnValue;
}
private void button1_Click(object sender, EventArgs e)
{
controller.SubmitTask("Do It");
}
}
The GUI form is unaware the controller is running asynchronous tasks.
public delegate void FinishedTasksHandler(string returnValue);
public class MyController
{
private ISynchronizeInvoke _syn;
public MyController(ISynchronizeInvoke syn) { _syn = syn; }
public event FinishedTasksHandler Finished;
public void SubmitTask(string someValue)
{
System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));
}
private void submitTask(string someValue)
{
someValue = someValue + " " + DateTime.Now.ToString();
System.Threading.Thread.Sleep(5000);
//Finished(someValue); This causes cross threading error if called like this.
if (Finished != null)
{
if (_syn.InvokeRequired)
{
_syn.Invoke(Finished, new object[] { someValue });
}
else
{
Finished(someValue);
}
}
}
}
Simple and re-usable way to work around this problem.
Extension Method
public static class FormExts
{
public static void LoadOnUI(this Form frm, Action action)
{
if (frm.InvokeRequired) frm.Invoke(action);
else action.Invoke();
}
}
Sample Usage
private void OnAnyEvent(object sender, EventArgs args)
{
this.LoadOnUI(() =>
{
label1.Text = "";
button1.Text = "";
});
}
Along the same lines as previous answers,
but a very short addition that Allows to use all Control properties without having cross thread invokation exception.
Helper Method
/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c, Action a)
{
if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate
{
a();
}));
else return false;
return true;
}
Sample Usage
// usage on textbox
public void UpdateTextBox1(String text)
{
//Check if invoke requied if so return - as i will be recalled in correct thread
if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
textBox1.Text = ellapsed;
}
//Or any control
public void UpdateControl(Color c, String s)
{
//Check if invoke requied if so return - as i will be recalled in correct thread
if (ControlInvokeRequired(myControl, () => UpdateControl(c, s))) return;
myControl.Text = s;
myControl.BackColor = c;
}
this.Invoke(new MethodInvoker(delegate
{
//your code here;
}));
For example to get the text from a Control of the UI thread:
Private Delegate Function GetControlTextInvoker(ByVal ctl As Control) As String
Private Function GetControlText(ByVal ctl As Control) As String
Dim text As String
If ctl.InvokeRequired Then
text = CStr(ctl.Invoke(
New GetControlTextInvoker(AddressOf GetControlText), ctl))
Else
text = ctl.Text
End If
Return text
End Function
Same question : how-to-update-the-gui-from-another-thread-in-c
Two Ways:
Return value in e.result and use it to set yout textbox value in backgroundWorker_RunWorkerCompleted event
Declare some variable to hold these kind of values in a separate class (which will work as data holder) . Create static instance of this class adn you can access it over any thread.
Example:
public class data_holder_for_controls
{
//it will hold value for your label
public string status = string.Empty;
}
class Demo
{
public static data_holder_for_controls d1 = new data_holder_for_controls();
static void Main(string[] args)
{
ThreadStart ts = new ThreadStart(perform_logic);
Thread t1 = new Thread(ts);
t1.Start();
t1.Join();
//your_label.Text=d1.status; --- can access it from any thread
}
public static void perform_logic()
{
//put some code here in this function
for (int i = 0; i < 10; i++)
{
//statements here
}
//set result in status variable
d1.status = "Task done";
}
}
Simply use this:
this.Invoke((MethodInvoker)delegate
{
YourControl.Property= value; // runs thread safe
});
Action y; //declared inside class
label1.Invoke(y=()=>label1.Text="text");
There are two options for cross thread operations.
Control.InvokeRequired Property
and second one is to use
SynchronizationContext Post Method
Control.InvokeRequired is only useful when working controls inherited from Control class while SynchronizationContext can be used anywhere. Some useful information is as following links
Cross Thread Update UI | .Net
Cross Thread Update UI using SynchronizationContext | .Net

Flow of initializing objects in XAML?

I'm not sure but from my hours of debugging, this should be the best description of my problem I can give.
I'm creating a WinRT app, there are two pages- Main Page and Details Page. Inside Main Page constructor, I have initialized a listbox. On click of any of the element of listbox, user is taken to the Details page.
I'm just learning all this and design may not be best but here is what I did.
I took a static variable in MainPage.cs, and set it to point to the element which is clicked by the user. Now in the constructor of the Details page, I used this static variable to set the datacontext of Details Page itself.
What flow I'm expecting is:-
MainPage is created first. Listbox is setup.
User will click on any of the element of listbox. Itemclick event handler runs. It will set the static variable (of Mainpage.cs) to hold the infomation which item is clicked and navigate user to the Details page.
In Details page constructor, I have set the datacontext to point to some information based on the value of static variable mentioned in the previous step.
It works for most of the times, but once in like every 5 times, The Details page constructor throws an exception stating the static variable is not initialized yet. Why is Details page's constructor running when I'm starting the app? and why only sometimes? Do I need to set DataContext of Details Page in some other method instead of constructor?
The code is somewhat complex and too much in terms of domain of the problem so I'm avoiding posting it. But if I'm failing to explain the problem please tell, I'll post it keeping it as related as I can.
CODE:-
This is the method called when an item in listbox is clicked--will take user to the Details page.
private void overviewlistbox_Tapped_1(object sender, TappedRoutedEventArgs e)
{
MatchOverview selectedmatch = (sender as ListBox).SelectedItem as MatchOverview;
matchFullDetails = new ObservableCollection<Match>();
foreach (Match m in UpdateController.matchList)
{
if (m.matchDescription == selectedmatch.matchDesc)
{
matchFullDetails.Add(m);
break;
}
}
if(!(matchFullDetails.Count == 0))
this.Frame.Navigate(typeof(Details));
}
This is the constructor for Main Page:-
public static ObservableCollection<Match> matchFullDetails;
public MainPage()
{
matchFullDetails = new ObservableCollection<Match>();
this.InitializeComponent();
UpdateController update = new UpdateController(); // Creating new object will update the overview_list of UpdateController(static list).
overviewlistbox.ItemsSource = UpdateController.overview_list;
}
And this is the code for constructor of details page, where the exception occurs:-
public static ObservableCollection<Match> matchdetails = new ObservableCollection<Match>();
DispatcherTimer dtm_detailspage = null;
public Details()
{
this.InitializeComponent();
matchdetails = MainPage.matchFullDetails; // matchdetails.Last<>() is take because we only need item which is added latest to the collection.
if (matchdetails.Last<Match>().type == "TEST") // Exception is thrown here--Initialization
// error. When I check MainPage.matchFullDetails,
// no data is shown which means its not yet
// initialized. Also the exception is thrown either at
// the start of the app, or when details page is visited. That too once in 4-5 times, not always.
{
matchdetails.Add(matchdetails.First<Match>() as TestMatch);
}
if (matchdetails.Last<Match>().type == "ODI")
{
matchdetails.Add(matchdetails.Last<Match>() as ODIMatch);
}
if (matchdetails.Last<Match>().type == "T20")
{
matchdetails.Add(matchdetails.Last<Match>() as T20Match);
}
}
Exception Screenshot:-
Call Stack data on bug encounter:-
[Cricket Expert.exe!Cricket_Expert.Details.Details() Line 33 + 0x5 bytes
[External Code]
Cricket Expert.exe!Cricket_Expert.Common.SuspensionManager.RestoreFrameNavigationState(Windows.UI.Xaml.Controls.Frame frame) Line 236 + 0x5 bytes
Cricket Expert.exe!Cricket_Expert.Common.SuspensionManager.RestoreAsyn() Line 124 0x8 bytes
Cricket Expert.exe!Cricket_Expert.App.OnLaunched(Windows.ApplicationModel.Activation.LaunchActivatedEventArgs args) Line 74 + 0x5 bytes
[External Code]
MAJOR UPDATE:
I finally found the flaw. If the Details page is still active, and the app is restarted, the problem occurs.
Is there a solution to this problem??
You can pass information on what needs to be displayed on the Details page through the Navigate call and set the DataContext in OnNavigatedTo override to avoid using static variables. Pages don't get created unless you do it specifically e.g. by navigating to one. They might not be recreated if a page has NavigationCacheMode changed from the default (Disabled) so instances of the page can be reused during navigation calls. Ultimately it's hard to say what's wrong but it seems like something in your code and we couldn't help you if you don't share a sample that reproduces the problem.
*EDIT
One way to debug Details being created before MainPage would be to add this code at the beginning of the Details constructor:
if (MainPage.matchFullDetails == null)
{
System.Diagnostics.Debugger.Break();
}
Then look at the Call Stack panel in Visual Studio to see how it gets constructed.
One way to see if matchFullDetails is ever set to null is to search for its assignment (put a cursor on matchFullDetails in Visual Studio code editor and hit Shift+F12).
Another way would be to make matchFullDetails into a property and test it like this:
private static ObservableCollection<Match> _matchFullDetails;
public static ObservableCollection<Match> matchFullDetails
{
get
{
return _matchFullDetails;
}
set
{
if (value == null)
{
System.Diagnostics.Debugger.Break();
}
_matchFullDetails = value;
}
}
*EDIT 2
You can initialize your static property in a static constructor like this:
public static ObservableCollection<Match> matchFullDetails;
static MainPage()
{
matchFullDetails = new ObservableCollection<Match>();
}
public MainPage()
{
this.InitializeComponent();
UpdateController update = new UpdateController(); // Creating new object will update the overview_list of UpdateController(static list).
overviewlistbox.ItemsSource = UpdateController.overview_list;
}
this will prevent the null reference exception but won't fix your problem overall. When your app gets suspended and resumed - you have to restore the full state and it seems like your matchFullDetails collection would need to be serialized and saved to disk when your app gets suspended. Alternatively you might simply ignore the suspension manager call in App.xaml.cs and always start on home page, though that's not a very good experience and I am not sure if it satisfies app certification.

How to execute a binding for a metro control

I want to write the contents of a per occasion active TextBox back to the bound property of the ViewModel when the user presses the key combination for save (Ctrl-S).
My Problem with it is, that I'm not able to trigger the execution of the binding so that the bound Text-Property reflects the contents of the TextBox.
-There seems to be no GetBinding-method. Therefore I can not get the Binding and execute it manualy.
-There is no Validate-method such as in WinForms which executes the Binding
-Giving focus to another control from within KeyDown seems not to work, the binding does not execute
How can I achieve this?
Take a look at Aaron's discussion about this in his WiredPrarie blog post : http://www.wiredprairie.us/blog/index.php/archives/1701
I think I understand your question better now. One way around this would be to use a sub-classed textbox with a new property like this from here:
public class BindableTextBox : TextBox
{
public string BindableText
{
get { return (string)GetValue(BindableTextProperty); }
set { SetValue(BindableTextProperty, value); }
}
// Using a DependencyProperty as the backing store for BindableText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BindableTextProperty =
DependencyProperty.Register("BindableText", typeof(string), typeof(BindableTextBox), new PropertyMetadata("", OnBindableTextChanged));
private static void OnBindableTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs eventArgs)
{
((BindableTextBox)sender).OnBindableTextChanged((string)eventArgs.OldValue, (string)eventArgs.NewValue);
}
public BindableTextBox()
{
TextChanged += BindableTextBox_TextChanged;
}
private void OnBindableTextChanged(string oldValue, string newValue)
{
Text = newValue ? ? string.Empty; // null is not allowed as value!
}
private void BindableTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
BindableText = Text;
}
}
Then bind to the BindableText property.
Solution for command-instances
Here a solution I have found which is relatively leightweight, but also a bit "hackish":
btn.Focus(Windows.UI.Xaml.FocusState.Programmatic);
Dispatcher.ProcessEvent(CoreProcessEventsOption.ProcessAllIfPresent);
btn.Command.Execute(null);
First I give the focus to another control (In my case the button which has the bound command). Then I give the system time to execute the bindings and in the end I raise the command which is bound to the button.
Solution without bound commands
Give the Focus to another control and call the Dispatcher.ProcessEvent(...).
anotherControl.Focus(Windows.UI.Xaml.FocusState.Programmatic);
Dispatcher.ProcessEvent(CoreProcessEventsOption.ProcessAllIfPresent);
// Do your action here, the bound Text-property (or every other bound property) is now ready, binding has been executed
Please see also the solution of BStateham.
It's another way to solve the problem