۱۳۸۹/۰۶/۲۹

نكته‌اي مهم در طراحي قالب‌ برنامه‌هاي Silverlight


قالب سيلورلايتي را ايجاد كرده بودم و IE در حالت نمايش عادي اين قالب 30 درصد CPU Usage ثابت داشت. علت را هم متوجه نمي‌شدم؛ چون در اين حالت اصلا كدي وجود نداشت كه بخواهد CPU Usage ايي را ايجاد كند. يك سري كد XAML جهت نمايش قالب در كنار هم قرار گرفته بودند و همين.
تا اينكه ديروز در وبلاگ رسمي مرتبط با كارآيي برنامه‌هاي Silverlight مطلبي منتشر شد كه دقيقا مشكل طراحي قالب من هم همان بود:

خلاصه آن:
اگر در حالت نمايش برنامه Silverlight شما (بدون اينكه كدي در حال اجرا باشد) به صورت ثابت CPU Usage بالايي را مشاهده مي‌كنيد، پارامتر enableRedrawRegions تگ بارگذاري افزونه‌ي Silverlight را به true مقدار دهي كنيد.
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2">
...
<param name="enableRedrawRegions" value="true"/>
...
</object>

پس از فعال سازي اين گزينه، برنامه را اجرا كنيد. نواحي را كه مرتبا در حال ترسيم مجدد هستند، با رنگ‌هاي آبي، زرد و صورتي مشاهده خواهيد كرد.
در اين حالت اگر ناحيه‌اي مرتبا در حال به روز رساني مشاهده گرديد، دقيقا همين ناحيه است كه سبب CPU Usage بالا و ثابت برنامه شما شده است و بايد فكري به حال آن كرد.

چه زماني ممكن است اين حالت (ترسيم‌هاي مجدد بدون پايان) رخ دهد؟
عموما اين مشكل در حين استفاده ناصحيح از افكت‌هاي پيش فرض Silverlight مانند DropShadowEffect رخ مي‌دهد. براي مثال مي‌خواهيد قسمتي از قالب شما سايه دار باشد اما نحوه‌ي اعمال اين سايه بسيار مهم است.
        <Border CornerRadius="3">
<!--High cpu usage-->
<Border.Effect>
<DropShadowEffect BlurRadius="7" Color="#FF1E2224" Opacity="3" ShadowDepth="6" Direction="200" />
</Border.Effect>
<StackPanel>
...
...

در اين مثال ابتدا يك border تعريف شده و سپس سايه‌اي به آن اعمال گرديده است. سپس داخل اين Border يك StackPanel قرار گرفته است به همراه يك سري از اشياء زير مجموعه آن. اين كار غلط است! همين مورد به ظاهر ساده 30 درصد CPU Usage ثابت را در برنامه ايجاد كرده بود.

علت چيست؟
با اين نحوه‌‌ي تعريف اشتباه DropShadowEffect ، هر نوع تغيير بصري در مجموعه‌ي Border و StackPanel داخل آن، سبب ترسيم مجدد كل ناحيه مي‌گردد. اين تغيير بصري حتي مي‌تواند شامل چشمك زدن يك cursor درون يك TextBox در قسمتي از اين ناحيه نيز باشد كه با استفاده از ويژگي enableRedrawRegions ، اين مورد را به خوبي مي‌توان مشاهده نمود.

راه حل اين مساله كدام است؟
از دو Border استفاده كنيد. يك Border با ضخامت كم تنها براي نمايش سايه (كه داراي هيچ نوع شيء فرزندي نيست) و Border و StackPanel قبلي هم به همان صورت ابتدايي (البته با حذف DropShadowEffect از آن) باقي بماند.