Permalänk
Medlem

C# & XAML problem!

Hej!
I skolan så håller vi på att göra en chatclient och en chatserver. Man skall kunna skriva in en IPAdress och Port i Client GUI som skapas med hjälp av XAML kod. Därifrån så ansluter den till min server. När jag sedan vill mata in text genom min textbox i mitt GUI och skicka till servern så funkar det alldeles utmärkt. Därefter så skall servern skicka ut det clienten har skrivit till alla clienter som är anslutna till denna server. Min server skickar tillbaka det jag har skrivit in men jag kan inte få texten att visas i min textbox i mitt GUI.

Jag har ett MainWindow.xaml och ett MainWindow.xaml.cs som där sköter allt som har med GUI att göra. Jag har en klass som heter Client.cs denna klass sköter anslutningen till servern. När den har hittat en anslutning skickar den upp en messagebox som säger att jag har anslutit. Efter att de har anslutit till varandra så startar jag en tråd som loopar igenom en While sats och väntar på att servern skall skicka information till mig. När den hittar information så skickar den upp en messagebox med texten. Mitt problem är att jag vill att den informationen som skickas från servern till clienten skall visas i en textbox som finns i mitt MainWindow.

MainWindow xamldoc = new MainWindow(); xamldoc.textbox.text = temp;

När jag gör detta så får jag upp ett meddelande där det står att jag inte kan använda mig utav den för att en annan tråd äger den. "The calling thread cannot access this object because a different thread owns it". Så nu efter 3 dagars googlande efter att hitta en lösning som jag förstår mig på och en 3 timmars lektion utan att läraren har koll på vad som spökar, hoppas jag att ni underbara SweC-are kan hjälpa mig!

Är det något som är oklart så försöker jag gärna förklara på ett annat sätt!

MVH

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Avstängd

Du kan inte uppdatera UI från annan tråd än UItråden. Du löser detta genom att göra updateringen i dispatchern. Ännu bättre är att använda sig av ett MVVM ramverk som löser dessa saker åt dig bakom huven. Kolla på Caliburn Micro grymt bra MVVM ramvekr

Visa signatur
Permalänk
Medlem

Om CurrentThread != this...Dispatcher.Thread så this.Dispatcher.BeginInvoke ...

Visa signatur

ASUS ROG Strix X570-F, AMD Ryzen 9 5900X, ASUS GTX 1080 Ti Strix Gaming OC, 32GB G.Skill Trident Z Neo CL16 3600MHz, Arctic Freezer II 240, Seasonic Prime Titanium 850W, Phanteks Enthoo Evolv X, 2x Samsung 970 EVO Plus 1TB, Seagate Firecuda 2TB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS ROG Crosshair VI Hero, AMD Ryzen 9 3900X, ASUS RTX 2060 Dual OC, 16GB G.Skill Flare X CL14 3200 MHz @3200 MHz, Arctic Freezer 240, Seasonic Prime Titanium 850W, Phanteks Eclipse P400S Glass, Samsung 960 Pro 512GB, Samsung 850 Pro 512GB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS Maximus V GENE, Intel i7 3770K @ 4,6 GHz (1.190 V), EVGA GTX 670 FTW SLi, 8GB G.Skill TridentX CL10 2400 MHz, Samsung 850 Pro 512GB, Samsung 840 Pro 256GB, Corsair Hydro H100i, Corsair AX860i, Fractal Design Define R4W, ASUS PG278Q

Permalänk
Medlem
Skrivet av CyberVillain:

Du kan inte uppdatera UI från annan tråd än UItråden. Du löser detta genom att göra updateringen i dispatchern. Ännu bättre är att använda sig av ett MVVM ramverk som löser dessa saker åt dig bakom huven. Kolla på Caliburn Micro grymt bra MVVM ramvekr

Har inte en blekaste vad MVVM är men det skall kollas upp.

Skrivet av KeyPakt:

Om CurrentThread != this...Dispatcher.Thread så this.Dispatcher.BeginInvoke ...

KeyPakt Skulle du kunna beskriva vad den där kod snutten gör?

Tack för svaren!

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Avstängd

Ja kolla på det, riktigt trevligt sätt att skriva UI på

BeginInvoke tar ett delegat som kommer köras på UI tråden

Visa signatur
Permalänk
Medlem
Skrivet av CyberVillain:

Ja kolla på det, riktigt trevligt sätt att skriva UI på

BeginInvoke tar ett delegat som kommer köras på UI tråden

Använd gärna lätta ord. Har lite svårt att förstå mig på nya ord. Fattar dock inte hur jag installerar Caliburn. hehe

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Avstängd

this.Dispatcher.BeginInvoke(DispatcherPriority.Input, new ThreadStart(() => { //do something }));

Caliburn Micro hello world
http://buksbaum.us/2010/08/01/caliburn-micro-hello-world/

Visa signatur
Permalänk
Medlem

Det du vill göra är något som kallas Invoke. Jag föredrar BeginInvoke då denna variant inte kan åstadkomma deadlock. BeginInvoke hakar på GUI tråden vilken är den enda tråden som tillåter kommunikation med GUI. I BeginInvoke anropar man normalt en funktion med ett delegate som motsvarar det man vill göra. Det finns många varianter på hur man vill lösa detta. Kolla MSDN på WPF BeginInvoke så får du bästa möjliga info så du lär dig behärska detta. Det är i grunden ganska enkelt men också svårt om man behöver synkronisera flera trådar. Skriver på mobilen nu så jag kan inte skriva ett exempel på rak arm, dock imorgon bitti om du så önskar.

Visa signatur

ASUS ROG Strix X570-F, AMD Ryzen 9 5900X, ASUS GTX 1080 Ti Strix Gaming OC, 32GB G.Skill Trident Z Neo CL16 3600MHz, Arctic Freezer II 240, Seasonic Prime Titanium 850W, Phanteks Enthoo Evolv X, 2x Samsung 970 EVO Plus 1TB, Seagate Firecuda 2TB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS ROG Crosshair VI Hero, AMD Ryzen 9 3900X, ASUS RTX 2060 Dual OC, 16GB G.Skill Flare X CL14 3200 MHz @3200 MHz, Arctic Freezer 240, Seasonic Prime Titanium 850W, Phanteks Eclipse P400S Glass, Samsung 960 Pro 512GB, Samsung 850 Pro 512GB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS Maximus V GENE, Intel i7 3770K @ 4,6 GHz (1.190 V), EVGA GTX 670 FTW SLi, 8GB G.Skill TridentX CL10 2400 MHz, Samsung 850 Pro 512GB, Samsung 840 Pro 256GB, Corsair Hydro H100i, Corsair AX860i, Fractal Design Define R4W, ASUS PG278Q

Permalänk
Medlem
Skrivet av KeyPakt:

Det du vill göra är något som kallas Invoke. Jag föredrar BeginInvoke då denna variant inte kan åstadkomma deadlock. BeginInvoke hakar på GUI tråden vilken är den enda tråden som tillåter kommunikation med GUI. I BeginInvoke anropar man normalt en funktion med ett delegate som motsvarar det man vill göra. Det finns många varianter på hur man vill lösa detta. Kolla MSDN på WPF BeginInvoke så får du bästa möjliga info så du lär dig behärska detta. Det är i grunden ganska enkelt men också svårt om man behöver synkronisera flera trådar. Skriver på mobilen nu så jag kan inte skriva ett exempel på rak arm, dock imorgon bitti om du så önskar.

Det skulle jag verkligen uppskatta! Jag får kolla lite mer på allt detta imorgon. Tack för all hjälp än så länge!

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Avstängd

Förutom Hello world länken jag länka till ovan kan du kolla in koden till ett av mina Open Source projekt, den använder sig av Caliburn Micro

https://github.com/AndersMalmgren/FreePIE

Visa signatur
Permalänk
Medlem

Nedan finner du 3 olika kodexempel. Det är lite olika när man använder vilket exempel men i detta fall göra samtliga exakt samma sak på i princip samma sätt. Om man har ett fall där man vill att flera GUI komponenter ska uppdateras och man vill säkerställa att det gör det i sekvens så är exempel 2 och 3 bra.

/************************ KODEXEMPEL 1 ************************/
// Detta exempel är mer allmänt vad gäller hur man jackar på GUI tråden för att visa händelser.
// Kod du placerar utanför metoden men i klassen eller utanförklassen för publik åtkomst (private blir då public)
#region Delegates
// Eftersom xamldoc.textbox.Text är en string behöver vi ett string delegate för att uföra BeginInvoke
// Du kan döpa delegate till vad du vill men jag brukar döpa dem så det blir enkelt att hitta rätt delegate med intellisense
private delegate void StringParamDelegate(string pString); // I C++ kallas detta funktionspekare
#endregion

// Kod du placerar i din metod
// Kontroll om körande tråd är samma som vår GUI-tråd
if (this.Dispatcher.Thread == System.Threading.Thread.CurrentThread)
{
xamldoc.textbox.Text = "meddelande du vill visa";
}
else
{
// Körande tråd skiljer sig från GUI-tråd
this.Dispatcher.BeginInvoke(new StringParamDelegate(delegate(string dText) { xamldoc.textbox.Text = dText; }),
new object[] { "meddelande du vill visa" });
}

/************************ KODEXEMPEL 2 ************************/
#region Delegates
private delegate void StringParamDelegate(string pString);
#endregion

// Kod du placerar i din metod
// Detta exempel visar hur du anropar en metod via BeginInvoke.
if (this.Dispatcher.Thread == System.Threading.Thread.CurrentThread)
{
UpdateTextBox("meddelande du vill visa");
}
else
{
// Körande tråd skiljer sig från GUI-tråd
this.Dispatcher.BeginInvoke(new StringParamDelegate(UpdateTextBox), "meddelande du vill visa");
}

private void UpdateTextBox(string pText)
{
try
{
xamldoc.textbox.Text = pText;
}
catch (Exception ex)
{
// Logga fel
}
}

/************************ KODEXEMPEL 3 ************************/
#region Delegates
private delegate void StringBoolParamDelegate(string pString, bool pBool);
#endregion

// Kod du placerar i din metod
UpdateTextBox("meddelande du vill visa");

// Anropa aldrig denna metod direkt med pInvoked = true utan låt den default vara false
private void UpdateTextBox(string pText, bool pInvoked = false)
{
try
{
if ((!pInvoked) && (this.Dispatcher.Thread != System.Threading.Thread.CurrentThread))
{
this.Dispatcher.BeginInvoke(new StringBoolParamDelegate(UpdateTextBox), pText, true);
}
else
{
xamldoc.textbox.Text = pText;
}
}
catch (Exception ex)
{
// Logga fel
}
}

NOTERA att det som i WPF genomförs med if (this.Dispatcher.Thread == System.Threading.Thread.CurrentThread) i WinForms heter if (this.InvokeRequired)

Visa signatur

ASUS ROG Strix X570-F, AMD Ryzen 9 5900X, ASUS GTX 1080 Ti Strix Gaming OC, 32GB G.Skill Trident Z Neo CL16 3600MHz, Arctic Freezer II 240, Seasonic Prime Titanium 850W, Phanteks Enthoo Evolv X, 2x Samsung 970 EVO Plus 1TB, Seagate Firecuda 2TB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS ROG Crosshair VI Hero, AMD Ryzen 9 3900X, ASUS RTX 2060 Dual OC, 16GB G.Skill Flare X CL14 3200 MHz @3200 MHz, Arctic Freezer 240, Seasonic Prime Titanium 850W, Phanteks Eclipse P400S Glass, Samsung 960 Pro 512GB, Samsung 850 Pro 512GB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS Maximus V GENE, Intel i7 3770K @ 4,6 GHz (1.190 V), EVGA GTX 670 FTW SLi, 8GB G.Skill TridentX CL10 2400 MHz, Samsung 850 Pro 512GB, Samsung 840 Pro 256GB, Corsair Hydro H100i, Corsair AX860i, Fractal Design Define R4W, ASUS PG278Q

Permalänk
Medlem
Skrivet av KeyPakt:

Nedan finner du 3 olika kodexempel. Det är lite olika när man använder vilket exempel men i detta fall göra samtliga exakt samma sak på i princip samma sätt. Om man har ett fall där man vill att flera GUI komponenter ska uppdateras och man vill säkerställa att det gör det i sekvens så är exempel 2 och 3 bra.

/************************ KODEXEMPEL 1 ************************/
// Detta exempel är mer allmänt vad gäller hur man jackar på GUI tråden för att visa händelser.
// Kod du placerar utanför metoden men i klassen eller utanförklassen för publik åtkomst (private blir då public)
#region Delegates
// Eftersom xamldoc.textbox.Text är en string behöver vi ett string delegate för att uföra BeginInvoke
// Du kan döpa delegate till vad du vill men jag brukar döpa dem så det blir enkelt att hitta rätt delegate med intellisense
private delegate void StringParamDelegate(string pString); // I C++ kallas detta funktionspekare
#endregion

// Kod du placerar i din metod
// Kontroll om körande tråd är samma som vår GUI-tråd
if (this.Dispatcher.Thread == System.Threading.Thread.CurrentThread)
{
xamldoc.textbox.Text = "meddelande du vill visa";
}
else
{
// Körande tråd skiljer sig från GUI-tråd
this.Dispatcher.BeginInvoke(new StringParamDelegate(delegate(string dText) { xamldoc.textbox.Text = dText; }),
new object[] { "meddelande du vill visa" });
}

/************************ KODEXEMPEL 2 ************************/
#region Delegates
private delegate void StringParamDelegate(string pString);
#endregion

// Kod du placerar i din metod
// Detta exempel visar hur du anropar en metod via BeginInvoke.
if (this.Dispatcher.Thread == System.Threading.Thread.CurrentThread)
{
UpdateTextBox("meddelande du vill visa");
}
else
{
// Körande tråd skiljer sig från GUI-tråd
this.Dispatcher.BeginInvoke(new StringParamDelegate(UpdateTextBox), "meddelande du vill visa");
}

private void UpdateTextBox(string pText)
{
try
{
xamldoc.textbox.Text = pText;
}
catch (Exception ex)
{
// Logga fel
}
}

/************************ KODEXEMPEL 3 ************************/
#region Delegates
private delegate void StringBoolParamDelegate(string pString, bool pBool);
#endregion

// Kod du placerar i din metod
UpdateTextBox("meddelande du vill visa");

// Anropa aldrig denna metod direkt med pInvoked = true utan låt den default vara false
private void UpdateTextBox(string pText, bool pInvoked = false)
{
try
{
if ((!pInvoked) && (this.Dispatcher.Thread != System.Threading.Thread.CurrentThread))
{
this.Dispatcher.BeginInvoke(new StringBoolParamDelegate(UpdateTextBox), pText, true);
}
else
{
xamldoc.textbox.Text = pText;
}
}
catch (Exception ex)
{
// Logga fel
}
}

NOTERA att det som i WPF genomförs med if (this.Dispatcher.Thread == System.Threading.Thread.CurrentThread) i WinForms heter if (this.InvokeRequired)

Dold text

När jag implementerar exempel 2 så får jag fel meddelande att "Error 1 'TCP_client_WPF.Client' does not contain a definition for 'Dispatcher' and no extension method 'Dispatcher' accepting a first argument of type 'TCP_client_WPF.Client' could be found (are you missing a using directive or an assembly reference?) D:\Users\xxxx\SkyDrive\Programmering\TCP client WPF\TCP client WPF\Client.cs 62 29 TCP client WPF"

Jag har satt

using System.Windows; using System.Windows.Threading; using System.Windows.Threading.Dispatcher;

Och det du har skrivit i ditt andra exempel verkar vara det som jag är ute efter. Jag har i princip kopierat det du skrivit för att se om den fungerar.

Tack för att du tog dig tiden att skriva alla de där exemplen!

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Medlem

En förutsättning för att använda en dispatcher är att det finns ett Window. Om ditt fönster är dynamiskt skapat som i tex den kod du skrivit ovan så bör du istället för this.Dispatcher skriva xamldoc.Dispatcher

Visa signatur

ASUS ROG Strix X570-F, AMD Ryzen 9 5900X, ASUS GTX 1080 Ti Strix Gaming OC, 32GB G.Skill Trident Z Neo CL16 3600MHz, Arctic Freezer II 240, Seasonic Prime Titanium 850W, Phanteks Enthoo Evolv X, 2x Samsung 970 EVO Plus 1TB, Seagate Firecuda 2TB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS ROG Crosshair VI Hero, AMD Ryzen 9 3900X, ASUS RTX 2060 Dual OC, 16GB G.Skill Flare X CL14 3200 MHz @3200 MHz, Arctic Freezer 240, Seasonic Prime Titanium 850W, Phanteks Eclipse P400S Glass, Samsung 960 Pro 512GB, Samsung 850 Pro 512GB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS Maximus V GENE, Intel i7 3770K @ 4,6 GHz (1.190 V), EVGA GTX 670 FTW SLi, 8GB G.Skill TridentX CL10 2400 MHz, Samsung 850 Pro 512GB, Samsung 840 Pro 256GB, Corsair Hydro H100i, Corsair AX860i, Fractal Design Define R4W, ASUS PG278Q

Permalänk
Medlem
Skrivet av KeyPakt:

En förutsättning för att använda en dispatcher är att det finns ett Window. Om ditt fönster är dynamiskt skapat som i tex den kod du skrivit ovan så bör du istället för this.Dispatcher skriva xamldoc.Dispatcher

Dispatcher fungerade när jag satte xamldoc istället för this. Men den kör inte "UpdateTextBox" metoden. Skickar med kodsnutten som den körs i.

while (tcpClient.Connected) { try { temp = sr.ReadLine(); if (xamldoc.Dispatcher.Thread == System.Threading.Thread.CurrentThread) { UpdateTextBox(temp); } else { xamldoc.Dispatcher.BeginInvoke(new StringParamDelegate(UpdateTextBox), temp); } } catch (Exception e) { MessageBox.Show(e.Message); break; } Thread.Sleep(5); }

Gjorde en breakpoint på ifsatsen och mitt "if" satement är inte sant så den hoppar vidare och kör else delen men den uppdaterar inte textboxen. Utan bara fortsätter i loopen.

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Medlem

Du bör kanske lägga till att du inte vill att den ska skriva till din TextBox om din temp = "", typ if (!string.IsNullOrEmpty(temp.Trim()))

Vad använder du för delimiter vid sändningen av meddelanden? Om du använder null så bör du filtrera bort null från inkommande meddelande för att inte paja det för GUI.

Visa signatur

ASUS ROG Strix X570-F, AMD Ryzen 9 5900X, ASUS GTX 1080 Ti Strix Gaming OC, 32GB G.Skill Trident Z Neo CL16 3600MHz, Arctic Freezer II 240, Seasonic Prime Titanium 850W, Phanteks Enthoo Evolv X, 2x Samsung 970 EVO Plus 1TB, Seagate Firecuda 2TB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS ROG Crosshair VI Hero, AMD Ryzen 9 3900X, ASUS RTX 2060 Dual OC, 16GB G.Skill Flare X CL14 3200 MHz @3200 MHz, Arctic Freezer 240, Seasonic Prime Titanium 850W, Phanteks Eclipse P400S Glass, Samsung 960 Pro 512GB, Samsung 850 Pro 512GB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS Maximus V GENE, Intel i7 3770K @ 4,6 GHz (1.190 V), EVGA GTX 670 FTW SLi, 8GB G.Skill TridentX CL10 2400 MHz, Samsung 850 Pro 512GB, Samsung 840 Pro 256GB, Corsair Hydro H100i, Corsair AX860i, Fractal Design Define R4W, ASUS PG278Q

Permalänk
Medlem
Skrivet av KeyPakt:

Du bör kanske lägga till att du inte vill att den ska skriva till din TextBox om din temp = "", typ if (!string.IsNullOrEmpty(temp.Trim()))

Vad använder du för delimiter vid sändningen av meddelanden? Om du använder null så bör du filtrera bort null från inkommande meddelande för att inte paja det för GUI.

Jag använder mig utav streamwriter och streamreader för att skicka och ta emot meddelanden.

När jag testade att lägga in UpdateTextBox under "xamldoc.Dispatcher.BeginInvoke(new StringParamDelegate(UpdateTextBox), temp);" Så gnällde den på att en annan tråd redan äger den. Måste väll ha nått att göra med att jag måste låta min nuvarande tråd ta över min allbox från GUI tråden?

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Medlem

Det är lite svårt att med så lite info säga vad som felar. Men, kan det vara så att en annan tråd i sig äger xamldoc? Om du kunde ge mer av din kod kan jag säga vad som felar, denna typ av programmering gör jag dagligen. Eftersom jag inte vet var din huvudkod körs ifrån blir det svårt att säga vilken tråd som bråkar. Om det är MainWindow som initierar client så borde det bästa vara om du i client skapar ett Event som MainWindow jackar på. När ett meddelande kommer så kickar du eventet vilket släppas upp av MainWindow och det är här du sedan gör BeginInvoke och då ska det vara this.Dispatcher.BeginInvoke eftersom this refererar till MainWindow.

Visa signatur

ASUS ROG Strix X570-F, AMD Ryzen 9 5900X, ASUS GTX 1080 Ti Strix Gaming OC, 32GB G.Skill Trident Z Neo CL16 3600MHz, Arctic Freezer II 240, Seasonic Prime Titanium 850W, Phanteks Enthoo Evolv X, 2x Samsung 970 EVO Plus 1TB, Seagate Firecuda 2TB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS ROG Crosshair VI Hero, AMD Ryzen 9 3900X, ASUS RTX 2060 Dual OC, 16GB G.Skill Flare X CL14 3200 MHz @3200 MHz, Arctic Freezer 240, Seasonic Prime Titanium 850W, Phanteks Eclipse P400S Glass, Samsung 960 Pro 512GB, Samsung 850 Pro 512GB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS Maximus V GENE, Intel i7 3770K @ 4,6 GHz (1.190 V), EVGA GTX 670 FTW SLi, 8GB G.Skill TridentX CL10 2400 MHz, Samsung 850 Pro 512GB, Samsung 840 Pro 256GB, Corsair Hydro H100i, Corsair AX860i, Fractal Design Define R4W, ASUS PG278Q

Permalänk
Medlem
Skrivet av KeyPakt:

Det är lite svårt att med så lite info säga vad som felar. Men, kan det vara så att en annan tråd i sig äger xamldoc? Om du kunde ge mer av din kod kan jag säga vad som felar, denna typ av programmering gör jag dagligen. Eftersom jag inte vet var din huvudkod körs ifrån blir det svårt att säga vilken tråd som bråkar. Om det är MainWindow som initierar client så borde det bästa vara om du i client skapar ett Event som MainWindow jackar på. När ett meddelande kommer så kickar du eventet vilket släppas upp av MainWindow och det är här du sedan gör BeginInvoke och då ska det vara this.Dispatcher.BeginInvoke eftersom this refererar till MainWindow.

Här får du all kod

MainWindows.xaml.cs Client client; public MainWindow() { InitializeComponent(); sendText.IsEnabled = false; clientBox.IsEnabled = false; } private void sendText_Click_1(object sender, RoutedEventArgs e) { string temp = clientBox.Text; client.sendInfo(temp); clientBox.Text = ""; } private void connectButton_Click_1(object sender, RoutedEventArgs e) { //int port = int.Parse(portBox.Text); client = new Client("192.168.1.5", 1337); connectButton.Visibility = Visibility.Hidden; disconnectButton.Visibility = Visibility.Visible; connectInfo.Content = "Connected"; clientBox.IsEnabled = true; sendText.IsEnabled = true; } private void clientBox_KeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { sendText_Click_1(this, new RoutedEventArgs()); } } private void disconnectButton_Click_1(object sender, RoutedEventArgs e) { connectInfo.Content = "Disconnected"; sendText.IsEnabled = false; clientBox.IsEnabled = false; disconnectButton.Visibility = Visibility.Hidden; connectButton.Visibility = Visibility.Visible; MessageBox.Show("Du är nu ej ansluten!"); } Client.cs class Client { private TcpClient tcpClient; public NetworkStream clientStream; //private Thread conThread; private Thread getThread; private StreamWriter sw; private StreamReader sr; private MainWindow xamldoc = new MainWindow(); private delegate void StringParamDelegate(string pString); public Client(string ip, int port) { tcpClient = new TcpClient(); try { tcpClient.Connect(ip, port); //Ansluter till min server if (tcpClient.Connected) { clientStream = tcpClient.GetStream(); //Skapar en nätverksström genom min tcpclient MessageBox.Show("You are connected!"); // Visar att jag har anslutits } else { MessageBox.Show("Your connection failed! Closing program"); Environment.Exit(0); } } catch (Exception e) { MessageBox.Show(e.Message); } getThread = new Thread(new ThreadStart(getInfo)); //Skapar en tråd åt min getInfo getThread.Start(); //Startar tråden getInfo som körs för att hämta data från servern. } public void getInfo() { sr = new StreamReader(clientStream); //Skapar en streamreader som läser data som clientstreamen skickar. string temp; while (tcpClient.Connected) { try { temp = sr.ReadLine(); if (!string.IsNullOrEmpty(temp.Trim())) { if (xamldoc.Dispatcher.Thread == System.Threading.Thread.CurrentThread) { UpdateTextBox(temp); } else { xamldoc.Dispatcher.BeginInvoke(new StringParamDelegate(UpdateTextBox), temp); } } } catch (Exception e) { MessageBox.Show(e.Message); break; } Thread.Sleep(5); } clientStream.Close(); tcpClient.Close(); } public void sendInfo(string temp){ sw = new StreamWriter(clientStream); try { sw.WriteLine("Anton: {0}", temp); sw.Flush(); } catch (Exception e) { MessageBox.Show(e.Message); } } private void UpdateTextBox(string pString) { try { xamldoc.allBox.Text = pString; } catch (Exception e){ MessageBox.Show(e.Message); } }

Dold text

Nu har du nått att kolla igenom. Säg bara till om det är nått mer som du undrar över!

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Medlem

Då var det som jag ungefär antog och då ska den lösning som jag föreslog fungera.

En råd på vägen är att aldrig försöka kommunicera tillbaka till ett fönster som något kom ifrån genom att skapa en ny instans av det fönster. Om du ska kommunicera tillbaka den fula vägen så bör det går genom att du när du initierar Client även skickar med en referens av fönstret enligt Client(string ip, int port, ref Window parentWindow), sedan sparar man in Konstruktorn till Client parentWindow till en static variabel. Genom att göra på detta sätt så låser man ju sin klient till att endast kunna kommunicera med WPF Window och det är inte bra men det funkar givetvis.
------------------------------------------------------------------------------------------------------------
En bättre lösning är att göra följande.
------------------------------------------------------------------------------------------------------------
* Flytta metoden UpdateTextBox till MainWindow och byt ut xamldoc till this.
* I Client.cs lägger du till följande Event som binder sig till delegatet StringParamDelegate.

public event StringParamDelegate OnNewMessage;

I Client när du mottagit ett meddelande som ska sändas vidare så gör du följande:
// Är det någon som lyssnar på mitt event
if (OnNewMessage != null)
{
// Sända meddelandet till den som lyssnar. Observera att om ditt är ett public static event så kan flera olika lyssnare få meddelandet samtidigt eftersom static betyder 1 instans totalt.
OnNewMessage("ditt meddelande");
}
else
{
// Vad göra
}

Nu kan du när du initierar din klient i MainWindow och skriver client.OnNewMessage och sedan tabbar du dig igenom så Visual Studio skapar funktionen åt dig ...vilket bör bli:
client.OnNewMessage += Client.StringParamDelegate(client_OnNewMessage);
// Med metoden
private void client_OnNewMessage(string pString)
{
if (xamldoc.Dispatcher.Thread == System.Threading.Thread.CurrentThread)
{
UpdateTextBox(pString);
}
else
{
this.Dispatcher.BeginInvoke(new StringParamDelegate(UpdateTextBox), pString);

}
}

En sak som du ska veta som är lite klurig när det gäller MessageBox är att meddelande boxen poppar för det fönster som för tillfälligt är aktivt dvs om du har en applikation med 3 fönster och poppar MessageBox i koden för fönster 1 medan fönster 3 är aktivt så kommer fönster 3 att äga MessageBoxen.

Visa signatur

ASUS ROG Strix X570-F, AMD Ryzen 9 5900X, ASUS GTX 1080 Ti Strix Gaming OC, 32GB G.Skill Trident Z Neo CL16 3600MHz, Arctic Freezer II 240, Seasonic Prime Titanium 850W, Phanteks Enthoo Evolv X, 2x Samsung 970 EVO Plus 1TB, Seagate Firecuda 2TB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS ROG Crosshair VI Hero, AMD Ryzen 9 3900X, ASUS RTX 2060 Dual OC, 16GB G.Skill Flare X CL14 3200 MHz @3200 MHz, Arctic Freezer 240, Seasonic Prime Titanium 850W, Phanteks Eclipse P400S Glass, Samsung 960 Pro 512GB, Samsung 850 Pro 512GB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS Maximus V GENE, Intel i7 3770K @ 4,6 GHz (1.190 V), EVGA GTX 670 FTW SLi, 8GB G.Skill TridentX CL10 2400 MHz, Samsung 850 Pro 512GB, Samsung 840 Pro 256GB, Corsair Hydro H100i, Corsair AX860i, Fractal Design Define R4W, ASUS PG278Q

Permalänk
Medlem
Skrivet av KeyPakt:

Då var det som jag ungefär antog och då ska den lösning som jag föreslog fungera.

En råd på vägen är att aldrig försöka kommunicera tillbaka till ett fönster som något kom ifrån genom att skapa en ny instans av det fönster. Om du ska kommunicera tillbaka den fula vägen så bör det går genom att du när du initierar Client även skickar med en referens av fönstret enligt Client(string ip, int port, ref Window parentWindow), sedan sparar man in Konstruktorn till Client parentWindow till en static variabel. Genom att göra på detta sätt så låser man ju sin klient till att endast kunna kommunicera med WPF Window och det är inte bra men det funkar givetvis.
------------------------------------------------------------------------------------------------------------
En bättre lösning är att göra följande.
------------------------------------------------------------------------------------------------------------
* Flytta metoden UpdateTextBox till MainWindow och byt ut xamldoc till this.
* I Client.cs lägger du till följande Event som binder sig till delegatet StringParamDelegate.

public event StringParamDelegate OnNewMessage;

I Client när du mottagit ett meddelande som ska sändas vidare så gör du följande:
// Är det någon som lyssnar på mitt event
if (OnNewMessage != null)
{
// Sända meddelandet till den som lyssnar. Observera att om ditt är ett public static event så kan flera olika lyssnare få meddelandet samtidigt eftersom static betyder 1 instans totalt.
OnNewMessage("ditt meddelande");
}
else
{
// Vad göra
}

Nu kan du när du initierar din klient i MainWindow och skriver client.OnNewMessage och sedan tabbar du dig igenom så Visual Studio skapar funktionen åt dig ...vilket bör bli:
client.OnNewMessage += Client.StringParamDelegate(client_OnNewMessage);
// Med metoden
private void client_OnNewMessage(string pString)
{
if (xamldoc.Dispatcher.Thread == System.Threading.Thread.CurrentThread)
{
UpdateTextBox(pString);
}
else
{
this.Dispatcher.BeginInvoke(new StringParamDelegate(UpdateTextBox), pString);

}
}

En sak som du ska veta som är lite klurig när det gäller MessageBox är att meddelande boxen poppar för det fönster som för tillfälligt är aktivt dvs om du har en applikation med 3 fönster och poppar MessageBox i koden för fönster 1 medan fönster 3 är aktivt så kommer fönster 3 att äga MessageBoxen.

Dold text

Tack! Nu funkar det. Hur ska jag göra om jag vill att min label ska ändra text beroende på om jag har anslutit eller inte? Måste jag krångla till det på detta viset också?

Visa signatur

Crosshair IV | 1055T @ 3.2 GHz | 4 GB Corsair dominator | Corsair H50 | Fractal Design R3 | 2x PowerColor 6950 2GB | Corsair HX 750W

Permalänk
Medlem

Det beror lite på hur man vill känna till om en uppkoppling är aktiv eller inte. I ditt fall kopplar den upp när du skapar en instans av Client och om det räcker med att veta status efter att instansen gjorts så är det enklast om du i Client lägger till en publik property, se nedan.

#region Properties
public bool Connected { get { return (((tcpClient != null) && (tcpClient.Connected)) ? true : false); } }
#endregion

sedan kan du i MainWindow titta på client.Connected efter raden client = new Client("192.168.1.5", 1337);

Visa signatur

ASUS ROG Strix X570-F, AMD Ryzen 9 5900X, ASUS GTX 1080 Ti Strix Gaming OC, 32GB G.Skill Trident Z Neo CL16 3600MHz, Arctic Freezer II 240, Seasonic Prime Titanium 850W, Phanteks Enthoo Evolv X, 2x Samsung 970 EVO Plus 1TB, Seagate Firecuda 2TB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS ROG Crosshair VI Hero, AMD Ryzen 9 3900X, ASUS RTX 2060 Dual OC, 16GB G.Skill Flare X CL14 3200 MHz @3200 MHz, Arctic Freezer 240, Seasonic Prime Titanium 850W, Phanteks Eclipse P400S Glass, Samsung 960 Pro 512GB, Samsung 850 Pro 512GB, Seagate Ironwolf 4TB, ASUS PG278Q

ASUS Maximus V GENE, Intel i7 3770K @ 4,6 GHz (1.190 V), EVGA GTX 670 FTW SLi, 8GB G.Skill TridentX CL10 2400 MHz, Samsung 850 Pro 512GB, Samsung 840 Pro 256GB, Corsair Hydro H100i, Corsair AX860i, Fractal Design Define R4W, ASUS PG278Q