Lekce 5 - Přístup k okýnkům, Mutex, fce messengeru - ISIM
Global.cs
Tady to bude trošku rozsáhlejší, budou zde vnořené třídy, proměnné a tak dál. Možná doporučuji si to pak roztřídit do více souborů.
Enum dostupnosti může být následující. Přiřazením hodnoty čísla, pak lze jednoduše přetypovat na integer a tuto hodnotu získat. Zde to využívám jako určení priority při řazení.
public enum Availability { Online = 1, Away = 2, DND = 3, Invisible = 4, Blocked = 5, Offline = 6 };
První vnořená třída obsahující instance okýnek:
public static class OpenForms { public static MainForm mainForm; public static ChatForm chatForm; }
Další vnořená třída je pro Mutex - odesílání signálu. Popsáno zde: http://www.itnetwork.cz/…gle-instance
public static class SingleInstance { public const int HWND_BROADCAST = 0xffff; public static int WM_SHOWME; [DllImport("user32")] public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); [DllImport("user32")] public static extern int RegisterWindowMessage(string message); static SingleInstance() { WM_SHOWME = RegisterWindowMessage("ISIM_SHOW_FORM"); } public static void ShowForm() { SingleInstance.PostMessage( (IntPtr)SingleInstance.HWND_BROADCAST, SingleInstance.WM_SHOWME, IntPtr.Zero, IntPtr.Zero); } }
Teď bude velká vnořená třída s funkcemi messengeru.
public static class ISIMFunctions {
Invoke
-> zajímavá a užitečná metůdka, využívající
se při multi threadingu -> zásahu do jiného threadu (okýnka) z jinýho
threadu (okýnka). Přivlastním si ho a pracuji s ním. Zde je proto, abych
zajistil spuštění okýnka chatu na hlavním vlákně. Jinak by se mohlo
stát, že se metoda OpenChatForm zavolá z jiného threadu a okýnko by jen
probliklo - jakmile by thread skončil, okýnko by se zavřelo. Existuje také
asynchronní metoda BeginInvoke
- což zjednodušeně znamená, že
se požadavek pošle a jede se dál, on se časem vykoná na dalším vlákně,
takže se na nic nečeká.
/// <summary> /// Přidání tabu případné otevření chat okýnka /// </summary> /// <param name="contact"></param> /// <param name="selectTab"></param> public static void OpenChatForm(ISIMContact contact, bool selectTab = false) { if (Global.OpenForms.chatForm == null) { // Abych zajistil otevření na hlavním threadu Global.OpenForms.mainForm.Invoke(new MethodInvoker(() => { Global.OpenForms.chatForm = new ChatForm(contact); Global.OpenForms.chatForm.Show(); Global.OpenForms.chatForm.Activate(); })); } else { Global.OpenForms.chatForm.BeginInvoke(new MethodInvoker(() => { Global.OpenForms.chatForm.chatTabs.AddTab(contact, selectTab); })); } }
()
-> je tzv. lambda znak, používá se pro definici
jednořádkové metody, kterou lze použít vnořeně v metodě. Používá se
místo delegátu delegate()
kdůli kratšímu zápisu.
object as MůjObject
je způsob takového C# like přetypování
objektu. Vlastně se zeptá, zda je objekt toho typu ->
object is MůjObject ? (MůjObject)object : (MůjObject)null
.
Ovšem nelze to použít například ve switch
, tuším že u enum
a nějakých dalších případech.
/// <summary> /// Přidání zprávy do chat okýnka dané konverzace a případné otevření okýnka /// </summary> /// <param name="contact"></param> /// <param name="message"></param> /// <param name="time"></param> public static void AddChatMessage(ISIMContact contact, string message, DateTime time, string from) { OpenChatForm(contact); Global.OpenForms.chatForm.BeginInvoke(new MethodInvoker(() => { (Global.OpenForms.chatForm.chatTabs.TabPages[contact.id] as ChatTabPage).AddChatMessage(contact, message, time, from); if (Global.OpenForms.chatForm.chatTabs.TabPages[contact.id] != Global.OpenForms.chatForm.chatTabs.SelectedTab) { contact.unreadedMessage = true; Global.OpenForms.chatForm.Invoke(new MethodInvoker(() => { Global.OpenForms.chatForm.Refresh(); })); Global.OpenForms.mainForm.Invoke(new MethodInvoker(() => { Global.OpenForms.mainForm.Refresh(); })); } })); }
Pro odesílání zpráv se udělá jedna univerzální metoda, která rozpozná, ze kterého protokolu jde požadavek a podle toho se zařídí.
/// <summary> /// Odeslání zprávy kontaktu /// </summary> /// <param name="contact"></param> /// <param name="conversation"></param> /// <param name="message"></param> public static void SendChatMessage(ISIMContact contact, object conversation, string message) { if (conversation is SkypeContact.ContactObject) { try { (conversation as SkypeContact.ContactObject).conversation.PostText(message); } catch (Exception ex) { AddChatMessage(contact, "Nepodařilo se odeslat zprávu", DateTime.Now, "ISIM"); Console.WriteLine("msg send failed" + ex.Message); } } // nejprve se zkontroluje dle nějakého identifikátoru které knihovně daný objekt náleží if (conversation is XmppContact.ContactObject) { Global.OpenForms.mainForm.Invoke(new MethodInvoker(() => { // nastala chyba - odpojeno od serveru if (((conversation as XmppContact.ContactObject).xmppClient == null) || ((conversation as XmppContact.ContactObject).xmppClient.XmppConnectionState != agsXMPP.XmppConnectionState.SessionStarted)) Global.ISIMFunctions.AddChatMessage(contact, "Chyba při odesílání zprávy", DateTime.Now, "ISIM"); // lze odeslat jen asi 1000 znaků dlouho zprávu, pokud je delší splitne se na části foreach (string msg in message.SplitString(900)) (conversation as XmppContact.ContactObject).xmppClient.Send(new agsXMPP.protocol.client.Message(new agsXMPP.Jid(contact.id), MessageType.chat, msg)); })); } if (conversation is IcqContact.ContactObject) { // blbě kódování v ICQ message = message.RemoveDiacritics(); (conversation as IcqContact.ContactObject).session.Messaging.Send(new IcqSharp.Base.Message(new IcqSharp.Base.Contact(contact.id), IcqSharp.Base.MessageType.Outgoing, message)); } Global.ISIMFunctions.AddChatMessage(contact, message, DateTime.Now, myNick); }
Při přidání kontaktu nebo editaci jeho dostupnosti se zavolá metoda
Sort
sesortí kontakty dle našeho komparátoru a i překreslí
ListView
což mě celkem štvalo, bže to problikává. Proto je
dobré používat BeginUpdate()
a EndUpdate()
pokud
děláme více operací, jako například při hromadném přidávání
kontaktů, zablokuje to překreslování udělám si co chci a pak to
překreslím naráz.
public static void AddContact(ISIMContact contact) { Global.OpenForms.mainForm.BeginInvoke(new MethodInvoker(() => { if (!Global.OpenForms.mainForm.contactListView.Items.ContainsKey(contact.id)) { Global.OpenForms.mainForm.contactListView.Items.Add(new ContactListViewItem(contact)); if (contact.availability == Availability.Offline) Global.OpenForms.mainForm.contactListView.Items[contact.id].Group = Global.OpenForms.mainForm.contactListView.Groups["offlineListViewGroup"]; else Global.OpenForms.mainForm.contactListView.Items[contact.id].Group = Global.OpenForms.mainForm.contactListView.Groups["onlineListViewGroup"]; Global.OpenForms.mainForm.contactListView.Sort(); } })); } public static void EditContactAvailability(string id, Availability availability) { Global.OpenForms.mainForm.BeginInvoke(new MethodInvoker(() => { if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(id)) { (Global.OpenForms.mainForm.contactListView.Items[id] as ContactListViewItem).contact.availability = availability; if (availability == Availability.Offline) Global.OpenForms.mainForm.contactListView.Items[id].Group = Global.OpenForms.mainForm.contactListView.Groups["offlineListViewGroup"]; else Global.OpenForms.mainForm.contactListView.Items[id].Group = Global.OpenForms.mainForm.contactListView.Groups["onlineListViewGroup"]; Global.OpenForms.mainForm.contactListView.Sort(); } })); } public static void RemoveContact(string id) { Global.OpenForms.mainForm.Invoke(new MethodInvoker(() => { Global.OpenForms.mainForm.contactListView.Items.RemoveByKey(id); })); } }