We found follwing solution to this Problem
Following code might help you solve the issue of updating a gui element from another thread
Module level
delegate void updateCallback(string tekst);
This is the method to update your element :
private void UpdateElement(string tekst)
{
if (element.Dispatcher.CheckAccess() == false)
{
updateCallback uCallBack = new updateCallback(UpdateElement);
this.Dispatcher.Invoke(uCallBack, tekst);
}
else
{
//update your element here
}
}
This is Working fine with primitive Types like String Int ... but we want to use a Instance of a Class.
Our delegate declaration Looks like this example:
delegate void updateCallback(Maps newMap)
We want to use it to update a Window which is created in a diffrent thread.
Howerever when we using objects we get:
The calling thread cannot access this object because a diffrent thread owns it.
Related
I have a code which reads multiple string-arrays out of a dynamical dll. I'm creating GameObjects out of each string inside that array and i want to group each array of strings as unique groups.
So far, I've managed to give theese GameObject specific tags to group them, but theese tags were pre-created in the Unity Editor. This works so far, but in case the dynamic class provides more arrays than tags i have created previously, it won't be able to group the additional arrays.
My question: is there any way to create new tags via script while the game is running? With theese i could just simply add them to the new GameObjects.
In case that this shouldn't be possible, does someone have an idea of a different way to sort theese groups?
Thanks in advance!
In principle, the tag system is just a Dictionary of Lists. The problem is that Unity's Dictionary us defined at compile-time. The downside of rolling your own Tag system is you need some way to clear destroyed objects.
public class CustomTag : MonoBehaviour
{
private static Dictionary<string, List<GameObject>> Collection = new Dictionary<string, List<GameObject>>();
public static void Register(string tag, GameObject item) {
if (!Collection.ContainsKey(tag))
Collection.Add(tag, new List<GameObject>());
Collection[tag].Add(item);
}
public static void Deregister(string tag, GameObject item) {
if (!Collection.ContainsKey(tag))
return; // No Such Tag
// In case of multiple entries, remove all occurences of item
// If you're sure you will only have one entry per item, you can just use 'Remove'
Collection[tag].RemoveAll(m => m == item);
}
public static IEnumerable<GameObject> FindObjectsWithTag(string tag) {
if (!Collection.ContainsKey(tag))
return null;
return Collection[tag].AsEnumerable();
}
public string Tag;
private void Start() {
Register(Tag, gameObject);
}
private void OnDestroy() {
Deregister(Tag, gameObject);
}
}
Add this component to each GameObject you create, and set the Tag. It will take care of registering and deregistering. You can access the list of objects per tag using CustomTag.FindObjectsWithTag(). Note that with the current setup, objects won't register with the tag system until they Awake (which IIRC won't be until just before next update)
In ShellViewModel I have below command binding to open a new window "Remote View"
public ICommand RemoteViewCommand
{
get { return new RelayCommand(RemoteViewExecute, CanRemoteViewExecute); }
}
private void RemoteViewExecute()
{
if (!CanRemoteViewExecute())
{
return;
}
var shellRemoteView = Application._Container.Resolve<ShellRemoteView>();
if (_ShellRemoteView.DataContext==null)
_ShellRemoteView.DataContext = Application._Container.Resolve<ShellRemoteViewModel>();
shellRemoteView.Show();
}
On startup I have already registered both "ShellRemoteView" and "ShellRemoteViewModel" using lifetime managers to have singleton instance.
_Container.RegisterType<ShellRemoteView>(new ContainerControlledLifetimeManager());
_Container.RegisterType<ShellRemoteViewModel>(new ContainerControlledLifetimeManager());
When shellRemoteView.Show() executed and I close the form, then again on calling shellRemoteView.Show() I'm getting Invalid Operation excepton:Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed.
Is there is any work-around in Unity to get window instance again if its closed.
This line is your problem:
return new RelayCommand(RemoteViewExecute, CanRemoteViewExecute);
Basically you are creating a new view each time you call the Get command. The way to fix this is to put a variable outside your GET statement that is scoped at the ViewModel level. Have it store a reference to the view and return that reference instead of creating a new reference each time. Look at the Singleton pattern for how best to do this.
You should register your View with LifetimeManager to create only one instance. Look at Using Lifetime Managers.
I am using the following code.
public partial class SettingApp
{
public SettingApp()
{
InitializeComponent();
Parallel.Invoke(SetDataInTextBox);
}
private void SetDataInTextBox()
{
txtIncAns.Text = Properties.Settings.Default.IncludeAN;
txtIncAuthor.Text = Properties.Settings.Default.IncludeAutt;
txtIncQuo.Text = Properties.Settings.Default.IncludeQU;
txtIncSpBegin.Text = Properties.Settings.Default.IncludeSP;
}
}
The program gives the following error
The calling thread cannot access this object because a different
thread owns it.
Which is the right way
update :
Whether this is right :
public partial class SettingApp
{
private delegate void SetDataInTextBoxDelegate();
public SettingApp()
{
InitializeComponent();
txtIncAns.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new SetDataInTextBoxDelegate(SetDataInTextBox));
}
private void SetDataInTextBox()
{
txtIncAns.Text = Properties.Settings.Default.IncludeAN;
txtIncAuthor.Text = Properties.Settings.Default.IncludeAutt;
txtIncQuo.Text = Properties.Settings.Default.IncludeQU;
txtIncSpBegin.Text = Properties.Settings.Default.IncludeSP;
}
}
Only the UI thread can access UI elements, which I'm guessing is what those txt things are. Parallel.Invoke is in your case not the UI thread, so the exception is thrown when you try to access the .Text property on the controls.
You need to marshal the call across to the UI thread. In WinForms, controls have various ways to help you do this:
if (myControl.InvokeRequired)
{
myControl.Invoke(...);
}
else
{
myControl.Text = "something";
}
MSDN has an article with examples on it here (VS2010):
http://msdn.microsoft.com/en-us/library/757y83z4(v=VS.100).aspx
Update 1:
For WPF the model is similar, but includes the Dispatcher:
myControl.Dispatcher.Invoke(...);
MSDN Forum Post
MSDN Article
Update 2: Of course, it looks like you don't even need to use multi-threaded code here. I would guess the overhead of using the multi-threaded portion is more than the code you eventually call. Simply remove the use of multiple threads from this section and set the properties directly:
public SettingApp()
{
InitializeComponent();
SetDataInTextBox();
}
private void SetDataInTextBox()
{
txtIncAns.Text = Properties.Settings.Default.IncludeAN;
txtIncAuthor.Text = Properties.Settings.Default.IncludeAutt;
txtIncQuo.Text = Properties.Settings.Default.IncludeQU;
txtIncSpBegin.Text = Properties.Settings.Default.IncludeSP;
}
As Adam said, only the UI thread can access UI elements. In the case of WPF, you'd use myControl.Dispatcher.Invoke().
Since these calls are all going to be invoked on the UI thread anyway, you should remove the Parallel.Invoke() and call the method directly.
Just an alternate suggestion. I would move toward databinding your textboxes to properties of a class, even if you don't want to go the full MVVM design, databinding is your friend in WPF. Then if you need the threading, WPF will handle updating controls on UI thread when the property changes, even when the property is changed on another thread.
hi i have this lines of code that i cant make it work
the goal is simple setting the form1 to visible = false
public static void DoActions(string Cmd){
if(Cmd == true)
{
MainForm.Visible = false;
}
}
but i keep on having this error
An object reference is required for
the non-static field, method, or
property
usually i set the called methond to static.. so the error will go away
but on this case how do i do it?
thanks for any help guys
'System.Windows.Forms.Control.Invoke(System.Delegate)'
This is happening because DoActions is a static method rather than an instance method, however MainForm is an instance field / property. The distinction is that instance methods operate on an instance of the class on which they are defined, wheras static methods do not.
This means that wheras instance methods are able to access properties, fields and methods of their containing class through the this keyword, for example:
// Instance field
Form1 MainForm;
void InstanceMethod()
{
Form1 frm = this.MainForm;
}
You cannot do the same thing from inside a static method (think about it, what instance would it operate on?). Note that C# will implicitly assume the use of the this keyword in places where it makes sense (so the above example could have been written as Form1 frm = MainForm).
See C# Static Methods for an alternative explanation of static vs instance methods (this is an important concept in object oriented programming that you should take the time to understand properly).
In your example you probably want to change DoActions to an instance method (by removing the static declaration):
public void DoActions(string Cmd)
{
if(Cmd == true)
{
this.MainForm.Visible = false;
}
}
This will allow it to access the instance MainForm field / property, however this may cause problems elsewhere in your code in places where you attempt to call DoActions from another static method without supplying an object instance.
Set form opacity and showintaskbar property in property window:
this.Opacity = 0;
this.ShowInTaskbar = false;
Your method is static - and so cannot access MainForm.
Make your method non-static if it is not required to be so.
public void DoActions(string Cmd)
{
if(Cmd == true)
{
MainForm.Visible = false;
}
}
If it is required, then create a static field in your class and ensure it is set before this method runs.
I am working on a PRISM application where we drill down into the data (to get more details).
In my implementation I have a nested MVVM and when I navigate down the tree I would like to pass a model to a my newly created view.
As far as I know, currently PRISM allows to pass strings, but doesn't allow to pass objects. I would like to know what are the ways of overcoming this issue.
i usually use a service where i register the objects i want to be passed with a guid. these get stored in a hashtable and when navigating in prism i pass the guid as a parameter which can then be used to retrieve the object.
hope this makes sense to you!
I would use the OnNavigatedTo and OnNavigatedFrom methods to pass on the objects using the NavigationContext.
First derive the viewmodel from INavigationAware interface -
public class MyViewModel : INavigationAware
{ ...
You can then implement OnNavigatedFrom and set the object you want to pass as navigation context as follows -
void INavigationAware.OnNavigatedFrom(NavigationContext navigationContext)
{
SharedData data = new SharedData();
...
navigationContext.NavigationService.Region.Context = data;
}
and when you want to receive the data, add the following piece of code in the second view model -
void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
{
if (navigationContext.NavigationService.Region.Context != null)
{
if (navigationContext.NavigationService.Region.Context is SharedData)
{
SharedData data = (SharedData)navigationContext.NavigationService.Region.Context;
...
}
}
}
ps. mark this as answer if this helps.
PRISM supports supplying parameters:
var para = new NavigationParameters { { "SearchResult", result } };
_regionManager.RequestNavigate(ShellRegions.DockedRight, typeof(UI.SearchResultView).FullName, OnNavigationCompleted, para);
and implement the INavigationAware interface on your View, ViewModel or both.
you can also find details here: https://msdn.microsoft.com/en-us/library/gg430861%28v=pandp.40%29.aspx