اشتراک‌ها
معرفی math.js
یک کتابخانه‌ی جامع ریاضی نوشته شده برای JavaScript
معرفی math.js
مطالب
جایگزین کردن jQuery با JavaScript خالص - قسمت اول - یافتن عناصر
jQuery سال‌ها به عنوان جزء اصلی توسعه‌ی برنامه‌های وب مطرح بوده‌است و برای بسیاری از توسعه دهندگان وب، یک پیشنیاز پیش‌فرض محسوب می‌شود؛ ساده‌است، قابل فهم است و به آن اطمینان داریم. زمانیکه از آن استفاده می‌کنیم دیگر نیازی نیست تا آنچنان به DOM، باگ‌های مرورگرها و یا رفتارهای متفاوت آن‌ها فکر کنیم. jQuery تمام این مشکلات را برای ما حل می‌کند. اما ... اگر روزی باگی در jQuery وجود داشت، نیاز به امکاناتی بود که هنوز در jQuery ظاهر نشده‌اند و یا حتی اجازه‌ی استفاده‌ی از jQuery را نداشته باشیم، در این حالت ... وحشت زده و تقریبا بدون هیچ نوع آمادگی به نظر خواهیم رسید.
خالق جی‌کوئری (John Resig)، این کتابخانه را در سال‌های 2006 زمانیکه Internet Explorer نگارش‌های 6 و 7 بیش از 60 درصد بازار مرورگرها را به خود اختصاص داده بودند، ارائه داد. بله؛ در آْن زمان JavaScript Web API بسیار خام، پایداری مرورگرها بسیار پایین و تطابق با استانداردهای وب در بین مرورگرهای مختلف نیز بسیار پایین بود. بنابراین علت محبوبیت کتابخانه‌ای که در این شرایط، تجربه‌ی کاری یکدستی را در بین مرورگرهای مختلف ارائه می‌داد، کاملا واضح بود. اما ... اکنون سال 2018 است و حتی مایکروسافت هم دیگر از نگارش‌های مختلف IE پشتیبانی نمی‌کند. DOM API موجود در مرورگرهای مدرن بسیار توانمند شده‌اند و در بین انواع و اقسام آن‌ها یکدست عمل می‌کنند. حتی اگر دلیل استفاده‌ی از jQuery ایجاد ساده‌تر حلقه‌ها بر روی اشیاء جاوا اسکریپتی باشد (رفع کمبودهای جاوا اسکریپت)، از زمان IE 9 به بعد، متدهای forEach و Object.keys به صورت توکار در جاوا اسکریپت وجود دارند و یا اگر نیاز به inArray.$ داشته باشید، متد Array.prototype.indexOf مدت‌ها است که جزئی از ES5 است. به همین جهت است که این روزها اخباری را مانند «GitHub نیز جی‌کوئری را کنار گذاشت» زیاد می‌شنوید. نه فقط کنار گذاشتن jQuery یک وابستگی ثالث را از برنامه حذف می‌کند، بلکه کار مستقیم با native API مرورگرها همواره به مراتب سریعتر است از کتابخانه‌هایی که سطح بالایی از abstraction آن‌ها را ارائه می‌دهند.


یافتن عناصر توسط JavaScript خالص

زمانیکه نیاز به انتخاب عناصری در صفحه باشند، بلافاصله ذهن ما به سمت ('myElement#')$ و ('myElement.')$ جی‌کوئری، معطوف می‌شود. اما ... این روزها برای انجام این نوع کارها واقعا نیازی به jQuery نیست!


یافتن عناصر بر اساس ID آن‌ها
 <div id="my-element-id"></div>
اگر بخواهیم این شیء div را بر اساس ID آن در صفحه بیابیم، روش کار آن با jQuery به صورت زیر است:
 var result = $('#my-element-id');
در اینجا این ID selector string، یک استاندارد W3C CSS1 است.
انجام این کار توسط web API و یا همان JavaScript خالص، چنین شکلی را دارد:
 var result = document.getElementById('my-element-id');
و جالب است بدانید این روش از زمان IE 5.5 وجود داشته‌است.
روش دیگر انجام اینکار با JavaScript به صورت زیر است:
  var result = document.querySelector('#my-element-id');
این روش و متد querySelector که بسیار شبیه به نمونه‌ی جی‌کوئری ارائه شده‌است، از زمان IE 8.0 قابل استفاده‌است.
در هر دو حالت، خروجی مقایسه‌ی ذیل، true است:
 result.id === 'my-element-id'; // returns true


یافتن عناصر بر اساس کلاس‌های CSS

<span class="some-class"></span>
با جی‌کوئری:
 var result = $('.some-class');
با جاوا اسکریپت خالص از زمان IE 8.0
  var result = document.getElementsByClassName('some-class');
و یا توسط querySelectorAll که شبیه به نمونه‌ی jQuery است و نیاز به پیشوند . را دارد:
 var result = document.querySelectorAll('.some-class');
در هر دو حالت، خروجی بازگشت داده شده، یک آرایه است:
 result[0].className === 'some-class'; // returns true


یافتن عناصر بر اساس تگ‌های عناصر

 <code>Console.WriteLine("Hello world!");</code>
با جی‌کوئری:
 var result = $('code');
با جاوا اسکریپت:
 var result = document.getElementsByTagName('code');
و یا
 var result = document.querySelectorAll('code');
در تمام این حالات، خروجی ارائه شده یک آرایه است:
 result[0].tagName === 'code'; // returns true



یافتن عناصر بر اساس کلاس نماها (Pseudo-classes)

Pseudo-classes از زمان ابتدایی‌ترین پیش‌نویس استاندارد CSS وجود داشته‌اند. برای مثال visited: یک Pseudo-classes است و به لینک‌های بازدید شده‌ی توسط کاربر اشاره می‌کند و یا focus: به المانی اشاره می‌کند که هم اکنون دارای focus است.
  <form>
     <label>Full Name
        <input name="full-name">
     </label>
     <label>Company
        <input name="company">
     </label>
  </form>
در این مثال اگر بخواهیم تکست باکسی را بیابیم که دارای focus است، روش جی‌کوئری آن به صورت زیر است:
  var focusedInputs = $('INPUT:focus');
و روش جاوا اسکریپتی آن به این صورت:
 var companyInput = document.querySelector('INPUT:focus');
کاری که در اینجا انجام شده ترکیب یک tag name و یک pseudo-class modifier است که جزئی از استاندارد CSS می‌باشد. بنابراین روش جی‌کوئری، چیزی بیشتر از انتقال این استاندارد به توابع بومی مرورگر نیست.


یافتن عناصر بر اساس ارتباط والد و فرزندی آن‌ها
  <div>
     <a href="https://www.dntips.ir">
        <span>Go to site</span>
     </a>
     <p>Some text</p>
     Some other text
  </div>
یافتن والدها:
روش یافتن والد anchor tag در جی‌کوئری توسط متد parent؛ با فرض اینکه a$ به شیء anchor اشاره می‌کند:
 var $result = $a.parent();
و در جاوا اسکریپت توسط خاصیت parentNode:
 var result = a.parentNode;

یافتن فرزندان:
در جی‌کوئری:
 var result = $('#myParent').children();
و برای یافتن فرزندان یک المان توسط CSS 2 child selectors:
 var result = document.querySelectorAll('DIV > *');
خروجی این کوئری، المان‌های a و p هستند و یا اگر فقط بخواهیم pها را انتخاب کنیم:
  var result = document.querySelectorAll('DIV > P');
روش دیگر انجام اینکار استفاده از خاصیت childNodes یک المان است:
 var result = document.getElementById('myParent').childNodes;
var result = div.childNodes;
البته این خاصیت آرایه‌ای، Text و Comments را هم علاوه بر عناصر بازگشت می‌دهد. البته اگر می‌خواهید آن‌ها را حذف کنید، از خاصیت children استفاده کنید:
 var result =document.getElementById('myParent').children;
و یا یافتن تمام المان‌های anchor ذیل المانی با Id مساوی myParent:
 var result =document.querySelectorAll('#myParent A');



جستجوی عناصر با صرفنظر کردن از بعضی از آن‌ها
  <ul role="menu">
     <li>choice 1</li>
     <li class="active">choice 2</li>
     <li>choice 3</li>
  </ul>
در این مثال گزینه‌ی دوم دارای class مساوی active است. اگر بخواهیم تمام liهایی را که دارای این کلاس نیستند، انتخاب کنیم، در جی‌کوئری خواهیم داشت:
 var $result = $('UL LI').not('.active');
و در جاوا اسکریپت:
 var result = document.querySelectorAll('UL LI:not(.active)');
هرچند JavaScript دارای متد not جی‌کوئری نیست، اما می‌توان از W3C CSS3 negation pseudo-class بجای آن استفاده کرد. مزیت آن، استاندارد بودن و عدم نیاز به کتابخانه‌ای ثالث برای تدارک آن است.


انتخاب چندین المان با هم

  <div id="link-container">
     <a href="https://github.com/VahidN">GitHub</a>
  </div>
  <ol>
     <li>one</li>
     <li>two</li>
  </ol>
  <span class="my-name">VahidN</span>
در اینجا می‌خواهیم المان‌های link-container، my-name و لیست مرتب شده را بدون نوشتن حلقه‌ای انتخاب کنیم. روش انجام اینکار در jQuery به صورت زیر است:
 var $result = $('#link-container, .my-name, OL');
و در جاوا اسکریپت خواهیم داشت:
 var result = document.querySelectorAll('#link-container, .my-name, OL');

یافتن گروهی از المان‌ها بر اساس نوع آن‌ها:
 var result = document.querySelectorAll(
 'BUTTON[type="submit"], INPUT[type="submit"]'
);
در اینجا تمام المان‌های ورودی از نوع <"button type="submit> و <"input type="submit> را بازگشت می‌دهد.


جایگزین کردن $ جی‌کوئری با جاوا اسکریپت

تا اینجا حتما به شباهت کدهای خالص جاوا اسکریپت و jQuery دقت کرده‌اید. اگر بخواهیم برای $ جی‌کوئری، یک معادل جاوا اسکریپتی تهیه کنیم، به قطعه کد زیر خواهیم رسید:
window.$ = function(selector) {
     return document.querySelectorAll(selector);
};
و نحوه‌ی استفاده‌ی از آن نیز همانند قبل است:
  $('.some-class');
  $('#some-id');
  $('.some-parent > .some-child');
  $('UL LI:not(.active)');
اشتراک‌ها
Code Push سرویس ابری مایکروسافت
یکی از مشکلاتی که همیشه برنامه نویسان موبایل با آن درگیر بوده اند بروز رسانی نرم افزارهای موبایل می‌باشد. هر بروز رسانی نرم افزار نیاز به طی شدن مراحل تایید App Store‌ها دارد که این امر در بروز رسانی نرم افزارها تاخیر ایجاد می‌کند و امکان رفع سریع مسایل نرم افزار را به تولید کنندگان نمی‌دهد. Code Push سرویسی ابری است که مایکروسافت ارائه می‌دهد تا با آن نرم افزارهای موبایل نصب شده برای کاربران بدون نیاز به طی شدن این مراحل بروزرسانی شود. این سرویس برای نرم افزارهای موبایل مبتنی بر React Native و Cordova طراحی شده است که در آن بخش HTML و JavaScript نرم افزار به لحظه بروزرسانی می‌شود. 
Code Push سرویس ابری مایکروسافت
نظرات مطالب
خلاصه‌ای کوتاه در مورد WinRT
در مورد این HTML که در بالا در مورد آن صحبت شد لازم است بیشتر توضیح داده شود:
برخلاف تصور عموم برنامه‌های HTML/CSS/JavaScript ویندوز 8 منحصرا برای WinRT تهیه می‌شوند و ... و ... قابل انتقال به سایر سکوهای کاری «نیستند». این‌ها از API مخصوص WinRT استفاده می‌کنند تا معنا پیدا کنند. بنابراین این مورد اصلا web programming متداول نیست. به آن‌ باید به چشم یک  standalone Windows 8 app نگاه کرد.
اشتراک‌ها
استفاده از LINQ در JavaScript
One of the more awesome things I like about being a .NET developer is LINQ. LINQ (Language Integrated Query) is a fluent query interface implemented in the .NET framework. It helps you query, sort and order any type of collection. This is a very neat way of querying arrays, lists, dictionaries, objects, etc. I’ve made 5 examples which run out of the box with Node.js (or io.js). You can also use the library for browser based JavaScript projects.
استفاده از LINQ در JavaScript
مطالب
وضعیت فناوری‌های مرتبط با دات نت از دیدگاه مرگ و زندگی!

مطلبی که در ذیل آورده شده صرفا یک برداشت شخصی است بر اساس نقل قول‌ها و بررسی وضعیت اعضای تیم‌های مرتبط با فناوری‌های مختلف بکار گرفته شده در دات نت فریم ورک و ... نه رسمی!

ADO.NET ، DataSet ، DataTable و امثال آن: مرده! (مرده به معنای اینکه دیگر توسعه‌ی جدی نخواهد یافت)
ADO.NET‌ اولین فناوری دسترسی به داده‌ها در دات نت فریم ورک بود/است. مدل طراحی آن هم بر اساس امکانات زبان‌های آن زمان (زمان شروع به کار دات نت) بود (و تا دات نت 4 هم تغییر عمده‌ای نکرده). برای مثال در زمان ارائه اولین نگارش آن خبری از Generics نبود (در دات نت 2 اضافه شد)؛ یا LINQ وجود نداشت (در دات نت سه و سه و نیم اضافه و تکمیل شد). به همین جهت طراحی آن در حال حاضر (با وجود دات نت 4) بوی ماندگی می‌دهد (مانند استفاده از دیتاست و دیتاتیبل) و با ORM های مایکروسافت جهت استفاده از امکانات Generics و LINQ جایگزین شده‌ است.
البته این مورد تنها مورد مرده‌ای است که "باید" یاد گرفت؛ مهم نیست که ORMs ارائه شده‌اند. مهم این است که زیر ساخت تمام ORM های نوشته شده برای دات نت همین ADO.NET خام است.


LINQ to SQL : مرده!
مایکروسافت با این فناوری ORM های خودش را شروع کرد اما بعد از مدتی دید که بهتر است یک نسخه‌ی عمومی‌تر با پشتیبانی از بانک‌های اطلاعاتی دیگر مانند اوراکل، MySQL و غیره را نیز ارائه دهد. همینجا بود که آن‌را خیلی ساده با Entity framework جایگزین کرد و در roadmap ارائه شده صراحتا EF به عنوان راه حل توصیه شده دسترسی به داده‌های مایکروسافت اعلام شده است (+). حالا این وسط دیگر مهم نیست شما پروژه نوشته بودید یا هر چی. دیگر منتظر تغییرات خاصی در LINQ to SQL نباشید. فقط یک سری رفع باگ و نگهداری پروژه را شاهد خواهید بود. البته در همان زمان خیلی زود تکذیب کردند که LINQ to SQL مرده اما برای نمونه آقای Damien که عضو اصلی تیم LINQ to SQL بودند، اکنون در تیم XBOX مشغول به کار هستند! (+) تو خود شرح مفصل بخوان از این مجمل!

ضمنا این رو هم در نظر داشته باشید که LINQ != LINQ to SQL ؛ به عبارتی LINQ به خودی خود فقط یک language feature است.


Windows Forms یا به اختصار WinForms : مرده!
به نظر مظلوم‌تر از این یکی در دات نت یافت نمی‌شود! همین چند وقت پیش یکی از اعضای مایکروسافت این نظر سنجی رو برگزار کرده بود که "ما چکار کنیم که شما راحت‌تر از WinForms به WPF مهاجرت کنید؟!" (+)
در قاموس WPF ، ویندوز فرمز یعنی Canvas panel ؛ به عبارتی صلب‌ترین حالت طراحی رابط کاربری و این انتقال و مهاجرت هرچند برای کسانی که عمری را روی آن گذاشته‌اند، دردناک خواهد بود اما با وجود توانایی‌های WPF چیزی را از دست نخواهند داد. سیستم Layout (طرح بندی) در WinForms و همچنین دلفی، بر اساس قراردهی اشیاء در مختصات مطلقی در صفحه است. مثلا این دکمه‌ی خاص در آن نقطه‌ی معلوم قرار می‌گیرد و همین. این روش طرح بندی یکی از چندین روش طرح بندی در WPF است که اصطلاحا Canvas نام دارد. توصیه اکید و مطلق در WPF آن است که از Canvas فقط برای طراحی اشیاء گرافیکی پیچیده استفاده کنید و نه طراحی رابط کاربر. چرا؟ چون برای مثال در Silverlight که برادر کوچکتر WPF محسوب می‌شود، رابط کاربری آن باید بتواند همانند HTML انعطاف پذیر باشد و با اندازه‌های مختلف مرورگر یا اندازه‌ی قلم‌های بزرگتر هماهنگ شده و مقاومت کند، بدون اینکه از ریخت بیفتد و این مورد را سایر سیستم‌های طرح بندی رابط کاربر (منهای Canvas یا همان سیستم طرح بندی WinForms) ارائه می‌دهند. مورد دیگری که در WPF و Silverlight بسیار حائز اهمیت است ، Content Controls می‌باشد. چقدر خوب می‌شد بتوان داخل یک کنترل، کلا یک سیستم طرح بندی را تعریف کرد و اشیاء دلخواهی را داخل آن قرار داد. مثلا ToolTip پیش فرض وجود دارد. بسیار هم خوب. بنده علاقه دارم، متن عنوان آن ضخیم باشد. کنار آن یک تصویر کوچک و در سمت چپ آن متن قرار گیرد. برای انجام اینکار در WPF لازم نیست تا شما منتظر نگارش بعدی دات نت باشید که دست اندرکاران مربوطه با افتخار در یک سند 50 صفحه‌ای توضیح دهند که چگونه می‌توان اینکار را انجام داد. یک سیستم طرح بندی را اضافه کنید. موارد ذکر شده را در آن تعریف کنید. بدون استفاده از هیچ نوع کامپوننتی یا بدون منتظر ماندن تا نگارش بعدی این محصولات، به مقصود خود رسیده‌اید.


ASP.NET Web forms : داره نفس‌های آخرش رو می‌کشه!
از زمانیکه ASP.NET MVC آمد نسخه‌ی Web forms تقریبا فراموش شد. به وبلاگ اسکات گاتری یا Haacked و سایر اعضای اصلی دات نت که مراجعه کنید در سه سال اخیر در حد تعداد انگشتان یک دست هم در مورد Web forms مطلب ننوشته‌اند. تمام تمرکز و نوآوری‌ها بر روی MVC متمرکز شده و حتی در نسخه‌ی 4 دات نت هم فقط یک سری صافکاری مختصر را در Web forms شاهد بودیم مثلا نام کنترل‌ها را خودتان هم در زمان رندر نهایی می‌توانید تعیین کنید! یا لطفا کردند و قسمتی از url routing موجود در ASP.NET MVC را به ASP.NET web forms 4 هم قرض دادند (این مورد شاید مهم‌ترین تغییر قابل ذکر در ASP.Net web forms 4 است).
البته این رو هم اضافه کنم که ASP.NET MVC‌ واقعا قابل احترام است؛ هدف از آن جدا سازی لایه‌های برنامه با الگوهای استاندارد صنعتی (و نه هر روش برنامه نویسی چند لایه من درآوردی)، ترویج کد نویسی بهتر، ترویج Unit testing ، رفع یک سری مشکلات ASP.NET Web forms (مثلا از ViewState های حجیم دیگر خبری نیست) و امثال آن است.
برای نمونه توجه شما را به مطلبی که آقای Dino Sposito در مورد ASP.NET Webforms نوشته جلب می‌کنم: (+)
به صورت خلاصه ایشان عنوان کرده زمان طراحی ASP.NET Webforms در 10 سال قبل، هدف ما انتقال ساده‌تر برنامه نویس‌های VB به محیط وب بود. به همین جهت دست به اختراع postback ، viewState ، کنترل‌های سمت سرور و غیره زدیم. (بنابراین تاکید تمام این‌ها این است که webforms فناوری دهه قبل "بود" و الان بنابر نیازهای جدید دست به طراحی مجدد زده‌ایم)

در مورد "پایان پروژه ASP.NET Ajax Control Toolkit" هم قبلا مطلب نوشته بودم و این یکی واقعا official است!


و در پایان باید متذکر شد که فلان فناوری مرده یا داره نفس‌های آخرش رو می‌کشه اصلا مهم نیست. هنوز هم هستند سازمان‌هایی که برنامه‌های نوشته شده با ASP کلاسیک (نگارش قبل از ASP.NET Web forms) خود را دارند و خیلی هم از آن راضی هستند!
این موارد رو از این جهت نوشتم که اگر می‌خواهید تازه به این جمع وارد شوید دقیقا بدانید باید روی چه مواردی بیشتر وقت بگذارید و یادگیری کدامیک صرفا اتلاف وقت خواهند بود (مثلا EF بر LINQ to SQL ارجح است و اگر امروز می‌خواهید شروع کنید با EF شروع کنید، یا دیگر کم کم با وجود WPF ، بازار کاری برای WinForms نخواهد بود).

نظرات مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
امکان تبدیل رخدادهای توکار مرورگرها به دایرکتیوهای Blazor در Blazor6x

یکسری دایرکتیو مانند onclick@ و امثال آن، از پیش در Blazor تعریف شده‌اند که امکان مدیریت رویدادهای جاوااسکریپتی را در کدهای سی‌شارپ میسر می‌کنند. اما تعداد این‌ها زیاد نیست. برای مثال تعداد رویدادهای قابل تعریف و پشتیبانی شده‌ی توسط مرورگرها قابل ملاحظه‌است. در Blazor 6x روشی جهت دسترسی ساده‌تر به این رویدادها ارائه شده‌است که شامل این مراحل است. برای نمونه فرض کنید می‌خواهیم به رویداد paste مرورگر دسترسی پیدا کنیم و یک دایرکتیو سفارشی oncustompaste@ را برای آن تهیه کنیم:
<input @oncustompaste="HandleCustomPaste" />
برای اینکار در ابتدا قطعه کد زیر را پس از blazor.webassembly.js در فایل index.html ثبت می‌کنیم (یا می‌توان از روش export function afterStarted که در بالا عنوان شد هم استفاده کرد):
<script> 
    Blazor.registerCustomEventType('custompaste', { 
        browserEventName: 'paste', 
        createEventArgs: event => { 
            // This example only deals with pasting text, but you could use arbitrary JavaScript APIs 
            // to deal with users pasting other types of data, such as images 
            return { 
                eventTimestamp: new Date(), 
                pastedData: event.clipboardData.getData('text') 
            }; 
        } 
    }); 
</script>
در اینجا برای رویداد paste مرورگر، تعدادی آرگومان تهیه شده و بازگشت داده می‌شود. آرگومان اول در اینجا یک مقدار اختیاری و نمایشی‌است و آرگومان دوم به شیء رویداد paste، دسترسی یافته و متن آن‌را بازگشت می‌دهد.
پس از اینکار، معادل دو پارامتر بازگشت داده شده را به صورت زیر در کدهای سی‌شارپ تهیه می‌کنیم:
namespace BlazorCustomEventArgs.CustomEvents 
{ 
    [EventHandler("oncustompaste", typeof(CustomPasteEventArgs), enableStopPropagation: true, enablePreventDefault: true)] 
    public static class EventHandlers 
    { 
        // This static class doesn't need to contain any members. It's just a place where we can put 
        // [EventHandler] attributes to configure event types on the Razor compiler. This affects the 
        // compiler output as well as code completions in the editor. 
    } 
 
    public class CustomPasteEventArgs : EventArgs 
    { 
        // Data for these properties will be supplied by custom JavaScript logic 
        public DateTime EventTimestamp { get; set; } 
        public string PastedData { get; set; } 
    } 
}
ابتدا از EventArgs ارث‌بری شده و معادل تاریخ و متن بازگشت داده شده، تبدیل به یک EventArgs سفارشی می‌شود. سپس نوع آن، به ویژگی EventHandler ای که بالای یک کلاس استاتیک خالی قرار گرفته شده، ارسال می‌شود. اینکار صرفا جهت اطلاع کامپایلر صورت می‌گیرد.
یک نکته: در اینجا نام oncustompaste به همان نام custompaste کدهای جاوااسکریپتی اشاره می‌کند. نام تعریف شده‌ی در قسمت سی‌شارپ، یک on در ابتدا اضافه‌تر دارد. اینکار سبب می‌شود که اکنون بتوان یک رویدادگردان oncustompaste@ سفارشی را که قابل مدیریت در کدهای سی‌شارپ است، داشت:
@page "/"

<p>Try pasting into the following text box:</p>
<input @oncustompaste="HandleCustomPaste" />
<p>@message</p>

@code {
    string message;
    void HandleCustomPaste(CustomPasteEventArgs eventArgs)
    {
        message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, you pasted: {eventArgs.PastedData}";
    }
}