4334738290
4334738290

Reputation: 393

Accessing form controls from multiple forms

I have 2 forms and 1 file to upload to youtube. I am accessing them like so from both forms (both of the forms don't interact together)

await new UploadVideo().Run(video);

Now inside my uploadvideo class I am trying to get the percentage uploaded to use in my form

    void videosInsertRequest_ResponseReceived(Video video)
    {
        //core.prog_up.Text = "Video id '{0}' was successfully uploaded." + video.Id;
    }

In both of the forms, I have the exact same form controls, so the naming convention is exactly the same. So depending on which form I initiated the uploadvideo class I want the form component to be accessed from the uploadclass.

I have named my forms: Form1 and Form2

I can iniate one by doing :

Form1 frm = new Form1();

But then I can't access Form2 if I initiate it from that form

Upvotes: 1

Views: 66

Answers (2)

Peter Duniho
Peter Duniho

Reputation: 70652

depedning on which form I initiate tge uploadvideo class I want the form component to be accessed from the uploadclass

No, not really. You only think you do.

Your UploadVideo class should not know anything about the Form classes. It has no need to, and it's exactly your effort to do otherwise that has led you into this trap. Instead, what you want to do is "decouple" your UploadVideo class from the other classes that use it. This avoids these kinds of difficulties and at the same time helps your UploadVideo class remain maximally reusable (you can even use it where there's no Form class involved).

One right way to do this is to implement an event, which each Form class can subscribe to as appropriate:

class UploadVideo
{
    public event EventHandler<string> StatusTextChanged;

    void videosInsertRequest_ResponseReceived(Video video)
    {
        StatusTextChanged?.Invoke(this, $"Video id '{video.Id}' was successfully uploaded.");
    }
}

NOTE: your original text didn't really make sense. It used a format replacement specifier {0}, but didn't pass that to string.Format(), instead just appending the Id property value to the end of the string. I've changed the text expression to work as one would normally expect it to need to.

If you're not using the latest C# and don't have the "interpolated strings" feature, you can use string.Format("Video id '{0}' was successfully uploaded.", video.Id) instead.

Then a Form class can subscribe:

partial class Form1 : Form
{
    async void button1_Click(object sender, EventArgs e)
    {
        UploadVideo uv = new UploadVideo();

        uv.StatusTextChanged += (sender, text) =>
        {
            Invoke((MethodInvoker)(() => label1.Text = text));
        }

        await uv.Run(video);
    }
}

(You didn't offer enough code to know exactly what the expression core.prog_up is really supposed to be, so in the above I've just assumed an arbitrary label1 object that's used to display the text.)

Another alternative is to use the Progress<T> class:

class UploadVideo
{
    private readonly IProgress<string> _progress;

    public UploadVideo(IProgress<string> progress)
    {
        _progress = progress;
    }

    void videosInsertRequest_ResponseReceived(Video video)
    {
        _progress.Report($"Video id '{video.Id}' was successfully uploaded.");
    }
}

and…

partial class Form1 : Form
{
    async void button1_Click(object sender, EventArgs e)
    {
        Progress<string> progress = new Progress(s => label1.Text = text);

        await new UploadVideo(progress).Run(video);
    }
}

Note that when using the Progress<T> class, there's no need to add the call to Control.Invoke() to get back on the UI thread, because it handles that automatically for you.

The above shows passing the IProgress<T> instance to the UploadVideo constructor, but you could of course pass it to the Run() method instead. Either way will work. It just depends on where you need to value.

Yet another approach avoids callbacks altogether. Again, your original code example is pretty vague, so it's not clear whether this would apply in your case. But assuming the callback would be handled just before the Run() method returns, and assuming the video object passed to the ResponseReceived event handler is the same one your code passes to the Run() method, then you could just use the completion of the call to the Run() method as the indication to update the UI:

partial class Form1 : Form
{
    async void button1_Click(object sender, EventArgs e)
    {
        await new UploadVideo().Run(video);
        label1.Text = $"Video id '{video.Id}' was successfully uploaded.";
    }
}

This is a particularly compelling approach, because it removes even the string literal from the UploadVideo class, putting it into the class that actually is directly involved in interacting with the user (i.e. the only place where a string value really matters).

If the above is not enough for you to get back headed in the right direction, you'll need to improve your question by editing it so that it includes a good Minimal, Complete, and Verifiable code example showing exactly how your scenario works.

Upvotes: 1

OmerCD
OmerCD

Reputation: 46

You can use parameters to pass the reference of form.

    private Form _frm;
    public Form1(Form form)
    {
        _frm = form;
        InitializeComponent();
    }

And then you can simply call the form like this: Form1 frm = new Form1(this)

Upvotes: 0

Related Questions