DCSIMG
January 2009 - Posts - שלמה גולדברג (הרב דוטנט)

שלמה גולדברג (הרב דוטנט)

מרצה בסלע ויועץ בעולם ה - net.

January 2009 - Posts

Convert from client time zone to server time zone

 

נניח שאנחנו רוצים לקבל בצד של השרת את התאריך בהתאמה למה שאצל הלקוח, אבל לפי ה TimeZone של השרת,
 
אם השרת יושב בישראל, שה TimeZone הוא GMT+2, ומגיעים אליו לקוחות ממקומות שונים, ואנחנו רוצים לדעת את הזמן של הקליינט אבל לפי ה TimeZone שלנו,
(כדי לעדכן בסיס נתונים, ועוד)
 
אז אפשר לעשות את זה בצורה הבאה:
 
הנה קוד ה html
 

    1 <body onload="SetLocalTime()">

    2     <form id="form1" runat="server">

    3     <div>

    4         <asp:HiddenField ID="time" runat="server" />

    5         <asp:Button ID="btnDate" Text="click"

    6             runat="server" OnClick="btnDate_Click" />

    7     </div>

    8     </form>

    9 </body>

 
אין לנו הרבה כאן, בסך הכל, hiddenField אחד, כדי להעביר את המידע לשרת, ולחצן שמפעיל את השרת.
 
שימו גם לב ל onload שיש ב body, והנה ה script.
 

   10     <script>

   11         function SetLocalTime() {

   12 

   13             var now = new Date();

   14 

   15             form1.time.value =

   16                 now.getUTCFullYear() + "," +

   17                 now.getUTCMonth() + "," +

   18                 now.getUTCDate() + "," +

   19                 now.getUTCHours() + "," +

   20                 now.getUTCMinutes();

   21         }

   22     </script>

 
 
אנחנו שמים בתוך ה hiddenField את התאריך הנוכחי אצל הלקוח, אבל בהמרה ל UTC (כי השרת יודע להמיר רק זמן מ UTC)
 
 
והנה צד השרת:
 

   23 protected void btnDate_Click(object sender, EventArgs e)

   24 {

   25     string[] arr = time.Value.Split(new char[] { ',' });

   26 

   27     DateTime clientUDC = new DateTime(

   28         int.Parse(arr[0]),          // year

   29         int.Parse(arr[1]) + 1,      // montsh (JS return 0-11)

   30         int.Parse(arr[2]),          // day

   31         int.Parse(arr[3]),          // hour

   32         int.Parse(arr[4]),          // minuts

   33         0);                         // second

   34 

   35     DateTime serverTime =

   36         System.TimeZone.CurrentTimeZone.ToLocalTime(clientUDC);

   37 

   38 

   39 }

 
וזהו, קל ופשוט.

מפגש ה UserGroup הראשון של קבוצת ה BI (מתכנתים איפה הייתם ??)

 

היום התקיים המפגש הראשון של קבוצת ה BI,
 
הייתי שם, היה ממש נהדר, קודם כל כי לראשונה זכיתי בפרס כלשהו (מטען בטריות USB),
בדרך כלל עושים הגרלה למי שממלא את המשוב, אבל הפעם חולקו הפרסים לפי ההשתתפות (מסתבר שהייתי די פעיל)
 
אמנם אני יותר משתייך לקבוצת ה R&D, אבל אני גם מרגיש שייך ל BI,
בזמנו (לפני חצי שנה) ירקתי דם על ה Reporting Services, והיום אפשר להגיד שאני מרגיש ממש טוב בעבודה איתו  (כנראה שהגיע הזמן לכתוב כמה פוסטים על ReporingServices)
 
 
ההרצאות היו נהדרות, ההרצאה של רונן על ReportingServices 2008, הייתה ממש מעניינת.
 
וכמובן גם ההרצאה של ערן על olap, אחד הדברים שממש הרשימו אותי, היה ה personalization שאפשר לעשות בקוביה לפי ה session הנוכחי.
 
ואי אפשר לשכוח את יוסי (שכחתי את שם המשפחה) שתפקד כמרצה בשני ההרצאות גם יחד.
 
 
לסיכום ממש נהניתי, ובעזרת השם אני אמשיך להגיע למפגשים של ה BI, מקווה לראות את כולכם.
ותודה לכל מי שעבד כדי שהקבוצה תוקם.
 
 
 
 
Posted: Jan 28 2009, 09:57 PM by Shlomo | with 3 comment(s)
תגים:,

Invalid postback or callback argument. Event validation is enabled using.... (add items to server control dynamicly from javascript)

 

נניח שאנחנו רוצים להוסיף ב ajax ערכים ל dropDownList (לא על ידי UpdatePanel).
נראה את הקוד הבא:
 
דבר ראשון נייצר WebService שאפשר לקרוא לו מ Ajax,
 

    1 [WebService(Namespace = "http://tempuri.org/")]

    2 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

    3 [ToolboxItem(false)]

    4 [ScriptService]

    5 public class WebService1 : System.Web.Services.WebService

    6 {

    7     [WebMethod]

    8     public string[] GetListByText(string text)

    9     {

   10         return new string[] { "A", "B", "C", "D" };

   11     }

   12 }

 
 
 
 
 שימו לב לשורה 4.
מגדיר את ה WebService, שאפשר לקרוא לו מ Ajax
 
 
כעת נראה את ה html: נראה בהתחלה את ה body
 
 

   13 <body>

   14     <form id="form1" runat="server">

   15     <div>

   16         <asp:ScriptManager ID="ScriptManager1" runat="server">

   17             <Services>

   18                 <asp:ServiceReference Path="~/WebService1.asmx"

   19                     InlineScript="true" />

   20             </Services>

   21         </asp:ScriptManager>

   22 

   23         <asp:TextBox ID="txt" runat="server"

   24             onblur="GoToServerToBringData(this)"></asp:TextBox>

   25 

   26         <asp:DropDownList ID="ddl" runat="server"></asp:DropDownList><br />

   27 

   28         <asp:Button ID="btn" Text="Click" runat="server" />

   29     </div>

   30     </form>

   31 </body>

 
 
 
בשורה 18, אנחנו רושמים את ה WebService שלנו.
 
בשורה 23 יש לנו TextBox שבעזיבת פוקוס, אנחנו נפעיל פונקציית JS שמיד נראה.
 
בשורה 26 יש לנו DropDownList ללא אפשריות לבחירה.
 
ובשורה 28 יש לנו לחצן שלא עושה כלום מעבר ל PostBack לשרת.
 
 
כעת נראה את חלק ה Script:
 
 

   32 <script type="text/javascript">

   33     function GoToServerToBringData(txt)

   34     {

   35         ChangeControlViewState.WebService1.GetListByText

   36                     (txt.value, success, faield);

   37     }

   38 

   39     function success(result)

   40     {

   41         for (var i = 0; i < result.length; i++)

   42         {

   43             var oOption = document.createElement("OPTION");

   44             oOption.text = result[i];

   45             oOption.value = result[i];

   46 

   47             form1.ddl.options[i] = oOption;

   48         }

   49     }

   50 

   51     function faield()

   52     {

   53         alert("faield");

   54     }

   55 </script>

 
 
 בשורה 33 מוגדרת הפונקציה שאליה מגיעים בזמן עזיבת הפוקוס.
אנחונו קוראים לפונקצייה ב WebSerivce, ושולחים את:
 
הערך מתיבת הטקסט,
שם של מתודה כשהפונקצייה תחזור בהצלחה,
ושם של מתודה להפעלה במידה וקריאת ה Ajax נכשלה
 
 
 בשורה 39 יש לנו את מתודת "ההצלחה" שבו אנחנו מוסיפים ל DropDwonList את הערכים שהגיעו.
 
 
 לכאורה הכל נראה תקין, לא ?
 
אז נכון שהכול יעבוד כמו שצריך, אבל מה יקרה אם נלחץ על הלחצן שעושה PostBack ?
 
 
 
Invalid postback or callback argument.  Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%@ Page EnableEventValidation="true" %> in a page.  For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them.  If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.
 
 
 
אופס, מה זה ?
 
 
הסיבה לשגיאה היא מאוד פשוטה, יש בעיית security לאפשר לשנות ערכים בצד הלקוח על controls שערכים שלהם עוברים לשרת.
 
אז איך בכל זאת פותרים את הבעייה:
 
1. הכי פשוט ומהר, לשנות את enableEventValidation ל false, כך שלא יהיה בצד השרת בדיקה על ה Controls, האם הערכים השתנו.
אבל זה לא מומלץ, רק בתנאי שזה דף פנימי, ואין חשש שמישהו ינסה להשתיל ערכים זדוניים.
 
2. לעבוד עם UpdatePanel, ולהוסיף את הערכים ל DropDwonList בצד השרת,
 
3. להגיד לשרת "אל תבדוק את ה Control הזה": לפי ה msdn עושים את זה בצורה הבאה:
 
 

    1 protected override void Render(HtmlTextWriter writer)

    2 {

    3     Page.ClientScript.RegisterForEventValidation(ddl.UniqueID);

    4 

    5     base.Render(writer);

    6 

    7 }

 
מה לעשות, שזה לא עובד, חפשתי לראות האם מדברים על זה, ולא מצאתי,
זה מצחיק כי ה overload של הפונקציה, מקבל string arguments, שאני יכול להכניס אלו ערכים יכולים להיות ב ddl, כך:
 

    1 protected override void Render(HtmlTextWriter writer)

    2 {

    3     Page.ClientScript.RegisterForEventValidation(ddl.UniqueID, "A");

    4     Page.ClientScript.RegisterForEventValidation(ddl.UniqueID, "B");

    5     Page.ClientScript.RegisterForEventValidation(ddl.UniqueID, "C");

    6     Page.ClientScript.RegisterForEventValidation(ddl.UniqueID, "D");

    7 

    8     base.Render(writer);

    9 

   10 }

 
זה עובד,
אבל אם אני מכניס את הערכים אני יכול גם להכניס אותם ישירות ל ddl, לא ?
 
 
אפשרות נוספת זה להשתמש ב selction ולהגדיר אותו כ runat=server,
כי הבדיקות הם רק על מי שיורש מ WebControl,
 
ואם רוצים אפשר ללכת עוד יותר רחוק, ולהשתמש ב selection, ולא להגדיר אותו כ runat=server, ולקבל את הערכים על ידי:

Request.Form["ddl"]

 

Send a reference type as a parameter (הדברים הפשוטים שהרבה שוכחים)

 

הרבה פעמים, אנחנו שוכחים את הדברים הבסיסיים והפשוטים שלומדים בתחילת הדרך, למשל מי זוכר את ההבדלים בין:
 
x.Parse
Convert.Toxxx
(cast)
 
התשובה נמצאת בשני פוסטים, מהראשונים שכתבתי, חלק ראשון, וחלק שני (ביצועים)
 
 
לפני כמה ימים יצא לי להיתקל בעוד אחד מאותם דוגמאות.
 
שימו לב לקוד הבא:
 

    1 public void f1()

    2 {

    3     List<string> list = new List<string>();

    4     f2(list);

    5 }

    6 

    7 public void f2(List<string> li)

    8 {

    9     // Work...

   10 

   11     li = new List<string>() { "a", "b", "c" };

   12 }

 
 
 האם הקוד יעבוד ?
 
לי זה לקח חמש דקות של תיסכול, וחיפוש מה לא עשיתי טוב,
 
הרי List הוא reference type, אז מדוע בתוך f2 הערכים נשמרים בתוך ה List, ואילו כשאני חוזר ל f1, ה List שלי ריק ?
 
אחרי כמה דקות, הבנתי את מה שאני מלמד את הסטודנטים בשיעור השני,
 
כשאנחנו שולחים פרמטר שהוא ref לפונקציה, אנחנו שולחים למעשה את המצביע שלו, שזה בסופו של דבר משתנה מסוג int,
ואנחנו יכולים לשנות את התוכן שלו, זאת אומרת להוסיף ערכים ל List, מהפונקצייה הקודמת,
אבל אנחנו לא יכולים לשנות את המצביע עצמו, ולכן ניסיון לייצר List חדש, לא ישפיע על ה List המקורי,
 
מה שנוכל לעשות, זה:
 
או להוסיף ערכים ל List המקורי, כמו בדוגמא הזאת:
 
 

    1 public void f1()

    2 {

    3     List<string> list = new List<string>();

    4     f2(list);

    5 }

    6 

    7 public void f2(List<string> li)

    8 {

    9     // Work...

   10 

   11     li.AddRange(new string[] { "a", "b", "c" });

 
 
או שנוכל לשלוח מצביע למצביע (המקבילה של ** ב ++C הישנה והטובה)
 

    1 public void f1()

    2 {

    3     List<string> list = new List<string>();

    4     f2(ref list);

    5 }

    6 

    7 public void f2(ref List<string> li)

    8 {

    9     // Work...

   10 

   11     li = new List<string>() { "a", "b", "c" };

   12 }

 
שימו לב שעכשיו אנחנו שולחים עם ref, שזה אומר:
 
תשלח את ה int שמייצג את המיקום בזיכרון של ה int שמייצג את המיקום של ה List בזיכרון.
Posted: Jan 25 2009, 08:49 PM by Shlomo | with 1 comment(s)
תגים:

סגירת מעגל

 

אני מניח שחלק מהקוראים כבר מכירים את הרקע שלי,

כנראה שהרוב לא יודעים.
 
 
ונשאלת השאלה איך אני כחרדי הגעתי לתחום ההייטק, אתם יודעים זה לא מצוי כל כך. 
 
התשובה היא שהכל בזכות הפרויקט שנקרא פרנסה בכבוד, במסגרת הפרויקט, ג'וינט ישראל בשיתוף עם התמ"ת, וסלע מממנים לימודים מלאים במכללת סלע למגזר החרדי, במטרה לעזור לאנשים שאין להם שום רקע אקדמי, לצאת לשוק העבודה ולפרנס בכבוד את המשפחה שלהם. 
 
אני יכול להעיד על עצמי, שכשהגעתי לפני כמעט ארבע שנים לסלע, לא ידעתי אנגלית בכלל (רמת ABC), אני מתבייש להגיד מה הייתה רמת המתמטיקה שלי, וכמובן לא היה לי מושג ירוק במחשבים. 
 
היום כמו שאתם רואים, יש לי קצת יותר משמץ של מושג במחשבים, ואני יודע לקרוא באנגלית, ברמה מספקת. 
 
והכול בזכות הג'ונט והמרכז הטכנולוגי של סלע.. 
 
 
 
ומה בעצם הניע אותי לכתוב את הפוסט הזה ? 
 
בימים אלו מסתיים המחזור השלישי של הפרויקט, וכרגע יש לנו עשרים מפתחים ועשרים בודקי תוכנה מהמגזר החרדי,
 ואני יכול להגיד מהיכרות אישית עם החבר'ה (לימדתי אותם asp.net) שיש לנו לפחות עשרה מפתחים ועשרה בודקי תוכנה שיכולים היום להשתלב בתעשיית ההייטק. 
 
גילוי נאות, אח שלי גם לומד במסגרת הפרויקט, (אני חושב שהוא תותח בתחום) 
 
אז מי שמעוניין וזקוק למתכנתים או לבודקי תוכנה, מוזמן ליצור קשר עם 
 
ארז פליס: erezf@sela.co.il
או איתי דרך טופס יצירת קשר, או ישירות במייל: shlomog@sela.co.il 
 
לציין שהגעתם דרך הבלוג. 
 
 
Posted: Jan 22 2009, 12:45 PM by Shlomo | with 4 comment(s)
תגים:, ,

Do you want to restart your computer now? (No I don't)

 

מכירים את החלון המעצבן הבא ?
au
 
זה מופיע בדרך כלל, כשאנחנו ילדים טובים ומפעילים את ה Automatic Update,
 
וכשהוא גומר להתקין הוא שואל אותנו, האם להפעיל את המחשב מחדש,
אז אני אומר לו, לא אני לא מעוניין, בא לי להמשיך לעבוד, בערב כשאני אסגור את המחשב אני אעשה את ה restart.
 
אבל כמו נודניק טוב, כל רבע שעה הוא שוב קופץ עם השאלה המרגיזה, עד שאני נכנע, ועושה restart כמו שהוא מבקש.
 
 
אז דרך אחרת לפתור את הבעייה, זה לסגור באופן זמני את ה service של ה automatic update.
אני מניח שכולכם מכירים את החלון של ה services, מגיעים אליו על ידי הפעלה של חלון ה Run, והפעלת הפקודה services.msc, נקבל את החלון הבא:
 
 
ss
 
 
 
 
במידה ונעשה Stop ל Service של Automatic Update (זה שנבחר), הוא יפסיק להתנהג כמו אמא פולניה, וכל כמה דקות לשאול האם לעשות restart,
וכמובן אחרי שנעשה את ה restart שלנו ה service יעלה בצורה אוטומטית בחזרה.
Posted: Jan 21 2009, 10:43 PM by Shlomo | with 4 comment(s)
תגים:,

DuplexChannel in WCF (Messanger)

 

ישבתי בכיתה עם סטונדטים, והם אומרים לי: אנחנו צריכים לממש צ'ט, איך עושים את זה,
אמרתי להם תעשו את זה ב WCF, אז הסטודנטים ביקשו דוגמא,

התיישבתי וכתבתי דוגמא ל messenger ב WCF.
 
גילוי נאות: אני לא יודע WCF כמו שצריך, אני עובד איתו כבר שנה, מקים services, משתמש בשירותים שלהם, דוחף attributes בכל מיני מקומות, אבל אין לי באמת שמץ של מושג מה אני עושה, אני מתכוון להיכנס לקורס WCF כדי ללמוד את זה בצורה מסודרת,
והיות שאני לא יודע WCF כמו שצריך, יכול להיות שבדוגמה שנראה עוד מעט כתבתי שטויות,
אשמח מאוד אם מי שיהיה לו כוח לקרוא את כל המאמר, ויראה שעשיתי דברים לא כמו שצריך, שיתקן אותי.
 
אז אחרי ההקדמה הארוכה, נעבור לדברים המעניינים.
לפני שנעבור על הקוד, נראה את התוצאה הסופית.
 
 msd
 
 
 
בסופו של תהליך, אנחנו מריצים את ה server על מחשב אחד, מריצים הרבה clients על מחשבים שונים, לכל לקוח יש רשימה של כל המשתמשים,
וכל משתמש יכול לשלוח הודעות מאחד לשני.
 
את הפרויקט תוכלו להוריד מכאן.
(על המחשב של השרת צריך להריץ את הפרויקט שנקרא HostService, ועל המחשבים האחרים (אפשר כמובן גם על המחשב של השרת, ואפשר כמה לקוחות על אותו מחשב) להריץ את הפרויקט שנקרא Messenger,
צריך לזכור שאם אתם מריצים על מחשב אחר מהשרת את הלקוח, לשנות בקונפיג ל ip המתאים, ולהוריד את חומת האש (לא זוכר איך כותבים באנגלית))
 
 
ונתחיל לעבור על הדוגמא:
הפרויקט מורכב מחמשה פרויקטים שונים:
 
Client - בו יושב הלוגיקה של הלקוח להתחברות מול השרת, כל לקוח שרוצה (יכול להיות win או console או web) יכול לעשות מופע ממנו, ולתקשר עם השרת.
 
HostService - בדוגמא שלנו, עושה hosting לשירות שהשרת מספק, למעשה המחשב שבו מריצים את ה hostSerivce, הוא השרת.
 
Interfaces - מכיל את ה interfaces של השירותים שהשרת מספק, נמצא בפרויקט נפרד, כדי שהלקוח יכיר רק אותו, ולא יכיר את מי שמממש אותו.
 
Messanger - בדוגמא שלנו, הוא הלקוח, אפליקציית WinForm שמשתמשת בשירותים של השרת (דרך הפרויקט של ה Client) כדי לשלוח ולקבל הודעות.
 
Service - מממש את ה interface של השירות, מתארח ב hostService.
 
 
 
נתחיל בפרויקט Interfaces.
יש בו קובץ אחד שנראה כך:
 

    1 [ServiceContract(CallbackContract = typeof(IChatClient))]

    2 public interface IChatService

    3 {

    4     [OperationContract]

    5     void SendMsg(string message, string chatterName, string fromUserName);

    6 

    7     [OperationContract]

    8     List<string> Register(string userName);

    9 }

   10 

   11 public interface IChatClient

   12 {

   13     [OperationContract]

   14     void PushMsg(string message, string sender);

   15 

   16     [OperationContract]

   17     void UserRegister(string userName);

   18 }


נעבור על הקוד:
נתעלם לרגע משורה 1.
משורה 2 עד שורה 9, אנחנו מגדירים את ה interface, שה service מספק.
המתודה הראשונה, הקליינט פונה לשרת, ואומר אני רוצה לשלוח הודעה, מספק את ההודעה, למי הוא רוצה לשלוח, ומי השולח.
המתודה השנייה, כשלקוח חדש רוצה להתחיל לשלוח ולקבל הודעות, הוא צריך להירשם, והוא מקבל את רשימת כל הקליינטים שנרשמו.
 
נחזור לשורה הראשונה,
במידה וזה היה WCF רגיל, שהקליינט פונה לשרת, ומקבל בחזרה מהשרת מידע, לא היינו צריכים יותר מזה,
אבל במקרה שלנו, אנחנו רוצים שהשרת יפנה מיוזמתו ללקוח אחר וישלח לו את ההודעה שהוא קיבל עבורו,
במקרה הזה הלקוח עצמו אמור להיות שרת, כי שרת יודע לפנות לשרת, ולא ללקוח, אז איך עושים את זה ?
 
זה מה למעשה הגדרנו בשורה הראשונה,
אמרנו שמי שירצה לעשות תקשורת מול השירות שאני (IChatService) מספק, יצטרך לממש את ה interface הבא (IChatClient), כך שאני (השרת) ידע להפעיל אצלך (הלקוח) מתודות.
 
נראה את ה interface השני:
שוב שני מתודות,
הראשון: מפעיל אצל הלקוח את המתודה, ושולח לו את ההודעה, ומי שלח, (תפקידו של הלקוח, לעשות עם המידע הזה מה שהוא רוצה, כי הלקוח אמור לממש את IChatClient)
המתודה השנייה, זה מדמה event, שבכל פעם שמישהו נרשם, השרת מודיע לכל הלקוחות הקיימים, מישהו חדש נרשם.

 
 
ונעבור לפרויקט Service.
הוא מממש את ה Interface הראשון.
הנה הקוד:

   

    1 [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]

    2 public class ChatService : IChatService

    3 {

    4     public static Dictionary<string, IChatClient> list =

    5         new Dictionary<string, IChatClient>();

    6 

    7     #region IChatService Members

    8 

    9     public List<string> Register(string userName)

   10     {

   11         if (!list.ContainsKey(userName))

   12         {

   13             foreach (IChatClient item in list.Values)

   14             {

   15                 item.UserRegister(userName);

   16             }

   17 

   18             list.Add(userName,

   19                 OperationContext.Current.GetCallbackChannel<IChatClient>());

   20         }

   21 

   22         List<string> returnList = list.Keys.ToList();

   23         returnList.Remove(userName);

   24         return returnList;

   25     }

   26 

   27     public void SendMsg(string message, string chatterName, string fromUserName)

   28     {

   29         list[chatterName].PushMsg(message, fromUserName);

   30     }

   31 

   32     #endregion

   33 }


לא להיבהל, זה לא כל כך מסובך.
כרגיל נתעלם מהשורה הראשונה, ונתחיל מהשורה השנייה.

יש לנו את ה class שמממש את ה interface, ובנוסף מחזיק list שה key הוא ה userName וה value הוא מישהו מסוג IChatClient.
 
בשורה 9 אנחנו מממשים את ה Register,
אנחנו בודקים האם לא קיים משתמש עם אותו שם.
רצים על כל הלקוחות שנמצאים ברשימה, ומפעילים אצל כולם את המתודה UserRegister. (כדי שידעו שמשתמש חדש נרשם)
מוסיפים את הלקוח החדש לרשימה, כשה key הוא ה userName וה value אנחנו לוקחים מה Contex הנוכחי שפתח מול השרת Channel, ואנחנו יודעים שהוא מממש את IChatClient.
 
בסוף התהליך אנחנו לוקחים את רשימת הלקוחות, מסירים את הלקוח שנרשם כרגע, ומחזירים אותו למי שנרשם, כדי שידע מי הלקוחות שכבר קיימים במערכת.
 

בשורה 27, אנחנו מממשים את SendMsg, שמה שהוא עושה, זה למצוא את ה Channel של הלקוח ב list, ולהפעיל עליו את המתודה PushMsg.
 
 
נחזור לשורה הראשונה.
היא בסך הכול מגדירה את הדבר הבא,
בכל פעם שמגיעה קריאה לשרת הוא צריך לייצר thread נפרד לכל קריאה, כך שאם קריאה אחת נתקעה, זה לא יתקע את שאר הקריאות.
 
 
 
ונעבור לפרויקט HostService.
כאמור אני לא מכיר WCF כמו שצריך, ואני לא מבין מה אני מרויח אם אני כותב את ההגדרות של ה Service שלי בקונפיגורצייה, (ואל תגידו לי שלא צריך לקמפל אם אני רוצה לשנות פתאום מלעבוד ב htpp ל nettcp, כי זה בולשיט אחד גדול,
בינינו, כשאנחנו כותבים service אנחנו יודעים בדיוק, איך הוא ירוץ, ואנחנו כותבים את הקוד מותאם לפי זה, ואם נרצה לשנות את התקשורת, מן הסתם נשנה עוד דברים, שנצטרך לקמפל,
ומעבר לכך אני שונא לקרוא קבצי XML, בעוד שאני אוהב לקרוא קטעי קוד)
 
ולכן כתבתי את זה בקוד.
הנה הקוד ב Console Apllication
 

    1 static ServiceHost host = null;

    2 

    3 static void Main(string[] args)

    4 {

    5     NetTcpBinding myBinding = new NetTcpBinding();

    6     myBinding.Security.Mode = SecurityMode.None;

    7 

    8     host = new ServiceHost(typeof(ChatService));

    9 

   10     string address = string.Format("net.tcp://{0}",

   11         ConfigurationManager.AppSettings["address"]);

   12 

   13     host.AddServiceEndpoint(typeof(IChatService), myBinding, address);

   14 

   15     host.Open();

   16 

   17     Console.WriteLine(address + ".\nPress any key to close");

   18     Console.ReadKey();

   19     Console.WriteLine("Closing ...");

   20 }


שורה 4, אני יוצר NetTcpBinding, מגדיר בשורה 5, שאני לא רוצה שום security (אחרת זה מתחיל לפעמים לעשות בעיות)
שורה 8 אני יוצר עבור ה ChatService את ה host שיחזיק אותו ויעשה לו את ה new מאחורי הקלעים.
 
שורה 10, אני לוקח את הכתובת מקובץ  הקונפיג.(localhost:12345)
 
שורה 13, אני יוצר EndPoint, למישהו שמממש את IChatService, ואני שולח את ה Binding, ואת הכתובת לפתיחת ה EndPoint.
 
בשורה 15 אני פותח את ה host,
וזהו.
עד כאן עברנו על כל הפרויקטים של השרת.
ה interface, ה Service ומי שעושה לו hosting
 
 
 
עכשיו נעבור לפרויקטים של הלקוח.
אז הוא מחולק למעשה לשניים,
 
פרויקט אחד שנקרא Client שמטפל בכל ענייני התקשורת וה binding,
והשני שנקרא messenger שהוא כרגע הלקוח האמיתי שלי, (מחר זה יכול להיות מישהו אחר, ולכן אני מפריד בין התעסקות עם התקשורת, לבין הלוגיקה של מה אני בעצם רוצה לעשות)
 
הנה הקוד של Client:
מכיל שני Classes,
הראשון נקרא Chatter שמחזיק את שם המשתמש ומחזיק intstance של ה class השני שנקרא proxy.

  

    1 public class Chatter

    2 {

    3     private string m_UserName;

    4     private Proxy m_Proxy;

    5 

    6     public Chatter(string userName, IChatClient

    7                     chatClient, List<string> users)

    8     {

    9         this.m_UserName = userName;

   10         m_Proxy = new Proxy(this.m_UserName, chatClient, users);

   11     }

   12 

   13     public void SendMsg(string message, string chatterName)

   14     {

   15         m_Proxy.SendMsg(message, chatterName);

   16     }

   17 }


ב ctor שלו הוא מקבל את שם המשתמש, מישהו שמממש את IChatClient (כדי לשלוח אותו ל server, כדי שה server ויכל להפעיל עליו מתודות)
ו list ריק, כדי שהוא ימלא אותו עם שמות כל מי שנרשם עד עכשיו, בשלב ה register.
 
והנה הקוד של ה proxy:

    1 class Proxy

    2 {

    3     private NetTcpBinding m_ServiceBinding = new NetTcpBinding();

    4     private IChatClient m_ChatClient;

    5     private string m_UserName;

    6     private IChatService m_Service;

    7 

    8     public Proxy(string userName, IChatClient chatClient, List<string> users)

    9     {

   10         m_UserName = userName;

   11         m_ChatClient = chatClient;

   12         m_ServiceBinding.Security.Mode = SecurityMode.None;

   13 

   14         users.AddRange(Service.Register(userName));

   15     }

   16 

   17     private IChatService Service

   18     {

   19         get

   20         {

   21             if (m_Service == null ||

   22                 ((IChannel)m_Service).State == CommunicationState.Closed)

   23             {

   24                 //address

   25                 string address = string.Format("net.tcp://{0}",

   26                     ConfigurationManager.AppSettings["address"]);

   27 

   28                 EndpointAddress endPoint = new EndpointAddress(address);

   29                 // a, b, c

   30                 IChatService chatService =

   31                     DuplexChannelFactory<IChatService>.CreateChannel

   32                         (m_ChatClient, m_ServiceBinding, endPoint);

   33 

   34                 m_Service = chatService;

   35             }

   36 

   37             return m_Service;

   38         }

   39     }

   40 

   41     public void SendMsg(string message, string chatterName)

   42     {

   43         Service.SendMsg(message, chatterName, m_UserName);

   44     }

   45 

   46 }

 

בשורות 3 עד 6, אני מחזיק
Binding,
שם המשתמש,
Instance של IChatClient,
ו Instance של IChatService.

ב ctor שלו  אני מאתחל את המשתנים, ונרשם ל service, וממלא את ה list אם הערכים שחזרו מה service. (בפוסט הבא אני אסביר למה השתמשתי ב AddRange, במקום לעשות השמה פשוטה ולכתוב users=register)
 
בשורה 17, אנחנו מחזירים את IChatService, על ידי,
שאחנו בודקים האם זה null (כי זה פעם ראשונה) או שה channel נסגר (כי עבר דקה (ברירת מחדל) מאז הפנייה האחרונה)
אנחנו מייצרים מחדש את ה channel, ונסתכל על שורה 30,
 
אנחנו מייצרים אותו על ידי ה class DuplexChannelFactory, שמקבל בפרמטר הראשון, מישהו שמממש את ה Interface שמוגדר ב Attribute על ה Interface של ה Service
זוכרים את הקוד הזה:
 

    1 [ServiceContract(CallbackContract = typeof(IChatClient))]

    2 public interface IChatService

   


וכמובן מקבל את ה endpoint, ואת ה binding.
 
 
 
ונעבור לפרויקט האחרון שלנו: messenger:
מכיל שני forms, הראשון login, שלמעשה לא עושה כלום מעבר לכך שבלחיצה על ok, הוא יוצר את הטופס השני.
וכאן הקוד של הטופס השני.

    1 public partial class Form1 : Form, IChatClient

    2 {

    3     private Chatter m_Chatter;

    4 

    5     public Form1(string userName)

    6     {

    7         InitializeComponent();

    8         List<string> users = new List<string>();

    9         m_Chatter = new Chatter(userName, this, users);

   10 

   11         comboBox1.Items.AddRange(users.ToArray());

   12         this.Text = userName;

   13     }

   14 

   15     #region IChatClient Members

   16 

   17     public void PushMsg(string message, string sender)

   18     {

   19         textBox1.Text += string.Format("{0}: Send: {1}{2}{3}",

   20             sender, message, Environment.NewLine, Environment.NewLine);

   21     }

   22 

   23     public void UserRegister(string userName)

   24     {

   25         comboBox1.Items.Add(userName);

   26     }

   27     #endregion

   28 

   29 

   30     private void button1_Click(object sender, EventArgs e)

   31     {

   32         m_Chatter.SendMsg(textBox2.Text, comboBox1.Text);

   33         textBox2.Text = string.Empty;

   34     }

   35 

   36 

   37 }


שימו לב לשורה הראשונה, הטופס עצמו מממש את IChatClient,
ב ctor בשורה 9 אני יוצר את ה chatter ושולח את this כמי שמממש את IChatClient

בשורה 11 אני ממלא את ה comboBox עם שמות המשתמשים שכבר נרשמו.
ואתם יכולים להסתכל על המימוש של IChatClient, שמעדכן את ה GUI כמו שצריך.
 

אם שרדתם עד כאן, מגיע לכם צל"ש.

מקווה שזה הועיל במשהו, וכמו שאמרתי, יכול להיות שעשיתי כאן דברים שלא כמו שצריך, אבל אני מקווה שבעוד חודש וחצי (בערך) אני כבר אדע WCF כמו שצריך. (אשמח לפגוש אתכם בקורס WCF שמנו מעביר בתאריך 23/03, אני מתעתד להיות שם)
 
 
אשמח לקבל הערות ממי שמבין יותר טוב ממני ב WCF, אם יש דברים שעדיף לעשות בצורה שונה.
Posted: Jan 21 2009, 10:19 PM by Shlomo | with 4 comment(s)
תגים:,

משתנה לוקלי בפונקציה (JavaScript) האומנם ? - או - הדרך הקלה להיכנס ללואה אינסופית

 

אתמול ישבה תקוה בורקיס (חברה לעבודה בסלע) וכתבה פונקציות ב JavaScript,
 
באיזשהוא שלב היא אומרת לי, שלמה, אני נכנסת ללולאה אינסופית, מה קורה כאן ?
 אז הסתכלנו על הקוד, ומה שהיה כתוב היה בערך כך:
 
 

    1 <script type="text/javascript">

    2     function f1()

    3     {

    4         for (i = 0; i < 5; i++)

    5         {

    6             // Work...

    7             if (i == 3)

    8             {

    9                 f2();

   10             }

   11         }

   12     }

   13 

   14     function f2()

   15     {

   16         for (i = 0; i < 2; i++)

   17         {

   18             // Work...

   19         }

   20     }

   21 </script>

 
נו.. אל תקראו הלאה, תסתכלו על הקוד ותנסו להבין מה קורה.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
זהו, נשברתם ?
 
הפיתרון מאוד פשוט אבל לא אינטאוטיבי.
 
ב JavaScript הדרך להגדיר משתנה סטטי של ה document הוא להתחיל להשתמש בו בלי להגדיר אותו, לדוגמא: המימוש הבא יודע לספור כמה פעמים היינו בתוך פונקציה.
 

    1     function CallCount()

    2     {

    3         if (typeof (count) == "undefined")

    4         {

    5             count = 0;

    6         }

    7 

    8         count++;

    9 

   10         alert(count);

   11     }

 
כמו שאתם רואים, שימוש במשתנה ללא הגדרה של var, יעשה אותו סטטי,
אבל הנה הסוד הקטן, לא רק שהוא סטטי, הוא גם גלובלי, זאת אומרת שינוי של משתנה עם אותו שם בפונקצייה אחרת, ישפיע גם על הפונקצייה הזאת,
 
ולכן כשתקוה רצה בלולאה על המשתנה i והפעילה פונקציה שגם רצה בלולאה על המשתנה i, היא למעשה שינתה את הערך של המשתנה שהיה אמור לספור את מספר הריצות בלולאה העליונה, ולכן הקוד של תקווה צריך להיראות כך:
 

    1 <script type="text/javascript">

    2     function f1()

    3     {

    4         for (var i = 0; i < 5; i++)

    5         {

    6             // Work...

    7             if (i == 3)

    8             {

    9                 f2();

   10             }

   11         }

   12     }

   13 

   14     function f2()

   15     {

   16         for (var i = 0; i < 2; i++)

   17         {

   18             // Work...

   19         }

   20     }

   21 </script>

 
שימו לב לשורה 4, 16 להוספת המילה var לפני ההדרה של המשתנה i

the page cannot be displayed - IIS 6.0

ביום חמישי ישבו שני חברים בעבודה, וניסו במשך זמן רב להבין מדוע האפליקצייה שהם כתבו, רצה ועובדת מצוין על ה IIS המקומי שלהם, ואילו כשהם מעלים את זה לשרת הם מקבלים את ההודעה המרגיזה the page cannot be displayed, וכמובן לא מופיע שום דבר ב event viewr,
 
בסופו של דבר הם שאלו אותי אם יש לי מושג מה קורה, ונזכרתי שגיא תמיר שהיה הבוס שלי עד לפני שבוע (עבדתי ב QualiSystems ב outsourcing כשנתיים, כרגע חזרתי לסלע), אמר לי לפני כשנה שהוא ירק דם עד שהוא הבין שה user שניגשים איתו באפליקצייה כ Anonymous חייב להיות חבר ב group שנקרא IIS-WPG, אחרת הדף לא נטען, וכמובן לא כתוב בשום מקום מה הסיבה שהדף לא נטען.
 
הוספתי את המשתמש שהם ניגשו איתו ל group המדובר, וכמעשה ניסים, הכל התחיל לעבוד.
 
 
Posted: Jan 17 2009, 09:59 PM by Shlomo | with 6 comment(s)
תגים:, ,

LinqToSql - Save just the last change - Part 2

 

בהמשך לפוסט הקודם שבו כתבתי איך לעשות עידכון רק לשינוי האחרון,
דברתי עם אדר לגבי המוטיבציה לעשות את זה, מדוע שנרצה לעדכן רק את האחרון, והגענו להסכמה שיש כאן משהו מוזר,
 
כי ב Ado הישן והטוב כשרצינו לעשות upadte היינו יכולים לשלוח מערך של DataRow, ולעדכן רק חלק מהנתונים, אז מדוע כשאנחנו מתקדמים ל LinkToSql אנחנו הולכים אחורה מבחינה פונקציונליות ?
 
בכל מקרה במדה ואנחנו מוכנים לעשות undo לכל השינוים האחרים מלבד האחרון, נוכל להשתמש בפתרון, מהפוסט הקודם, 
 
אבל במדה ולא נרצה לעשות undo, יניב חדד הציע לי, לייצר DataContex חדש, להעביר אליו את השינוי האחרון, ולעשות עליו SubmitChanges,
 
אז ניסיתי לממש אותו, ומסתבר שזה לא כל כך פשוט, אמנם זה לא הרבה שורות קוד,
אבל לא ברור לי מדוע בקשה כל כך פשוטה, צריכה להיות עם קוד מסובך ולא יפה,
 
בכל מקרה, הנה הפיתרון: (הדוגמא הייתה על Northwind) (ותסלחו לי על האנגלית המזויעה שלי)
 
 

    1 public static void UpdateLastChanges()

    2 {

    3     // Get all changes from the data contex

    4     ChangeSet cs = ndc.GetChangeSet();

    5 

    6     // Take the last update

    7     Order lastChanget = (Order)cs.Updates[1];

    8 

    9     // Create a new data contex

   10     NorthwindDataContext newContex = new NorthwindDataContext();

   11 

   12     // Find the same order in the new data contex by the OrderId

   13     Order original = newContex.Orders.Single(

   14         item => item.OrderID == lastChanget.OrderID);

   15 

   16     // Get the type of Order 

   17     // it's for runnig with reflaction and change the properties

   18     Type orderType = original.GetType();

   19 

   20     // Get the all modufied members in the order

   21     ModifiedMemberInfo[] memberInfos =

   22         ndc.Orders.GetModifiedMembers(lastChanget);

   23 

   24     foreach (ModifiedMemberInfo memberInfo in memberInfos)

   25     {

   26         // Get the PropertyInfo by the name of the change member

   27         PropertyInfo originalMemberInfo =

   28             orderType.GetProperty(memberInfo.Member.Name);

   29 

   30         // And cahnge the order in the new data contex

   31         originalMemberInfo.SetValue(original, memberInfo.CurrentValue, null);

   32     }

   33 

   34     // Submit Changes of the new data conex

   35     newContex.SubmitChanges();

   36 

   37     // Let's know the old data contex

   38     // taht the clast change is saved in the database

   39     ndc.Refresh(RefreshMode.OverwriteCurrentValues, cs.Updates[1]);

   40 }

 
השורה האחרונה היא מאוד חשובה,
אנחנו צריכים לעדכן את ה dataContex שאנחנו עובדים איתו, שהשינוי כבר התבצע.
Posted: Jan 15 2009, 07:11 PM by Shlomo | with no comments
תגים:, ,

LinqToSql - Save just the last change

 

מישהו חביב בשם נריה שאל אותי את השאלה הבאה:
 
"יש לי אפליקציה חלונאית, ובה dataGridView המקבל את הנתונים ע"י Binding  מ DataContext.
ואני רוצה כפתור שישמור רק את השינוי האחרון שבוצע."
 
באופן אישי לא יצא לי כל כך להשתמש עדיין ב LinqToSql, אבל כמובן שזאת הייתה הזדמנות נהדרת לבדוק את זה, וידוע שאני אוהב ללמוד דברים חדשים, אז התחלתי להסתכל..
 
בסוף עשיתי את הדבר הבא, עשיתי undo לכל השינויים האחרונים, חוץ מלאחרון, ואז שמרתי את השינוי האחרון.
הנה הקוד:

    1 public static void UpdateLastChanges()

    2 {

    3     ChangeSet cs = ndc.GetChangeSet();

    4     var listOfUpdates = cs.Updates.ToList();

    5     listOfUpdates.RemoveAt(listOfUpdates.Count - 1);

    6 

    7     ndc.Refresh(RefreshMode.OverwriteCurrentValues, listOfUpdates);

    8 

    9     ndc.SubmitChanges();

   10 }

 
זה עובד, אבל אני לא מרוצה מזה,
דבר ראשון, אני לא מעוניין לעשות undo, מה שאני רוצה לעשות, זה לשמור ב database רק את השינוי האחרון.
שנית, אני לא יודע האם הפעולה האחרונה הייתה, Insert, Update or Delete,
 
אם למישהו יש רעיון יותר טוב, אני ונריה נשמח מאוד לשמוע.
שלמה

target = '_blank' vs. target = '_BLANK'

 

לימדתי היום קורס Programming Web Clients בסלע,
אני רגיל לכתוב את קוד ה html ביד ולא עם editor שיוצר בשבילי את התגים, ואני רגיל לכתוב את הכל באותיות גדולות, לדוגמא:
<DIV ALIGN='CENTER'>
להפתעתי הראה לי אחד מהתלמידים, שהתקן אומר לכתוב באותיות קטנות.
ואז גיליתי שלמעשה גם ב html הפשוט שלכאורה הוא לא case sensitive יש מצב שבו זה משנה האם כותבים באותיות קטנות או גדולות.
 
אנחנו יודעים שלינק יכול לקבל כ target את המצבים הבאים.
self: כדי לפתוח את הלינק באותו חלון
blank: כדי לפתוח חלון חדש
וכל שם אחר כדי לפתוח חלון חדש במדה ועדיין לא נפתח חלון עם אותו השם.
 
עכשיו נראה את הקוד הבא:
<a href='http://www.google.com' target='_BLANK'>Google com</a>
<a href='http://www.sela.co.il' target='_BLANK'>Sela</a>
 
לכאורה ציפיתי, שכשנלחץ על הלינקים, הוא יפתח כל אחד בחלון נפרד, מה שמתברר, שאת הראשון שלנחץ הוא אמנם פותח בחלון נפרד, ושם החלון הוא BLANK, וכשפותחים את השני, הוא מחליף את החלון הראשון, במקום לפתוח בחלון חדש, מפני שכשכתבנו BLANK באותיות גדולות, הוא לא הבין שהכוונה לדף חדש, אלא הוא הבין שהכוונה לחלון עם השם BLANK, וכדי לפתוח תמיד בחלון חדש חייבים לכתוב blank באותיות קטנות, כך:
<a href='http://www.google.com' target='_blank'>Google com</a>
<a href='http://www.sela.co.il' target='_blank'>Sela</a>

Difference behavior between Visual Studio Development Server and IIS (Ajax - UpdatePanel)

 

בשעה טובה רצינו להוציא גרסה ל QA למוצר שאנחנו מפתחים, ואנחנו מגלים שאיכשהו הדברים לא עובדים כמו שצריך.
ואז ראש הצוות שלי תומר כהן מגלה שיש התנהגות שונה בין ה Visual Studio Development Server לבין ה IIS,
 
מה שמאוד חמור, כי לכאורה ה WebServer הפנימי של Visual Studio צריך לחקות בדיוק את ההתנהגות של ה IIS, אחרת איך נוכל לפתח אפליקציות WEB עם השרת הפנימי במקום עם ה IIS.
 
אז הנה הסיטואציה:
 
נניח שיש Page שמכיל IFrame שמכיל לחצן כלשהו. וה Pgae מכיל גם לחצן
 
מתוך ה Page ב JavaScript אנחנו לוחצים על הלחצן ב Page ועל הלחצן ב Frame.
 
 
לכאורה אנחנו מצפים שקודם יתרחש Button_Click של ה Page ורק אחרי זה Button_Click של ה Frame.
זה באמת עובד כך.
אבל אם יש ב Page גם UpdatePanel, אם אנחנו רצים עם ה webServer הפנימי של Visual Studio, הכל עובד בסדר, קודם רץ ה Click של ה Page, ורק אחרי זה רץ ה Click של ה Frame.
אבל אם אנחנו רצים תחת IIS, הכל רץ בסדר הפוך. קודם רץ ה Click של ה Frame ורק אח"כ רץ ה Click של ה Page.
 
אם לא התבלבלתם עד עכשיו, הנה הקוד.
 
 
 

    1 <form id="form1" runat="server">

    2     <asp:ScriptManager ID="fff" runat="server">

    3     </asp:ScriptManager>

    4     <asp:UpdatePanel ID="dd" runat="server">

    5         <ContentTemplate>

    6             <iframe src="WebForm1.aspx"></iframe>

    7             <asp:Button ID="Button1" runat="server"

    8                 OnClick="Button1_Click" Text="Button" />

    9         </ContentTemplate>

   10     </asp:UpdatePanel>

   11     <input id="Button2" type="button" value="button"

   12             onclick="f()" />

   13 </form>

 

והנה הקוד של ה Frame

   14 <form id="form1" runat="server">

   15     <asp:Button ID="Button1" runat="server"

   16         onclick="Button1_Click" Text="Button" />

   17 </form>

והנה פונקציית ה JavaScript שנמצאת בתוך ה Page

   18 function f()

   19 {

   20     document.getElementById('Button1').click();

   21     frames[0].document.getElementById('Button1').click();

   22 }

 
 
זה מאוד מוזר, ואני לא מצליח להבין את ההסבר להתנהגות, בכל מקרה מצאנו workaround, בכך שאנחנו כותבים את ה JavaScript כך:

   23 function f()

   24 {

   25     document.getElementById('Button1').click();

   26     window.setTimeout("frames[0].document.getElementById('Button1').click();", 1);

   27 }

 
כמו שאתם רואים, אנחנו מריצים את השורה השנייה אחרי השהייה של מילי שנייה אחת, ואז הכל עובד כמצופה גם ב IIS, שקודם רץ ה Click ב Page, ואחרי זה רץ ה Click של ה Frame
 
אם מישהו יודע או מכיר את הסיבה להתנהגות המוזרה, אני מאוד אשמח לשמוע.
 
עידכון:
למעשה אני עושה עוול ל Visual Studio Development Server, אמנם הוא לא בסדר שהוא לא מתאים את עצמו ל IIS, אבל מי שלא בסדר במקרה הזה, זה ה IIS, כי אנחנו מצפים שההתנהגות  כמו ב Visual Studio, ולא כמו ב IIS
 

טיפ אחד, TreeView מוזר, וחלון של Windows 3.11 ב XP

 

אז החלטתי לכתוב פוסט קליל היום, אז קבצתי כמה דברים שמזמן רציתי לכתוב בפוסט אחד.
 
טיפ: איך לקנפג שכשמורידים קובץ (CSS לדוגמא) ה Browser לא יפתח אותו אוטומטית, אלא ישאל האם לשמור.
 
  • תפתחו תיקייה כלשהיא.
  • לחיצה על Tools.
  • לחיצה על Folder Options.
  • בחירה ב File Types.
  • בחירה בקובץ שלכם.
  • לחיצה על Advanced.
  • וסימון Confrim open after download.
 
FileTypes
 
 
FileTypesAd
 
 
 
 
TreeView מוזר.
פתחתי את האפשרויות של ה Internet Explorer, וגיליתי שתחת Advanced, הקטגוריות הם למעשה TreeView, בלי שאתה יודע בעצם שזה TreeView.
 
 
IEProperties
 
לחיצה כפולה על אחד מהקטגוריות, תפתח את הרשימה.
 
 
IEpropCollapse
מה שמוזר, שאין לבן אדם מן היישוב שמץ של סיכוי לדעת שמדובר ב TreeView.
 
 
ואחרון חביב. חלון של Windows 3.11 ב XP.
 
  • נכנסים ל Control Panel.
  • נכנסים לתיקיית Fonts.
  • בוחרים ב File.
  • ואז ב Install new fonts.
 
 
AddFont
מעניין איך זה בויסטה ?

Update gui controls from other thread

 

באחד הפוסטים הראשונים כתבתי על דרך לעדכן controls מתוך thread אחר.
אני שוב אדגים כאן את הבעייה בקצרה,
אם ננסה להריץ את הקוד הבא:

    1 private void button1_Click(object sender, EventArgs e)

    2 {

    3     Thread t = new Thread(new ThreadStart(target));

    4     t.Start();

    5 }

    6 

    7 private void target()

    8 {

    9     this.Text = "ddddd";

   10 }

נקבל את השגיאה הבאה:
 
CrossThread
 
 
כמובן שההסבר הוא פשוט, בגלל ענייני Security אסור לשנות Gui של controls שנוצרו ב thread אחר.
 
אז יש כמה וכמה שיטות להתגבר על זה, מקודם מצאתי שיטה חדשה (ונהדרת) להתגבר על זה.
אם לא מעניין אותנו ה Security בנקודה הזאת. אפשר להגדיר ל CLR להתעלם משגיאות מהסוג הזה, איך ?
לכל Control יש מאפיין סטטי שנקרא CheckForIllegalCrossThreadCalls, שאשפשר להגדיר אותו כ false, וזהו.
מאותו רגע אפשר לשנות את ה gui של ה control מכל thread
 
גאוני, לא ?
 
הנה הקוד.
 

    1 public Form1()

    2 {

    3     InitializeComponent();

    4     Form1.CheckForIllegalCrossThreadCalls = false;

    5 }

    6 

    7 private void button1_Click(object sender, EventArgs e)

    8 {

    9     Thread t = new Thread(new ThreadStart(target));

   10     t.Start();

   11 }

   12 

   13 private void target()

   14 {

   15     this.Text = "ddddd";

   16 }

 
שימו לב לשורה 4.
More Posts Next page »