چطوری با WPF یه ساعت آنالوگ بسازیم؟

آپدیت:چون کدها درست نمایش داده نمیشد کدهارو منتقل کردم به صفحه gist روی هر مرحله کلیک کنید منتقل میشید به کد موردنظربعد از چندین پست پشت سر هم در مورد کاتلین حالا نوبت به WPF میرسه، این سری میخوام بهتون نشون بدم که به کمک WPF یه ساعت آنالوگ طراحی کنید.شاید فکر کنید که ساخت همچین چیزی مشکل باشه ولی با WPF هیچی سخت نیستبرای شروع ما نیاز به 2 تا فایل داریم یه کلاس و یه فایل ریسورس دیکشنری، کلاس برای پیاده سازی متدها و پراپرتی ها و ریسورس دیکشنری برای استایل دادن به کنترلمون!خب پس اول از همه یه کلاس ایجاد میکنیم به اسم AnalogClock و از Control ارث بری میکنیم کلاس ما الان به این شکل هست:مرحله اولحالا یه فایل ResourceDictionary به اسم AnalogClockStyle ایجاد میکنیم و یه استایل توش تعریف میکنیم و خاصیت Template رو براش درنظر میگیریم و داخلش یه ControlTemplate قرار میدیم که وصلش میکنیم به کلاسی که ساختیم ساعتی که قراره بسازیم داخل این کنترل تمپلت قرار میگیرهمرحله اول استایلخوب حالا باید شروع کنیم به طراحی ساعتمون، توی ساعت آنالوگ ما 3 تا عقربه داریم به عنوان ساعت، دقیقه، ثانیه و 1 دایره که عقربه ها داخلش میچرخن برای عقربه ها میتونیم از شی Line و برای دایره از شی Ellipse استفاده کنیم پس من 3 تا Line و 1 Ellipse قرار میدم:مرحله دوم استایلخب پراپرتی های Line رو باهم مرور میکنیم که چیکار کردیم، StrokeThickness ضخامت خط رو مشخص میکنه که برای ساعت 2، برای دقیقه 1.5 و برای ثانیه 1 در نظر گرفتم.VerticalAlignment و HorizontalAlignment رو هم روی Center قرار دادم تا از لحاظ عمودی و افقی داخل پنل وسط قرار بگیرنپراپرتی Stroke هم رنگ اون شی رو مشخص میکنه که برای عقربه ساعت رنگ بنفش، دقیقه آبی و ثانیه قرمز رو درنظر گرفتم. شی Line 2 تا پراپرتی دیگه داره به اسم X1 و X2 که محل قرار گیری خط رو مشخص میکنن که همینطور که میبینید X1 همه عقربه ها 0 و X2 اونها متفاوت هست چون عقربه ساعت از همه کوتاه تره X2 ساعت رو کمتر از بقیه درنظر گرفتم و دقیقه و ثانیه هم اندازه!شی Ellipse چیز خاصی نداره فقط بهش ضخامت دادیم و طول و عرضش رو مشخص کردیم تا یه دایره با طول و عرض 210 داشته باشیم.چون قرار هست عقربه هارو بچرخونیم پس به شی های Line پراپرتی Name رو ست میکنیم که بتونیم از کلاسی که ایجاد کردیم بهشون دسترسی داشته باشیم. نکته ای که هست اینه که حتما باید اصول نامگذاری رعایت بشه تا بعدا کاربر بتونه تو برنامه خودش بدون دسترسی مستقیم به سورس استایل دلخواهش رو Override کنه، قاعده نام گذاری هم با حروف بزرگ PART_ شروع میشه و بعد از آندرلاین اسم دلخواه باید نوشته بشه. پس اسم شی های ما میشهPART_HourPART_MinutePART_Secondحالا برای اینکه عقربه ها وسط دایره قرار بگیرن و جهت ها درست باشه باید عقربه ها رو به کمک Transform بچرخونیمشون پس داخل پنل Grid کد زیر رو مینویسیم تا تمام محتوای اون 90 درجه بچرخهمرحله سوم استایلو برای اینکه کل محتوا درست نمایش داده بشه RenderTransformOrigin رو برای Grid مشخص میکنیم<Grid RenderTransformOrigin=&quot0.5, 0.5&quot>حالا کدهای استایل ما باید به این صورت باشهمرحله آخر استایلما تا اینجا کنترلمون رو طراحی کردیم و استایل مربوطه رو براش درنظر گرفتیم من بیشتر از این طراحیش نمیکنم گرچه شما میتونید استایل های دلخواهتون روش اعمال کنید (رنگ، پس زمینه، بزرگی، کوچیکی و....) حالا باید بریم سراغ کلاسمون و این عقربه هارو طبق ساعت بچرخونیمشون!اول از همه باید به اون شی ها دسترسی داشته باشیم پس به کمک TemplatePart به اون اشیا دسترسی پیدا میکنیم بالای کلاسمون به این صورت کد رو مینویسیم و اسم شی که در دیکشنری مشخص کردیم + نوع اون که Line هستش من برای اینکه بعدا مشکلی پیش نیاد اسم کنترل رو داخل متغیر ثابت نگهداری کردممرحله دوموقتی که ما شی رو از دیکشنری پیدا کردیم باید داخل یه شی دیگه از نوع خودش نگهداری کنیم پس برای هر کدوم یه شی دیگه من ایجاد میکنم:Line _hour, _minute, _second;حالا کافیه متد OnApplyTemplate رو override کنیم و اشیا خودمون رو داخلش پیدا کنیم:مرحله سومبه کمک متد Template.FindName میتونیم اشیا رو پیدا کنیم پس میگیم که:_hour = Template.FindName(PART_HOUR, this) as Line;حالا اون اشیایی که داخل کلاس ایجاد کردیم شامل همون اشیایی هست که داخل دیکشنری ایجاد کردیم و هر تغییری روش بدیم روی دیکشنری اعمال میشه!حالا کافیه که عقربه هارو روی دایره بچرخونیم:_hour.RenderTransform = new RotateTransform((DateTime.Now.Hour / 12.0) * 360, 0.5, 0.5);تو کد بالا ما اومدیم ساعت فعلی سیستم رو دریافت کردیم بعد تقسیم بر 12 کردیم چون ساعت داخل دایره به 12 قسمت تقسیم میشه، نتیجه رو در 360 که کل درجه دایره هست ضرب میکنیم تا درجه موردنظر ساعت رو پیدا کنیم دوتا عدد بعدی هم همون محل قرار گیری هستش که داخل دیکشنری برای RenderTransformOrigin مشخص کردیم. برای دقیقه و ثانیه هم به همین صورت هستش منتهی باید به جای 12.0 از 60.0 استفاده کنید چون دایره برای دقیقه به 60 قسمت و ثانیه 60 قسمت تقسیم میشه_minute.RenderTransform = new RotateTransform((DateTime.Now.Minute / 60.0) * 360, 0.5, 0.5);حالا که برنامه رو اجرا کنیم ساعت فعلی سیستم رو بهمون نشون میدهاما یه مشکلی هست و ثانیه ها و دقیقه ها اپدیت نمیشن پس برای حل این مشکل از تایمر استفاده میکنیم، یه تایمر میسازیم و میگیم که هر 1 ثانیه تابع چرخش عقربه هارو فراخوانی بکنهمرحله چهارمکد نهایی کلاسمون:مرحله آخرنتیجه:امیدوارم که از این آموزش خوشتون اومده باشه