Windows Workflow: Getting variable from WorkflowApplication - c#

I'm using the Windows Workflow Foundation (WWF). I made an Activity (XAML) with one Sequence in which I defined a variable.
I run the activity in the console application by creating an instance of WorkflowApplication. How can I get the value of a variable in my console application?
I persist an instance of WorkflowApplication in XML and in it I saw my variable and its value. Is there any correct way to get the value of a variable from XML?

So in your last comment you stated you want to get the state in the console application before the workflow is completed. Unfortunately In/Out and Out arguments are only available upon completion of the workflow. But there are ways to communicate with the host process using other constructs than workflow variables and arguments.
One of the ways to do that is to use a custom extension that can be used to interact with the host process. An extensions can be of any type and is available to the workflow and the host process. A complete example:
using System;
using System.Activities;
namespace WorkflowDemo
{
class Program
{
static void Main(string[] args)
{
var app = new WorkflowApplication(new MyCustomActivity());
var myExtension = new MyCommunicationExtension();
myExtension.MyValueChanged += (s, e) => Console.WriteLine(myExtension.MyValue);
app.Extensions.Add(myExtension);
app.Run();
Console.ReadKey();
}
}
public class MyCommunicationExtension
{
public string MyValue { get; private set; }
public event EventHandler<EventArgs> MyValueChanged;
public void OnMyValueChanged(string value)
{
MyValue = value;
MyValueChanged?.Invoke(this, EventArgs.Empty);
}
}
public class MyCustomActivity : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
var extensionObj = context.GetExtension<MyCommunicationExtension>();
if (extensionObj != null)
{
extensionObj.OnMyValueChanged("Hello World");
}
}
}
}

Related

C# EventHandler : How to handle EventHandler in client application?

I have written a class library(dll) which handle Telephony calls. This class library has delegates that handle phone call events such as OnCallReceived, OnHoldCall etc.
So now i want to add this class library to my Windows Forms App, and be able to handle Phone Call events(OnCall, OnHolde etc) in my Windows Forms application. How can achieve this?
For example
//My Class Library
Class Test
{
ThirdParyLibrary tpl
public Test()
{
tpl= new tpl();
tpl.OnReceiveCall += Handler(OnReceiveCall);
}
public void OnReceiveCall()
{
//i want this event to take place in client app
}
}
//My Windows Forms App
Client App
public main()
{
Test t =new Test()
//i want OnReceiveCall to be processed here
//t.OnReceiveCall
{
Message.Show('You received a call');
}
}
// i want this event to take place in client app
Since you want the Event Handling mechanism to take place in the Client App, which I suppose is another Class containing Main, I have created a small console that replicates the problem scenario
Uploaded to fiddle as well
using System;
namespace Test
{
public class ThirdPartyLibrary
{
public delegate void dEeventRaiser();
public event dEeventRaiser OnReceiveCall;
public string IncomingCall(int x)
{
if (x > 0 && OnReceiveCall != null)
{ OnReceiveCall(); return "Valid "+x.ToString(); }
return "Invalid"+x.ToString();
}
}
public class EventSubscription
{
public EventSubscription()
{
ThirdPartyLibrary a = new ThirdPartyLibrary();
a.OnReceiveCall += HandleTheCall;
var iAnswer = a.IncomingCall(24198724);
Console.WriteLine("Call received from "+iAnswer);
}
public virtual void HandleTheCall()
{
Console.WriteLine("Default way I handle the call");
}
}
public class Program : EventSubscription
{
public override void HandleTheCall()
{
Console.WriteLine("Override sucessful, new way to handle the call ");
}
static void Main(string [] args)
{
Program pb = new Program(); // Control goes EnventSubscription constructor as it is derived
Console.Read();
}
}
}
Output:

How can I debug a Xamarin watchOS Linker Error

I am developing an iOS App using Xamarin Forms for which I have created a core model around which all app functionality works.
I would like to include a simple watchOS app which allows the user to operate on a single instance of this model at any one time. I have implemented some code to update the model in the watchOS App using WCSession (via this WCSessionManager Class). I have also reused some code for implementing a timer from my Xamarin Forms project.
However I am encountering a Linker error when building my solution. I think it may be because I have referenced my Xamarin Forms project from my watchOS project, which may not be allowed. Here is the error:
/Users/luketimothy/Projects/TodoQ/TodoQ.Watch/TodoQ.Watch.WatchOSExtension/MTOUCH: Error MT2001: Could not link assemblies. Reason: Error while processing references of 'TodoQWatchWatchOSExtension, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' (MT2001) (TodoQ.Watch.WatchOSExtension)
The file the error references is MTOUCH. I am not sure what this is exactly, but the only place in my watchOS app I have referenced my Xamarin Forms code is this object:
using System;
using System.Collections.Generic;
using TodoQ.Models;
using TodoQ.Utilities;
using WatchConnectivity;
using WatchKit;
namespace TodoQ.Watch.WatchOSExtension
{
internal class TodoState
{
private TodoItem current;
private ISessionTimer timer;
public TodoItem Current { get => current; set { current = value; TaskUpdated(this, value); } }
public event TaskUpdatedEventHandler TaskUpdated;
public delegate void TaskUpdatedEventHandler(object sender, TodoItem current);
public event TimerElapsedEventHandler TimerElapsed;
public delegate void TimerElapsedEventHandler(object sender, TimerElapsedEventArgs current);
public TodoState()
{
WCSessionManager.SharedManager.ApplicationContextUpdated += DidReceiveApplicationContext;
timer = new PomodoroTimer();
timer.ProgressUpdate += (object sender, ProgressUpdateEventArgs e) =>
{
TimerElapsed(this, new TimerElapsedEventArgs() { Elapsed = e.Elapsed, EndTime = e.EndTime });
};
timer.MilestoneUpdate += (object sender, PomodoroStateID e) =>
{
var audio_file = WKAudioFilePlayerItem.Create(WKAudioFileAsset.Create(new Foundation.NSUrl("ShortBreak.wav")));
var audio_player = WKAudioFilePlayer.Create(audio_file);
audio_player.Play();
WKInterfaceDevice.CurrentDevice.PlayHaptic(WKHapticType.Notification);
};
}
public void DidReceiveApplicationContext(WCSession session, Dictionary<string, object> applicationContext)
{
var message = (TodoItem)applicationContext["FocusedItem"];
if (message != null)
{
Console.WriteLine($"Application context update received : {message.Heading}");
Current = message;
}
}
public void StartTimer()
{
timer.StartSession();
}
}
public class TimerElapsedEventArgs
{
public TimeSpan Elapsed;
public TimeSpan EndTime;
}
}
So, my question is. If this ought to be allowed, and the error is something else, could I get some help tracking down what this MTOUCH is and why it's throwing this error? If it is not allowed, what is the recommended solution for sharing this kind of code between my Phone App and my Watch App? Could I put it in a PCL? Should I copy the code between projects?
You should not reference your WatchOS project to the Forms project. It should be added in iOS project directly.
And if you want to define some common code for reuse. You could create a shared library:
Add some public classes there:
namespace UtiLibrary
{
public static class UtiClass
{
public static List<Model> datas { get => new List<Model> { new Model { Name = "name" } }; }
}
public class Model
{
public string Name { set; get; }
}
}
Then you could utilize it on each platform which has referenced this library.

Xamarin Forms - Implementing Singleton

I am actually trying to archive global variables in Xamarin where any page can consume it. After a lot of research, looks like the best way to archive such thing is using the Singleton design pattern. I am facing difficulty to implement this. take a look...
global.cs
using System;
namespace xamarin_forms
{
sealed class Global
{
public string test { get; set; }
private static Global _instance = null;
private Global()
{
}
static internal Global Instance()
{
if (_instance == null)
{
_instance = new Global();
}
return _instance;
}
}
}
App.xaml.cs
using Xamarin.Forms;
namespace xamarin_forms
{
public partial class App : Application
{
Global global = Global.Instance();
public App()
{
InitializeComponent();
MainPage = new PageWelcome();
global.test = "123";
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
Ok, so far, I just created my singleton class with a simple test property. I set this to 123 when I initialize my application.
Now, on another page, welcome page...I'd like to read the value that I set previously on the initialization...
PageWelcome.xaml.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace xamarin_forms
{
public partial class PageWelcome : ContentPage
{
public PageWelcome()
{
InitializeComponent();
Global global = Global.Instance();
DisplayAlert("Alert", global.test, "OK");
}
}
}
Actually this is not working. It's returns me a null. So, how to use this correctly ? Thanks !
In your App's constructor, you first create an instance of PageWelcome. This instance reads the test property of your Global singleton and displays its contents in an alert. At this point, no value has been assigned to that property as far as I can see.
It is only after the PageWelcome constructor finishes that you actually assign a value to the test property of your singleton. Change your App constructor to
public App()
{
InitializeComponent();
global.test = "123";
MainPage = new PageWelcome();
}
and it should work as expected.
You don't need a Singleton.
Just create a static class with your variables static and you would be able to use them on any Page, like you want global variables.
// 1. Create static class Global with string _Test
public static class Global
{
public static void Init()
{
// your init class
}
private static string _Test { get; set; }
public static string Test
{
get => return _Test;
set => _Test = value;
}
}
// 2. Init Global in your App.cs
public App()
{
Global.Init();
}
// 3. Then use them on any page
public PageWelcome()
{
Global.Test = "123";
}

Instantiating a delegate method to be used in a class library

I'm building an email-monitoring framework that I'll be using for a handful of users, so I'm building a class library to wrap everything in. I'm instantiating the configuration (sender, subject, last-received, ...) in a static class. Therefore, I have something like this.
public static class MyConfig
{
public static int Sender { get; set; }
// and so on and so forth
public static void BuildMyConfig(string theSender, string theRecipient, ...)
{
Sender = theSender;
// yada yada yada...
}
}
public class Monitoring
{
public delegate void DoSomethingWithEmail(EmailContents theContents);
public void StartMonitoring() {
//When I get an email, I call the method
DoSomethingWithEmail(theEmailWeJustGot);
}
}
Obviously, what we do with the email will be something completely different in each case. What I'm trying to is instantiate that delegate. Where would I do that? The MyConfig class and then invoke it from there as a static method? The instance of the Monitoring class?
An application would look like...
public class SpecificMonitor
{
Monitoring.BuildMyConfig("foo#bar.com", "bar#foo.com", ...);
Monitoring m = new Monitoring();
m.StartMonitoring();
//But where do I build the delegate method???
}
I've gotten compiling errors with every option I've tried so far. I've also tried overriding a method instead of using a delegate, using interfaces... but I think delegation is where it's at.
Thanks in advance!
Consistent with the rest of your design (although I do not necessarily agree that the design is great) you could allow for the callback to be set in the configuration class
public static class MyConfig
{
public static string Sender { get; set; }
public static DoSomethingWithEmail EmailReceivedCallback { get; set; }
public static void BuildMyConfig(string theSender, string theRecipient,
DoSomethingWithEmail callback)
{
Sender = theSender;
EmailReceivedCallback = callback;
}
}
// Make sure you bring the delegate outside of the Monitoring class!
public delegate void DoSomethingWithEmail(string theContents);
When an incoming email is acknowledged by your application you can now pass the email to the callback assigned to the configuration class
public class Monitoring
{
public void StartMonitoring()
{
const string receivedEmail = "New Answer on your SO Question!";
//Invoke the callback assigned to the config class
MyConfig.EmailReceivedCallback(receivedEmail);
}
}
Here is an example of usage
static void Main()
{
MyConfig.BuildMyConfig("...", "...", HandleEmail);
var monitoring = new Monitoring();
monitoring.StartMonitoring();
}
static void HandleEmail(string thecontents)
{
// Sample implementation
Console.WriteLine("Received Email: {0}",thecontents);
}
Define the constructor so that when people instantiate a Monitoring object, they must define the delegate:
public class Monitoring
{
public delegate void DoSomethingWithEmail(EmailContents theContents);
public Monitoring(Delegate DoSomethingWithEmail)
{
this.DoSomethingWithEmail = DoSomethingWithEmail;
}
public void StartMonitoring() {
//When I get an email, I call the method
DoSomethingWithEmail(theEmailWeJustGot);
}
}
Then pass in the delegate you want when you instantiate each Monitoring:
Monitoring m = new Monitoring(delegate(EmailContents theContents)
{
/* Do stuff with theContents here */
});
m.StartMonitoring();

c# register commandline argument don't start new instance

Application c:\pinkPanther.exe is running and it is application i wrote in c#.
Some other application starts c:\pinkPanther.exe purpleAligator greenGazelle OrangeOrangutan and i would like not to start new instance of c:\pinkPanther.exe with these arguments, but to currently running c:\pinkPanther.exe register it and react to it somehow.
How to do it?
EDIT!!!: i'm very sorry about pinkPanther.exe and ruzovyJeliman.exe that caused the confusion - i translated question from my native language and missed it :(
This is assuming your application is a WinForms app, as that will make it easier to keep it open. This is a very simple example, but it will show you the basics:
Add a reference to Microsoft.VisualBasic.
Create an Application class inheriting from WindowsFormsApplicationBase. This base class contains built-in mechanisms for creating a single-instance application and responding to repeated calls on the commandline with new arguments:
using Microsoft.VisualBasic.ApplicationServices;
//omitted namespace
public class MyApp : WindowsFormsApplicationBase {
private static MyApp _myapp;
public static void Run( Form startupform ) {
_myapp = new MyApp( startupform );
_myapp.StartupNextInstance += new Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventHandler( _myapp_StartupNextInstance );
_myapp.Run( Environment.GetCommandLineArgs() );
}
static void _myapp_StartupNextInstance( object sender, Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs e ) {
//e.CommandLine contains the new commandline arguments
// this is where you do what you want with the new commandline arguments
// if you want it the window to come to the front:
e.BringToForeground = true;
}
private MyApp( Form mainform ) {
this.IsSingleInstance = true;
this.MainForm = mainform;
}
}
All you have to change in Main() is call Run() on your new class rather than Application.Run():
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false );
MyApp.Run( new MyMainForm() );
}
}
WindowsFormsApplicationBase has some other capabilities you can explore, as well.
To communicate with the other instance of the application, you need some sort of inter-process communication. Apparently, WCF is the recommended form of IPC in .Net. You can do that with code like this (using WPF, but WinForms would be similar):
[ServiceContract]
public interface ISingletonProgram
{
[OperationContract]
void CallWithArguments(string[] args);
}
class SingletonProgram : ISingletonProgram
{
public void CallWithArguments(string[] args)
{
// handle the arguments somehow
}
}
public partial class App : Application
{
private readonly Mutex m_mutex;
private ServiceHost m_serviceHost;
private static string EndpointUri =
"net.pipe://localhost/RuzovyJeliman/singletonProgram";
public App()
{
// find out whether other instance exists
bool createdNew;
m_mutex = new Mutex(true, "RůžovýJeliman", out createdNew);
if (!createdNew)
{
// other instance exists, call it and exit
CallService();
Shutdown();
return;
}
// other instance does not exist
// start the service to accept calls and show UI
StartService();
// show the main window here
// you can also process this instance's command line arguments
}
private static void CallService()
{
var factory = new ChannelFactory<ISingletonProgram>(
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), EndpointUri);
var singletonProgram = factory.CreateChannel();
singletonProgram.CallWithArguments(Environment.GetCommandLineArgs());
}
private void StartService()
{
m_serviceHost = new ServiceHost(typeof(SingletonProgram));
m_serviceHost.AddServiceEndpoint(
typeof(ISingletonProgram),
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None),
EndpointUri);
m_serviceHost.Open();
}
protected override void OnExit(ExitEventArgs e)
{
if (m_serviceHost != null)
m_serviceHost.Close();
m_mutex.Dispose();
base.OnExit(e);
}
}

Categories

Resources