I want to use Async-await tasks in my Unity3d game for IO, Network and other operations. I know I can use IEnumerators and Coroutines, but those have a very limited functionality and fails to work while working with Web Service APIs using TcpClient, HttpClient and other async tasks in C#.
I cannot work with UI Components while in async Task. Suppose I get a string from a Web API, I cannot set Text field's text from async task. How can it be done.
There are many solutions to this problem.
Two very similar implementations are UnityMainThreadDispatcher and Microsoft's own UnityDispatcher
They implement running IEnumerators and Actions to be on the main thread respectively. Both rely on a MonoBehaviour pseudo singleton.
This answer goes into a some more detail and shows an alternative implementation.
Searched all over the internet, but didn't find a proper way to achieve async-await in Unity. Some say to use external plugin, some say not possible. No proper answer. Hence, here is the proper way of doing it.
Like most application frameworks, Unity game runs on main UI thread. Hence changing UI elements from an async task doesn't work as we need to call Unity's API only from a Main thread like calling from Methods and IEnumerators. Methods and IEnumerators run on main UI thread.
Also Unity doesn't provide a method to call Main thread like .Net does in Xamarin.Forms (Device.BeginInvokeOnMainThread).
For that we need to use MVVM architecture. MVVM stands for Model-View-ViewModel. We don't need to import anything, just change the way our project works.
By default, Unity uses singleton approach and doesn't provide any application building
framework. Hence MVVM would be better for making versatile games. Using MVVM, we can standardize our code by splitting UI Management, our Application's logic and Data, by splitting Models, Views and logic. MVVM is widely used in cross platform application development.
In MVVM we bind controls (UI Components) to the properties in ViewModel, and we only change properties in ViewModel. Then ViewModel notifies these property changes to the View and thus those changes are reflected in UI components in our scene.
Create a ViewModel Class
public class MainSceneViewModel : INotifyPropertyChanged
{
public string Title { get { return title; } set { title = value; OnPropertyChanged(nameof(Title)); } }
string title;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
In your Scene Script (Attach to a Gameobject in Unity Inspector).
public class MainSceneScript : MonoBehaviour
{
public Text SceneTitle; // Assign Text Component in Unity Inspector
MainSceneViewModel mainSceneViewModel = new MainSceneViewModel();
void Start()
{
mainSceneViewModel.PropertyChanged += ViewModelPropertyChanged;
DelayedTitleChange();
}
async void DelayedTitleChange()
{
await Task.Delay(2000);
mainSceneViewModel.Title = "This is the Main Scene";
}
// Will be called whenever a property of `MainSceneViewModel` is updated.
void ViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(MainSceneViewModel.Title))
{
SceneTitle.text = mainSceneViewModel.Title;
}
}
// Clean up
void Dispose()
{
mainSceneViewModel.PropertyChanged -= ViewModelPropertyChanged;
}
}
We can also use Model and notify property changed of that model.
Related
I'm pretty new to WPF, and now I stumbled on something for which I could not find the answer anywhere on the internet. I have the following problem:
Within the same solution, I have 2 projects. One is an application that represents a production process, called MaintenancePlanner. The other is a GUI called MaintenancePlannerGUI.
What I want to achieve is the following: upon pressing a button, the simulation of my production process starts (which takes place in MaintenancePlanner). Then, in the MaintenancePlannerGUI, I have for example a progressbar. The value of the progressbar should change according to the value of the property of an object within the MaintenancePlanner simulation.
Therefore, I need to bind this somehow. However, I don't understand how to do this. I make use of the MVVM structure. So my structure looks like follows:
MaintenancePlanner
AssemblyFab.cs
AssemblyLine.cs
ShellModel.cs (something like Program.cs, but specifically to be used for MaintenancePlannerGUI only)
MaintenancePlannerGUI
Views
ShellViewModel.cs
ViewModels
ShellView.xaml
Now, AssemblyLine for example contains a property Speed. Note that multiple instances of AssemblyLine are attached to AssemblyFab, in the form of a List<AssemblyLine> AssemblyLines.
In ShellView.xaml I have a progressbar:
<ProgressBar Width="10" Height="45" Margin="0,5,10,0" Orientation="Vertical" Minimum="0" Maximum="50" Value="{Binding ???}"/>
In ShellViewModel.cs I create an instance of the MaintenancePlanner simulation AssemblyFabSim by creating an instance of ShellModel.cs from MaintenancePlanner where the whole AssemblyFab and its constituents are created, like this:
AssemblyFabSim = new ShellModel();
Now, I tried something very crude like:
Value="{Binding AssemblyFabSim.AssemblyFab.AssemblyLines[0].Speed}
But that obviously didn't work. Another idea that came to my mind is to make use of the NotifyPropertyChanged Methods.
So in that case, I could create a property in ShellViewModel.cs named for example test and bind that to my progressbar. Then I could update test by getting a notification if the property changed in the ShellModel.cs. But then I also need to monitor the changes in AssemblyFab and AssemblyLine from within ShellModel.cs, so to propagate the change from AssemblyLine to AssemblyFab to ShellModel to ShellViewModel to the View. And I am a little bit confused about this approach.
private void ShellModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Speed")
{
test = AssemblyFabSim.AssemblyFab.AssemblyLines[0].MouldCleanInterval;
}
}
I was wondering whether this is indeed the way to go, and if so, how to do this exactly? Or are there perhaps other simpler ways? Thanks in advance!
Edit 1
My ShellViewModel.cs now contains the following, as ShellViewModel inherits the INotifyPropertyChanged class like this ShellViewModel : INotifyPropertyChanged
public ShellModel AssemblyFabSim { get; set; }
AssemblyFabSim.PropertyChanged += ShellModel_PropertyChanged;
private void ShellModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "TestSpeed")
{
test = AssemblyFabSim.AssemblyFab.AssemblyLines[0].Speed;
}
}
private double _test;
public double test
{
get { return _test; }
set
{
_test = value;
NotifyOfPropertyChange();
}
}
And I now bind my progressbar value like Value="{Binding test}. Then ShellModel.cs also will inherit INotifyPropertyChanged and I add:
public void UpdateSpeed()
{
try
{
TestSpeed = AssemblyFab.AssemblyLines[0].Speed;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
NotifyOfPropertyChange(nameof(TestSpeed));
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
And the UpdateSpeed() method is called from within the Assemblyline.
New problem: The value of the processbar gets updated, but only after the simulation is finished I see the result. That is, the GUI just freezes until the simulation stops and it then shows the last value of Speed.
If your shellViewModel has a property like
public ShellModel AssemblyFabSim {get;}
you should be able to bind to it, and if all the other properties in the path is correct they should also work. But as far as I know, bindings does not support indexers, so I do not think AssemblyLines[0] will work.
Wpf does not care about what projects classes are defined in, only that it has a object to bind to, and the properties are correctly named. Usually everything should also implement INotifyPropertyChanged to work well.
But note that deeply nested paths is probably not a good idea, you should try to separate the UI and business logic, so you should try to avoid binding to anything other than viewModel classes designed for it.
Notably, if you have a progress bar you should bind to a Progress<T> object that is handed to the method that needs to report progress. You should avoid using a progress-property on the object performing the work, after all, what would happen if the method was called concurrently from multiple threads?
You need to ensure that you are calling the UpdateSpeed() on a background thread since the UI thread cannot both update the progress bar and execute your code simultaneously.
We want to display a "Loading, please wait..." screen before the application starts executing other functions on a Win CE 5.0 powered device. The application is being developed using .NET Compact Framework 3.5 (C#).
The issue is that, the UI is only updated once the set of current processes are completed, to a stage where other functions will only run with user interaction. C# has a Form.Shown() event, which would allow the application to run other functions, only ONCE the form has been displayed. However, the Compact-Framework does NOT include.
The solution I've approached was using multi-threading, where I would have one thread which would display the "Loading, please wait..." screen, and the other would take care of other function.
The issue with Threading is, when managing UI components, it gives an error if not applied the correct techniques:
public Form1()
{
InitializeComponent();
ThreadStart tstart = new ThreadStart(showLoad);
Thread t1 = new Thread(tstart);
Thread t2 = new Thread(checkConnect);
t1.Start();
t2.Start();
}
private void checkConnect()
{
conn.portConnect(); //Connects to port Successfully
if (conn.isConnected == true) //Variable which checks the connectivity status
{
panelMENUshow();
}
else
{
panelCONFIGshow();
}
}
private void showLoad()
{
if (imgLoading.InvokeRequired)
{
imgLoading.Invoke((Action)(() => imgLoading.Show())); //Image box displaying "Loading, please wait..." shows successfully if I use Control.Invoke
}
//If I don't use Control.Invoke I get the following error:
//"Control.Invoke must be used to interact with controls created on a separate thread."
}
On the showLoad() function, it is not a problem to add Control.Invoke. However, the functions panelMENUshow() and panelCONFIGshow() contains many bits of code that manages UI components, and it would be not practical to use Control.Invoke in every line of code referring to a UI component.
Is there a way of:
stopping the threading but carry on running the code that involves UI management?
practically manage UI components within a thread?
PS: The approach was based on the answer of this post.
Use the refresh function to force the control to be immediately redrawn.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.refresh(v=vs.90).aspx
When writing WinForm apps you want to keep the complexities of threading away from the main UI thread. I've seen too many applications with thread code weaving in and out of them, tying the logic into a knot.
Instead, model anything that requires any kind of threading as a component that, while it may use threads internally, presents a friendly, even-based model to the outside world. These components should not have any direct interaction with your UI.
Given that, your code would look something more like this (just an example, not syntax checked and probably with lots of typos etc, but it will give you an idea):
public class CommunicationObject
{
// you will probably have several EventArgs to define to pass extra info
public event EventHandler<EventArgs> Connected;
// you need this instance to dispatch events to the UI thread
private Control _invoker;
public CommunicationObject(Control invoker)
{
_invoker = invoker;
// start a thread here, or better yet, add an Enabled property or
// Start method to kick it off
}
// from the thread that is doing the real work, call this when you are connected
private void OnConnected()
{
_invoker.Invoke(() =>
{
EventHandler<EventArgs> handler = Connected;
if (handler != null)
{
handler(this, EventArgs.Empty); // eventually you might need your own event args
}
});
}
}
public class Form1 : Form
{
private CommunicationObject _comm;
public Form1()
{
InitializeComponent();
imgLoading.Show(); // show msg until connected
_comm = new CommunicationObject(this);
_comm.Connected += Comm_Connected; // wire up event handler
}
private void Comm_Connected(object src, EventArgs e)
{
if (imgLoading.Visible)
{
imgLoading.Hide(); // hide once connected
}
panelMENUshow();
}
}
Note that the form has no threads in it at all. Push that complexity into stand-alone objects. It might seem like a bit more work, but you will thank yourself later!
During a lengthy (about 1 minute) process I am trying to log some progress by writing time-stamped messages to a text control. But all messages appear at once. Apparently, all PropertyChanged events are queued until my busy process is done, and received by the text control all at once. How can I kind of 'flush' the events in the middle of my busy process? I searched but could not find a Flush/Update/Dispatch call to immediately process queued events.
A multi threaded solution is in question 1194620, but I would first like to avoid multithreading if possible. In older environments (C++, .Net Winforms/ASP) there were always system calls like Update to interrupt a busy process to handle pending events.
Edit: Please don't tell me that that a lengthy process should be in another thread. I agree. But this is inherited code, and before I would even think about converting to multithreaded, I first need to log certain events to understand what it does. Besides, this app has many other problems that need to be fixed first. Also, after fixing problems, the lengthy process might not be lenghty anymore.
The method of writing strings from anywhere in de code I found in question 18888937 and works fine.
This is the code-behind.
Edit: I added the call to the solution in the Accepted Answer.
public partial class App : Application, INotifyPropertyChanged
{
/// <summary>
/// Property for the log message for the TextBlock control
/// </summary>
public string StartupMessage
{
get { return _StartupMessage; }
set
{
if (_StartupMessage.Length == 0)
{
_StartupMessage = string.Format("{0:HH-mm-ss} {1}",
DateTime.Now, value);
}
else
{
_StartupMessage = string.Format("{0}{1}{2:HH-mm-ss} {3}",
_StartupMessage, Environment.NewLine, DateTime.Now, value);
}
OnPropertyChanged("StartupMessage");
}
}
private string _StartupMessage = "";
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
DoEvents();//see the accepted answer below
}
}
this is the text control:
<TextBlock x:Name="textblock_StartupMessages"
Margin="10" TextWrapping="Wrap"
Text="{Binding Path=StartupMessage, Source={x:Static Application.Current}}">
</TextBlock>
and here is how I place messages from another place in the code:
public class AllRoutesViewModel : ViewModelBase
{
public AllRoutesViewModel()
{
(System.Windows.Application.Current as App).StartupMessage =
"start of AllRoutesViewModel()";
avoid multithreading if possible. In older environments (C++, .Net
Winforms/ASP) there were always system calls like Update to interrupt
a busy process to handle pending events.
This is attempting a design pattern on a system which was designed not to behave like the systems you mentioned.
Long running operations should not be done on the GUI thread in WPF.
Notify property change only works when the GUI thread is not blocked because it is inherently a GUI process. The code you have is blocking the GUI thread. If you properly run the task in a background worker, or an async task and properly update your property, the notify will make the GUI behave visually as you actually want and expect.
But by the design you present, to graphically do this is impossible. The best answer is to learn the WPF design pattern and follow it, instead of forcing a different technologies design pattern.
You might consider using Dispatcher.PushFrame.
More information is available about the class here.
Also, here is the relevant code sample from MDSN (slightly modified):
using System.Windows.Threading; //DispatcherFrame, needs ref to WindowsBase
//[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
While this solution might give you want you want in this case, I have to agree with what others have said about design patterns. Please consider something like MVVM in the future.
I have a MvxViewController and in the ViewDidLoad i bind the button click to the viewmodel. When the button is clicked I open another view in which I will need to return a string back to my first view
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand);
set.Apply();
}
public ICommand MyButtonCommand
{
get
{
_myButtonCommand = _myButtonCommand ?? new MvxCommand(MyButtonCommandClick);
return _myButtonCommand;
}
}
private void MyButtonCommandClick()
{
ShowViewModel<ViewModelNumber2>();
}
After some logic is ran in my second view I want to return the string
private void SomeMethodInViewModelNumber2()
{
//Raise event that will get pickup up in MyView
//Or somehow get "SomeString"
if (OnMyResult != null)
OnMyResult ("SomeString");
}
The problem is that I don't want to send the string back using the messenger. I have my reasons but basically because ViewModelNumber2 can be opened from many different places and works slightly different and managing the different messages that would need to be sent back and where to subscribe to these messages would be a mess
Is there any way that I can do something like the below?
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand).OnMyResult((myString) => {Process(myString)});
set.Apply();
}
Or perhaps when I create ViewModelNumber2 I should pass a callBack into the constructor and use that to send the string back from ViewModelNumber2 to MyView1ViewModel
ShowViewModel<ViewModelNumber2>(OnMyResult);
What is the best way to do this?
In short: I don't know what "the best way to do this" is.
The area of ChildViewModel-ParentViewModel messages is complicated - especially because on platforms like Android using Activities and WindowsPhone using Pages you have no guarantee that the ParentViewModel will be in memory when the Child is shown. (Note: this isn't a problem on iOS as its "app suspension" model is simpler)
When I do need one ViewModel returning data to another, then:
Often I try to implement the data collection views as "popup dialogs" rather than as "whole pages" - this makes the parent-child ViewModel relationship more correct - and ensures the parent ViewModel will be in memory when the child closes.
Often I recommend people use a Messenger-based technique like Greg describes in: http://www.gregshackles.com/2012/11/returning-results-from-view-models-in-mvvmcross/
often I've done this messaging via background services rather than via ViewModel-ViewModel messaging (a bit like the way screens are updated in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/tree/master/N-17-CollectABull-Part6)
Another solution I've used is to:
implement a IDropBoxService singleton - with an API like void Deposit(key, value) and bool TryCollect(key, out value)
allow the closing "child" ViewModels to leave "values" when they close
implement IVisible functionality in my "parent" ViewModel - like in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-42-Lifecycles/Lifecycle.Core/ViewModels/FirstViewModel.cs#L10
use the IVisible method to check for messages
To implement anything perfectly, you really should add serialisation code to make sure this all works during "tombstoning" on all platforms... but often this is overkill - for a simple data collection dialog users often don't need "perfect" tombstoning support.
I've got a program I am working on that has multiple windows. The windows are similar in functionality and I want to have a single event handler to cover a button press event for each window in the application. Is this possible?
If you need to bind a handler in code behind you can encapsulate a handler by delegate and inject into the Windows which are required it.
For instance using Action<T>:
Action<string> commonHandler = (parameter) =>
{
// handler code here
};
class MyWindiow
{
public MyWindiow(Action<string> handler)
{
// store to local and assign to button click
// button.CLick += (o, e) => { handler(parameterToBepassed); }
}
}
I'd look into using a framework to help you out here. My favorite is Prism v4.
If you follow the M-V-VM design pattern you're life will be a lot easier. You'll need to understand Data Binding and DataContext.
That being said, if you decide to go this path, you can bind each of your windows to a command:
<Button Command="{Binding DoFooCommand}" Content="DoFoo"/>
You're ViewModel would have a DelegateCommand member to execute.
public class SomeViewModel : NotificationObject
{
public SomeViewModel()
{
DoFooCommand = new DelegateCommand(ExecuteFoo);
}
public DelegateCommand DoFooCommand { get; set; }
private void ExecuteFoo()
{
//Use the EventAggregator to publish a common event
}
}
And finally, somewhere else in your solution you'll have a code file/class that subscribes to the event and waits for someone to publish the event to process it.
public class SomeOtherPlace
{
public SomeOtherPlace()
{
//Use the EventAggregator to subscribe to the common event
}
public void FooBarMethodToCallWhenEventIsPublished(SomePayload payload)
{
//Do whatever you need to do here...
}
}
I realize some of the things were left out (such as what a "SomePayload" is... look into the EventAggregator information), but I did not want to get into it too much. Just give you a guide on where to go for information and some base code to work off of. If you decide to use the EventAggregator then you'll need to ensure that your subscribing call and publishing calls are utilizing the SAME instance of the EventAggregator. You can do this by looking into MEF. Prism is setup to work with MEF perfectly... I'm not going to lie. Doing all this requires a bit of a learning curve, but it will be worthwhile in the end when you can unit test your ViewModels easily and have your code loosely coupled. The EventAggregator is a great way for different classes to communicate to each other without relying on knowing about each other. And MEF is great for having a Container for your services that you want to utilize across your application.
Hope that gave you a bit of insight on how to go about doing what you want to do on the correct path.