views:

264

answers:

8

Is it possible to create a context that is accessible from a set of windows but not from a different set?

For example, two web request are running in their own context and objects like ServiceSecurityContext.Current are pointing to different instances.

My application is a Word like application with a PlanWindow that open a plan document. I would like to be able to do something like PlanContext.Current which would return me the current plan, instead of having to pass the document around to any dialog that is opened from this PlanWindow.

I presume that this has something to do with threading but I don't know where to start.

A: 

A static dictionary? PlanContext[sessionID] ?

ifwdev
But that would force me to carry a SessionId around. If I am to pass a value to all form and object, it must as well be the document itself.
Pierre-Alain Vigeant
A: 

I've never done this, so I can only point you in a direction that seems correct.

First, here's how to use Application.Run on two threads to make to multiple "main" windows in a Windows Forms Application, each on their own thread:

http://msdn.microsoft.com/en-us/library/ms157901.aspx

Secondly, here's how to have per-thread state:

http://stackoverflow.com/questions/1561036/how-does-httpcontext-current-work-in-a-multi-threaded-environment/1561059#1561059

http://blogs.msdn.com/jfoscoding/archive/2006/07/18/670497.aspx

Greg
A: 

I think that this has nothing to do with threading.

If by Current, you mean Selected or Active Plan, then you can set a Property on the PlanWindow object that tracks the selected Plan (i.e: updated whenever a different plan is selected/activated) and have this property be accessible to all other windows/dialogs (make it static for example)

najmeddine
+1  A: 

How about solving this with the following architecture:

IPlanContextReceiver
{   
     public object StateByWichPlanContextCanDeciceWhatToReturn get;
}

class SomeWindow : Window, IPlanContextReceiver

And in PlanContext, instead the Current property you have

public static PlanContext GetCurrent(IPlanContextReceiver receiver)
{
     lock(contextSync) // be threadsafe if necessary
     {
         if(/*condition that checks receiver.StateByWichPlanContextCanDeciceWhatToReturn*/)
         {
            // context is valid for this receiver
            // return the correct context from an internal store or similar
            return Contexts["TheContextForCoolReceivers"];
         }
         else if(/*condition that checks receiver.StateByWichPlanContextCanDeciceWhatToReturn*/)
         {
            // context is valid for this receiver
            // return the correct context from an internal store or similar
            return Contexts["TheContextForUncoolReceivers"];
         }

         // no existing context is available for this receiver
         return null;
    } 
}

If you make it a singleton again GetCurrent could also be an instance method. And instead of a Method it could also be a indexer - that is a matter of taste.

Also, it is up to you what StateByWichPlanContextCanDeciceWhatToReturn actually is. It could be as complex as multiple properties that need to be checked or it could be as simple as a string that you set once for each f your window context groups.

bitbonk
A: 

I don't do C, but in Java you have the java.lang.ThreadLocal for this.

public class MyClass {

    private static ThreadLocal<MyClass> instance = new ThreadLocal<MyClass>() {
        protected View initialValue() {
            return null;
        }
    };

    private MyClass(Object arg1, Object arg2) {
        this.arg1 = arg1; // Web request?
        this.arg2 = arg2; // Web response?
    }

    public static MyClass getInstance() {
        return instance.get();
    }

    public static MyClass createInstance(Object arg1, Object arg2) {
        MyClass myClass = new MyClass(arg1, arg2);
        setInstance(myClass);
        return myClass;
    }

    protected static void setInstance(MyClass myClass) {
        if (myClass == null) {
            instance.remove();
        } else {
            instance.set(myClass);
        }
    }

    public void release() {
        setInstance(null);
    }

}

You call createInstance() at the moment when it is to be created for the first time. Then you can call getCurrentInstance() at every moment you want while still running the same thread.

See if something similar exist in C. Good luck.

BalusC
A: 

Hi,

First, i'll assume that you're application is a MDI Windows Form one and each child windows displays a document to the user.

1) Flag all your Window (child windows) with this interface :

public interface IPlanViewer
{
    //this means that all you windows will have this property
    PlanDocument Document { get; }
}

2) Create the "Context" class

public class Context
{
    public static Context Current { get; set; }
    static Context()
    {
        Current = new Context();
    }

    //The key method : I'm returning the document of the currently selected child windows or null if no windows are opened
    public PlanDocument Document
    {
        get { 
            var currentView =  Form.ActiveForm as IPlanViewer;
            if (currentView != null)
                return currentView.Document;
            else
                return null;
        }
    }
}

3) Use this context like this

private void WorkWithCurrentDoc() 
{
    var doc = Context.Current.Document;
    doc.Title = "totot"
    // etc ...
}

That's it. I hope this will help you.

Manitra.

Manitra Andriamitondra
The problem I have with using Form.ActiveForm is that if I am in a Dialog, this is now the active form and not the IPlanViewer.
Pierre-Alain Vigeant
Hi Pierre Alain, I understand your problem, I just submited another answere that could be better.
Manitra Andriamitondra
+1  A: 

Hi again,

So here is another proposition wich work even if the active form is a dialog box :

1) Flag all your Window (child windows) with this interface :

public interface IPlanViewer
{
    //this means that all you windows will have this property
    PlanDocument Document { get; }
}

2) Create the "Context" class

public class Context
{
    public static Context Current { get; set; }
    public IPlanDocument Document { get; set; }
    static Context()
    {
        Current = new Context();
    }
}

3) In you MDI form handle the MdiChildActive event with this method

private void MdiForm_MdiChildActivate(object sender, EventArgs e)
{
    var currentView = Form.ActiveForm as IPlanViewer;
    if (currentView != null)
        Context.Current.Document = currentView.Document;
}

4) Use this context like this

private void WorkWithCurrentDoc() 
{
    var doc = Context.Current.Document;
    doc.Title = "totot"
    // etc ...
}

That's it. Is it any better ?

Manitra.

Manitra Andriamitondra
A: 

Hi,

I had tackled a similar problem in one of my projects. I used the GetData and SetData methods of System.Runtime.Remoting.Messaging.CallContext.

The CallContext is unique to each thread and you can use it to store objects that are specific to the thread.

In this particular scenario, I would create a PlanContext class like this:

public class PlanContext
{
    const string _PlanDocumentSessionKey = "Current_PlanDocument";
    public static PlanDocument Current
    {
     get
     {
      return CallContext.GetData(_PlanDocumentSessionKey) as PlanDocument;
     }
     set
     {
      CallContext.SetData(_PlanDocumentSessionKey,value);
     }
    }
}

And in code where you instantiate the document, add this:

PlanContext.Current = newDocument;

FYI, HttpContext.Current also uses CallContext to retrieve the Context for a particular thread.

LightX
In the Winform context, you have 1 single GUI thread. Using CallContext would always return the same PlanDocument ... So this would be equivalent to a static property named Current on the PlanContext class. Or am I missing something ?
Manitra Andriamitondra
If he is planning to use a single thread, a static variable will do just fine. But I assumed he wants to use multiple threads for each of the PlanWindow he creates (since he was comparing his scenario to two webrequests). Personally, I feel using multiple threads is a better approach since a modal dialog (say print dialog) opened in one of the windows will not block the input for another document.
LightX