0

I'm working on a client server application and this all works fine but since I programmed it so that when the client gets response from the server this is handled in a different class and thread. I would like to switch form1.Visible to be false from there (after login is successful). I tried many things like:

Trying to pass the form instance but was not able to get it there since the code happens as a reply from the server.

Making a function in Form1 that hides it and starts form2. this failed because I can't start that part from a different thread.

Finding the active form and hide that did not work either since I was not allowed to do that from a different thread.

How can i do something simple as hiding a form from a different class and thread?

the code:

public class ClientHandleData
{
    public static ByteBuffer playerBuffer;
    private delegate void Packet_(byte[] data);
    private static Dictionary<long, Packet_> packets = new Dictionary<long, Packet_>();
    private static long pLength;

    public static void InitMessages()
    {
        Console.WriteLine("Initializing network messages..");
        packets.Add((long)ServerPackets.SLoginCheckRes, Packet_Login_response);
    }

    public static void HandleData(byte[] data)
    {
        byte[] Buffer;
        Buffer = (byte[])data.Clone();

        if (playerBuffer == null) playerBuffer = new ByteBuffer();
        playerBuffer.WriteBytes(Buffer);

        if (playerBuffer.Count() == 0)
        {
            playerBuffer.Clear();
            return;
        }

        if (playerBuffer.Length() >= 8)
        {
            pLength = playerBuffer.ReadLong(false);
            if (pLength <= 0)
            {
                playerBuffer.Clear();
                return;
            }
        }

        if (playerBuffer.Length() >= 8)
        {
            pLength = playerBuffer.ReadLong(false);
            if (pLength <= 0)
            {
                playerBuffer.Clear();
                return;
            }
        }

        while (pLength > 0 & pLength <= playerBuffer.Length() - 8)
        {
            if (pLength <= playerBuffer.Length() - 8)
            {
                playerBuffer.ReadLong();
                data = playerBuffer.ReadBytes((int)pLength);
                HandleDataPackets(data);
            }
            pLength = 0;

            if (playerBuffer.Length() >= 8)
            {
                pLength = playerBuffer.ReadLong(false);
                if (pLength < 0)
                {
                    playerBuffer.Clear();
                    return;
                }
            }
        }
    }

    public static void HandleDataPackets(byte[] data)
    {
        long packetnum; ByteBuffer buffer; Packet_ packet;
        buffer = new ByteBuffer();
        buffer.WriteBytes(data);
        packetnum = buffer.ReadLong();
        buffer = null;

        if (packetnum == 0) return;

        if (packets.TryGetValue(packetnum, out packet))
        {
            packet.Invoke(data);
        }
    }

    private static void Packet_Login_response(byte[] data)
    {
        long packetnum; ByteBuffer buffer;
        buffer = new ByteBuffer(); ;
        buffer.WriteBytes(data);
        packetnum = buffer.ReadLong();
        string msg = buffer.ReadString();

        if(msg == "Wrong password")
        {
            System.Windows.Forms.MessageBox.Show("Wachtwoord is incorrect.");
        }
        else if(msg == "Wrong username")
        {
            System.Windows.Forms.MessageBox.Show("Gebruikersnaam is incorrect.");
        }
        else if(msg == "Login succes")
        {
            //here i want form1.visible to be false.
        }
    }
}

This script triggers when I click a login button on Form1. that starts loginclick on my networkmanager and that sends the data to the server. when the server replies the above code will run.

  • Possible duplicate of [find form instance from other class](https://stackoverflow.com/questions/17514251/find-form-instance-from-other-class) – mjwills Jun 11 '18 at 11:50
  • You have two issues here - the first is maintaining a reference to your `Form1` instance so you can use it in the above code somehow (and it's not clear how the code in your question relates to your forms), the second is interacting with your UI from a non-UI thread (which is what I think you're seeing in some of your errors) - see [this related question](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread) for how to do cross-thread UI updates. – James Thorpe Jun 11 '18 at 11:50

1 Answers1

1

Your architecture is wrong. You have to have a reference to form1 somewhere. Possibly it should be as a interface.

In worst case scenario, you could pass it to ClientHandleData like:

public class ClientHandleData
{
  public static Form LoginForm { get; set; }
}

And at the beginning of your app:

ClientHandleData.LoginForm = form1;

And then if you want to close form from a different thread you should use Invoke:

static void DoHideLoginForm()
{
    LoginForm.Visible = false;
}

static void HideLoginForm()
{
    if(LoginForm.InvokeRequired)
        LoginForm.Invoke(new Action(() => DoHideLoginForm()));
    else
        DoHideLoginForm();
}

And then you just call HideLoginForm when you want to hide it. This code first checks if you're in a different thread (InvokeRequired) and calls DoHideLoginForm from a proper thread.

But your architecture is bad and it can bring you to more and more problems. I think that this should be done more like this:

  • You should have a login controller class
  • You should have login interface and login form, like

    public interface ILoginView: IDisposable
    {
        string Login {get; set;}
        string Pass {get; set;}
    
        DialogResult ShowDialog();
    }
    

Then your loginForm (form1) should implement this interface:

class LoginForm: Form, ILoginView
{
    //here go Login and Pass properties
}

And your controller should be like this:

class LoginController
{
    ILoginView loginView;

    public LoginController(ILoginView loginView)
    {
        this.loginView = loginView;
    }

    public async Task<bool> Login()
    {
        loginView.ShowDialog();
        bool result = await DoYourLoginProcedure(loginView.Login, loginView.Pass);
        if(result)
        {
           loginView.Hide(); //or possibly Dispose
        }
    }
}

And then in your entry point:

LoginForm loginForm = new LoginForm();
LoginController contr = new LoginController(loginForm);
if(!contr.Login())
  //exit application

Of course this is not ideal solution, but this is the way you should go. Read about MVC, IoC (inversion of control) and DI (dependency injection)

Adam Jachocki
  • 1,897
  • 1
  • 12
  • 28
  • I thank you! I first implemented option 1 and rand perfectly but was not satisfied since you mentioned there is a better solution. Implemented the second suggestion and got it running as well. – Joris de Vos Jun 11 '18 at 13:09