views:

180

answers:

3

Hello,

I always set the modifiers on my form to private,I don't like internal nor public.

Till now I used to Invoke like this:

public string Addtext
{
    if(InvokeRequired)
    {
         Invoke((MethodInvoker)delegate
         {
             textbox.text = value;
         });
    }
    else
        textbox.text = value;
}

But adding such property for every member on my form is just not Object Orientated at all.

I want to create a function that will Invoke the parameter(action).I tried my best,but I failed - it requires the form members to be public or internal :(

    public void PerformActionOnForm(Action<FormMain> action)
    {
        var form = Form.ActiveForm as FormMain;
        object s = action.Clone();
        if (form != null)
        {
            form.PerformAction(action);
        }
    }

    public void PerformAction(Action<FormMain> action)
    {
        if (InvokeRequired)
            Invoke(action, this);
        else
            action(this);
    }

two problems in my code:

It requires the property I'd like to change to be != private :(

Doesn't work if the form is not on focus.

+2  A: 

In what way is adding properties for data that needs to be accessed or set outside of the scope of the form "not object oriented at all?" This is really your only option. Code in an anonymous delegate (or any delegate, for that matter) executes in the context in which it was declared. The only facility for getting around visibility issues is reflection, and that is big smelly code smell. Create your properties and use them as appropriate.

As for your second option, I'm assuming that you want to execute this on your "main form". You have two options here: assume that there is only one instance and keep that as a static property on the class, assigning it in the instance constructor.

public partial class MainForm : Form
{
    private static MainForm singletonInstance;

    public static MainForm SingletonInstance
    {
        get { return singletonInstance; }
    }

    public MainForm() : base()
    {
        InitializeComponent();

        singletonInstance = this;
    }
}

public void PerformActionOnForm(Action<FormMain> action)
{
    var form = MainForm.SingletonInstance;
    // object s = action.Clone(); What was this for?
    if (form != null)
    {
        form.PerformAction(action);
    }
}

The other only works if all of your forms are properly "owned" and the only form with no owner is your main form. In that instance you could do this:

public void PerformActionOnForm(Action<FormMain> action)
{
    var form = Form.ActiveForm.TopLevelControl as FormMain;
    // object s = action.Clone(); What was this for?
    if (form != null)
    {
        form.PerformAction(action);
    }
}
Adam Robinson
Thanks! I'd like to get an answer for my first question as well.
John
I did answer your first question (it's really more of a statement than a question). Yes, your properties must not be private. There is nothing non-OO about making properties for information that you need to deal with.
Adam Robinson
A: 

Why do you have so many possible entry points to your form that could be called from other threads? Do the thread marshaling lower down (a controller for your form might help) and you won't have to worry about boilerplate code like this.

Jim Arnold
Please give me more information how to do it,thanks!
John
It's a pretty big topic :) Any work you spin off asynchronously should be tightly controlled, otherwise you're opening yourself up to rampant threading issues (having to scatter Invoke() calls across your forms is a symptom of this). In practice, this means limiting the interactions between threads. Maybe you could ask a more specific question about asynchronous programming with WinForms
Jim Arnold
Where did he say anything about threads...?
Adam Robinson
Why else would you need to use Invoke?
Jim Arnold
+1  A: 

Calling a UI component from a non-UI thread

Assuming you only have one message loop (99% that's the case), then:

public static class SynchronizedInvoker
{
    public static void Invoke(Action action)
    {
        Form form = Application.OpenForms.Cast<Form>().FirstOrDefault();
        if (form != null && form.InvokeRequired)
            form.Invoke(action);
        else
            action();
    }
}

Calling the code:

SynchronizedInvoker.Invoke(() => myForm.Text = myText);

Accessing private UI components

Accessing private UI members is not different from accessing other private members to .NET objects. It's in the nature of a private member not to be accessed from other objects. If you still want access, you'll eitherway have to pass the reference of the UI component to the caller or use reflection to resolve the path to the private object.

An example of passing the reference of the UI component to the caller:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ThreadPool.QueueUserWorkItem(delegate { MyWorker.Run(button1); }); 
    }
}

class MyWorker
{
    public static void Run(Button button)
    {
        SynchronizedInvoker.Invoke(() => button.Text = "running");
        Thread.Sleep(5000); // do some important work here
        SynchronizedInvoker.Invoke(() => button.Text = "finished");
    }
}

Using reflection is technically possible, but not ideal. You need to know the path to the private member, and that requires information about the internals of an object. You should then question yourself why you've made it private in the first place.

taoufik
SynchronizedInvoker.Invoke(() => myForm.button1.Text = "test"; << "Inaccessible due ot its protection level - private",why it doesn't work for me?
John
Thanks for the comment, posting updated.
taoufik
Where did he say anything about threads...?
Adam Robinson