مطالب
مقایسه بین Proxy و ChannelFactory در WCF
برای ساخت یک WCF Client یا دسترسی به یک سرویس WCF دو راه وجود دارد.
  • استفاده از WCF Proxy
  • استفاده از ChannelFactory

قصد دارم طی یک مقایسه کوتاه این دو روش را بررسی کنیم:

WCF Proxy:

Proxy در واقع یک کلاس CLR است که به عنوان نماینده یک اینترفیس که از نوع  Service Contract است مورد استفاده قرار می‌گیرد. یا به زبان ساده تر، یک Proxy در واقع نماینده Service Contract ای که سمت سرور پیاده سازی شده است در سمت کلاینت خواهد بود. Proxy تمام متد یا Operation Contract‌های سمت سرور را داراست به همراه یک سری متد‌ها و خواص دیگر برای مدیریت چرخه طول عمر سرویس،  هم چنین اطلاعات مربوط به وضعیت سرویس و نحوه اتصال آن به سرور. ساخت Proxy به دو روش امکان پذیر است:

  • با استفاده از امکانات AddServiceReference موجود در Visual Studio. کافیست از پنجره معروف زیر با استفاده از یک URL سرویس مورد نظر را به پروژه سمت کلاینت خود اضافه نمایید

همچنین  می‌توانید از قسمت Advanced نیز برای تنظیمات خاص مورد نظر خود(مثل تولید کد برای متد‌های Async یا تعیین نوع Collection‌ها در هنگام انتقال داده و ...) استفاده نمایید.

  • با استفاده از SvcUtil.exe . کاربرد svcutil.exe در موارد Metadata Export، Service Validtation، XmlSerialization Type Generator و Metadata Download و ... خلاصه می‌شود. با استفاده از Vs.Net Command Promptو svcutil می‌توان به سرویس مورد نظر دسترسی داشت.مثال
    svcutil.exe /language:vb /out:generatedProxy.vb /config:app.config http://localhost:8000/ServiceModelSamples/service

ChannelFactory:

ChannelFactory یک کلاس تعبیه شده در دات نت می‌باشد که به وسیله یک اینترفیس که به عنوان تعاریف سرویس سمت سرور است یک نمونه از سرویس مورد نظر را برای ما خواهد ساخت. اما به خاظر داشته باشید از این روش زمانی می‌توان استفاده کرد که دسترسی کامل به سمت سرور و کلاینت داشته باشید.

برای آشنایی با نحوه پیاده سازی به این روش نیز می‌توانید از این مقاله کمک بگیرید.

مثال:

public static TChannel CreateChannel()
        {
            IBookService service;

            var endPointAddress = new EndpointAddress( "http://localhost:7000/service.svc" );

            var httpBinding = new BasicHttpBinding();
            
            ChannelFactory<TChannel> factory = new ChannelFactory<TChannel>( httpBinding, endPointAddress );

            instance= factory.CreateChannel();

            return instance;
        }
همان طور که مشاهده می‌کنید در این روش نیاز به یک EndpointAddress به همراه یک نمونه از نوع Binding مورد نظر دارید. نوع این Binding حتما باید با نوع نمونه ساخته شده در سمت سرور که برای هاست کردن سرویس‌ها مورد استفاده قرار گرفته است یکی باشد. این نوع‌ها می‌تواند شامل NetTcpBidning ،WShttpBinding  BasicHttpBinding ،WSDualHttpBinding، MSMQ Binding و البته چند نوع دیگر نیز باشد.
در نتیجه برای ساخت یک سرویس به روش ChannelFactory باید مراحل زیر را طی نمایید:
  • یک نمونه از WCF Binding بسازید
  • یک نمونه از کلاس EndPointAddress به همراه آدرس سرویس مورد نظر در سمت سرور بسازید(البته می‌توان این مرحله را نادیده گرفت و آدرس سرویس را مستقیما به نمونه ChannelFactory به عنوان پارامتر پاس داد)
  • یک نمونه از کلاس ChannelFactory یا استفاده از EndPointAddress بسازید
  • با استفاده از ChannelFactory یک نمونه از Channel مورد نظر را فراخوانی نمایید(فراخوانی متد CreateChannel)

تفاوت‌های دو روش

Proxy
 ChannelFactory
فقط نیاز به یک URL برای ساخت سرویس مورد نظر دارد. بقیه مراحل توسط ابزار‌های مرتبط انجام خواهد شد  
 شما نیاز به دسترسی مستقیم به اسمبلی حاوی Service Contract پروژه خود دارید.
 استفاده از این روش بسیار آسان و ساده است
 پیاده سازی آن پیچیدگی بیشتر دارد
فهم مفاهیم این روش بسیار راحت است
نیاز به دانش اولیه از مفاهیم WCF برای پیاده سازی دارد
 زمانی که میزان تغییرات در کلاس‌های مدل و Entity‌ها زیاد باشد این روش بسیار موثر است.(مدیریت تغییرات در WCF)
 زمانی که اطمینان دارید که مدل و entity‌ها پروژه زیاد تغییر نخواهند کرد و از طرفی نیاز به کد نویسی کمتر در سمت کلاینت دارید، این روش موثرتر خواهد بود
 فقط به اینترفیس هایی که دارای ServiceContractAttribute هستند دسترسی خواهیم داشت.
به تمام اینترفیس‌های تعریف شده در بخش  Contracts دسترسی داریم.
 فقط به متد‌های که دارای OperationContractAttribute هستند دسترسی خواهیم داشت.    به تمام متد‌های عمومی سرویس دسترسی داریم.  

آیا می‌توان از روش AddServiceReference تعبیه شده در Vs.Net، برای ساخت ChannelFactory استفاده کرد؟

بله! کافیست هنگام ساخت سرویس، در پنجره AddServiceReference از قسمت Advanced وارد برگه تنظیمات شوید. سپس تیک مربوط به قسمت های  Reused Type in referenced assemblies  و Reused Types in specified referenced assemblies را بزنید. بعد از لیست پایین، اسمبلی‌های مربوط به Domain Model و هم چنین Contract‌های سمت سرور را انتخاب نمایید. در این حالت شما از روش Channel Factory برای ساخت سرویس WCF استفاده کرده اید.

مطالب
استفاده از ItemsControl جهت ساختن کنترل های پویا در WPF

از ItemsControl برای ارائه مجموعه ای از کنترل‌ها استفاده می‌شود،در اینجا قرار است از آن استفاده کنیم و یک کنترل پویا ایجاد کنیم.برای مثال در نظر بگیرید،قرار است یک DropDownPanel ایجاد کنیم و در جاهای مختلف برنامه کنترل‌های مختلفی را درون آن  قرار بدهیم.برای ایجاد آن به صورت زیر عمل میکنیم:

<UserControl x:Class="MySystem.Common.Controls.DropDownPanel"
             …              
             x:Name="This">
    <Grid>
        <ToggleButton x:Name="ShowPopupButton"/>
        <Popup
            PlacementTarget="{Binding ElementName=ShowPopupButton}"
            Placement="{Binding PopupPlacement, ElementName=this}"
            PopupAnimation="Slide"
            AllowsTransparency="True"
            Focusable="True" 
            StaysOpen="False"
            IsOpen="{Binding IsChecked, ElementName=ShowPopupButton }">
            <Border Background="#FFE3EAF3" BorderThickness="1" Padding="2">
                <Grid>
                    <ItemsControl ItemsControl.ItemsSource="{Binding Path=PanelItems,ElementName=This }">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <Grid>

                                </Grid>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>
                </Grid>
            </Border>
        </Popup>
    </Grid>
</UserControl>

همانگونه که در کد بالا می‌بینید ،برای ایجاد DropDownPanel  از یک ToggleButton و یک Popup که خصوصیت IsOpenآن به IsChecked مربوط به ToggleButton وصل شده است، استفاده کردیم و در قسمت بدنه کنترل به جای قراردان کنترل هایی که قرار است در آن نمایش داده شوند،از یک ItemsControl استفاده کردیم که خصوصیت ItemsSource آن به یک خصوصیت پیوست شده از نوع ObservableCollection<UIElement>در Code Behind، مقید شده است.تعریف این خصوصیت پیوست شده به صورت زیر است:

  public static readonly DependencyProperty PanelItemsProperty = DependencyProperty.Register("PanelItems"
            , typeof(ObservableCollection<UIElement>)
            , typeof(DropDownPanel)
            , new PropertyMetadata(new ObservableCollection<UIElement>()));
        public ObservableCollection<UIElement> PanelItems
        {
            get
            {
                return (ObservableCollection<UIElement>)GetValue(PanelItemsProperty);
            }
            set
            {
                SetValue(PanelItemsProperty, value);
            }
        }

برای استفاده از کنترل یک وهله از این کنترل را ایجاد می‌کنیم و کنترل هایی که قرار است درDropDownPanelنمایش داده شوندرا بهPanelItems  اضافه میکنیم:

<Window x:Class="MySystem.UI.View.Window1"
              ….
             xmlns:controls ="clr-namespace:MySystem.Common.Controls;assembly=GoldAccountingSystem.Common.Controls">
    <Grid>
        <controls:DropDownPanel>
            <controls:DropDownPanel.PanelItems>
                    !--Put Controls Here--!
            </controls:DropDownPanel.PanelItems>
        </controls:DropDownPanel>
    </Grid>
</Window>

تا این مرحله کنترل مورد نظر را ایجاد و استفاده کردیم.اما یک مشکل وجود دارد،چنانچه از این کنترل چند بار در یک فرم استفاده شود، به درستی عمل نمیکند به اینصورت که فرزندان PanelItems تمام شی‌های ساخته شده از کنترل در یک فرم برابر هم و برابر مقداری می‌شود که برای آخرین کنترل قرارداده ایم. دلیل این امر این است که ما یکبار در هنگام تعریف خصوصیت PanelItems یک وهله از آن را به عنوان مقدار پیش فرض ایجاد کردیم و برای همه‌ی نمونه هایی از کنترل که در فرم قرار می‌گیرند از همان وهله استفاده می‌شود.

برای حل مشکل فوق یک کلاس از نوع ObservableCollection<UIElement> ایجاد کرده و هنگام ساختن کنترل در فرم از این کلاس برای وهله سازی مجدد از PanelItems استفاده می‌کنیم:

namespace MySystem.Common.Controls
{
    public class UIElementCollection : ObservableCollection<UIElement> { }
}

همانطور که گفته شد از کلاس ایجاد شده برای وهله سازی به صورت زیر استفاده می‌شود:

<Window x:Class="MySystem.UI.View.Window1"
              ….
             x:Name="This"
             xmlns:controls ="clr-namespace:MySystem.Common.Controls;assembly=GoldAccountingSystem.Common.Controls">
    <Grid>
        <commonControls:DropDownPanel>
            <commonControls:DropDownPanel.PanelItems>
                <commonControls:UIElementCollection>
                    !--Put Controls Here--!
                </commonControls:UIElementCollection>
            </commonControls:DropDownPanel.PanelItems>
            </commonControls:DropDownPanel>
    </Grid>
</Window>
یک مثال ساده از توضیحات بالا را از آدرس روبرو می‌توانید دریافت نمایید: ItemsControlTest-a349625a156d4aeaaca288f000ae6d7a.rar  
مطالب
بهبود SEO در ASP.NET MVC
گوگل خلاصه نتایج Indexing یک سایت را توسط ابزاری به نام Google webmaster tools در اختیار علاقمندان قرار می‌دهد. Bing نیز چنین ابزاری را تدارک دیده است.
به آمارهای خطای حاصل از سایت جاری که دقت می‌کردم یک نکته آن جالب بود: «محتوای تکراری»


mydomain.com/Home/Index
mydomain.com/home/index
mydomain.com/Home/index
mydomain.com/home/Index
همانطور که ملاحظه می‌کنید، گوگل به کوچکی و بزرگی حروف بکار رفته در لینک‌ها حساس است. هرچند 4 لینک فوق به یک صفحه اشاره می‌کنند، اما گوگل 4 بار آن‌ها را ایندکس خواهد کرد و نهایتا به صورت یک خطای «محتوای تکراری» در گزارشات SEO آن ظاهر خواهد شد (به همراه کاهش رتبه SEO سایت).

راه حل

برای حل این مساله دو نکته باید درنظر گرفته شود:
الف) هدایت دائمی (Redirect permanent) صفحات قدیمی به صفحاتی جدید، با آدرس lowercase

using System.Globalization;
using System.Web;
using System.Web.Mvc;

namespace WebToolkit
{
    public class ForceWww : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            modifyUrlAndRedirectPermanent(filterContext);
            base.OnActionExecuting(filterContext);
        }

        private static void modifyUrlAndRedirectPermanent(ActionExecutingContext filterContext)
        {
            if (canIgnoreRequest(filterContext))
                return;

            var absoluteUrl = HttpUtility.UrlDecode(filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture));
            var absoluteUrlToLower = absoluteUrl.ToLowerInvariant();

            absoluteUrlToLower = forceWwwAndLowercase(filterContext, absoluteUrlToLower);
            absoluteUrlToLower = avoidTrailingSlashes(filterContext, absoluteUrlToLower);

            if (!absoluteUrl.Equals(absoluteUrlToLower))
            {
                filterContext.Result = new RedirectResult(absoluteUrlToLower, permanent: true);
            }
        }

        private static string avoidTrailingSlashes(ActionExecutingContext filterContext, string absoluteUrlToLower)
        {
            if (!isRootRequest(filterContext) && absoluteUrlToLower.EndsWith("/"))
                return absoluteUrlToLower.TrimEnd(new[] { '/' });

            return absoluteUrlToLower;
        }

        private static bool isRootRequest(ActionExecutingContext filterContext)
        {
            return filterContext.RequestContext.HttpContext.Request.Url.AbsolutePath == "/";
        }

        private static bool canIgnoreRequest(ActionExecutingContext filterContext)
        {
            return filterContext.IsChildAction || 
                   filterContext.HttpContext.Request.IsAjaxRequest() ||
                   filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri.Contains("?");
        }

        private static string forceWwwAndLowercase(ActionExecutingContext filterContext, string absoluteUrlToLower)
        {
            if (isLocalRequet(filterContext))
                return absoluteUrlToLower;

            if (absoluteUrlToLower.Contains("www"))
                return absoluteUrlToLower;

            return absoluteUrlToLower.Replace("http://", "http://www.")
                                     .Replace("https://", "https://www.");
        }

        private static bool isLocalRequet(ActionExecutingContext filterContext)
        {
            return filterContext.RequestContext.HttpContext.Request.IsLocal;
        }
    }
}
کلاس فوق، نگارش تکمیل شده ForceWww که پیشتر در این سایت دیده‌اید. توسط آن سه بررسی مختلف بر روی لینک جاری در حال پردازش صورت خواهد گرفت:
- تمام آدرس‌های سایت باید www داشته باشند؛ تا آدرس‌های آن یکنواخت شده و خصوصا مشکلات لاگین و نوشته شدن کوکی‌ها به ازای آدرس‌های مختلف و سر درگمی کاربران کاهش یابد.
- اگر آدرس جاری lowercase نباشد، تبدیل به نمونه lowercase شده و درخواست کننده، به آدرس جدید هدایت می‌شود. این مورد خصوصا جهت موتورهای جستجو برای تصحیح نتایج آن‌ها بسیار مفید است.
- اسلش انتهای لینک‌ها در صورت وجود حذف خواهد شد. این مورد نیز در کاهش تعداد خطاهای «محتوای تکراری» مؤثر است.
- اگر آدرسی، کوئری استرینگ داشته باشد از آن صرفنظر خواهد شد؛ زیرا ممکن است اطلاعات موجود در آن به کوچکی و بزرگی حروف حساس باشند.


ب) کاهش بار سایت توسط تولید خودکار Urlهایی که در بدو امر lowercase هستند

برای پیاده سازی این مطلب می‌توان از پروژه سورس باز «LowercaseRoutesMVC» استفاده کرد. سه فایل cs دارد که می‌توانید به پروژه خود اضافه کنید. پس از آن، هرجایی در پروژه خود routes.MapRoute دارید تبدیل کنید به routes.MapRouteLowercase .
به این ترتیب به صورت خودکار تمام Urlهای تولید شده توسط HTML helpers توکار ASP.NET MVC (و نه Urlهایی که دستی نوشته شده‌اند)، در حین درج در صفحه به صورت lowercase ظاهر خواهند شد (صرفنظر از اینکه نام‌های کنترلرها و یا اکشن متدهای تعریف شده camel case هستند یا خیر). مزیت این مساله کاهش یک مرحله Redirect است که در قسمت الف ذکر شد. در این کتابخانه کمکی نیز از آدرس‌هایی که دارای کوئری استرینگ باشند، صرفنظر می‌شود.
مطالب
وب‌پارت اسلاید متحرک در شیرپوینت 2010
در خلال تعدادی از پروژه‌هایی که در زمینه توسعه شیرپوینت 2010 درگیر بودم، یکی از وب‌پارت‌هایی که اکثر مشتریان درخواست می‌کردند وب پارت اسلاید متحرک بود. به نظر من داشتن این وب‌پارت ظاهرا خیلی برای مشتریان مهم بنظر می‌رسید، بهمین سبب در زیر به پیاده‌سازی آن اشاره می‌کنم.

ساخت لیست سفارشی

ابتدا یک لیست سفارشی بنام ContentSlides ایجاد می‌کنیم و  ستونی از نوع Rich HTML به آن اضافه می‌کنیم.

ایجاد یک پروژه شیرپوینتی از نوع Visual Web Part

سپس یک پروژه شیرپوینت از نوع Visual Web Part در سایتی که لیست فوق در آن قرار دارد می‌سازیم.





در این مرحله نامگذاری‌های پیش‌فرض ویژوال استودیو که برای ویژوال وب‌پارت در نظر گرفته را به نام مناسب تغییر می‌دهیم.



افزودن پلاگین AnythingSlider

ابتدا پلاگین AnythingSlider را از این آدرس دریافت نمایید. سپس فایلهای anythingslider.css, anythingslider-ie.css, jquery.min.js, jquery.anythingslider.js  و "default.png " را به ویژوال وب‌پارت اضافه می‌کنیم. برای اضافه کردن فایلهای این پلاگین، ابتدا فولدر "Layouts" به پروژه اضافه می‌نماییم و سپس فایلهای این پلاگین در این فولدر قرار می‌دهیم.

سپس در خط 129 و 155 و 181 فایل anythingslider.css

background: url("../images/default.png") no-repeat; 
را به
background: url(“default.png") no-repeat;
تغییر دهید و شکل پروژه به صورت زیر خواهد شد.

در ادامه، بر روی "ContentSliderVisualWebPartUserControl.ascx" دابل کلیک کنید و کد زیر را به آن اضافه می‌کنیم.

<SharePoint:CssRegistration ID="AnythingSliderCssRegistration" runat="server" 
Name="/_layouts/ContentSliderWebPart/anythingslider.css"></SharePoint:CssRegistration>
<SharePoint:CssRegistration ID="AnythingSliderCssRegistrationIE7" runat="server" 
Name="/_layouts/ContentSliderWebPart/anythingslider.css" ConditionalExpression="lte IE 7"></SharePoint:CssRegistration>

<SharePoint:ScriptLink ID="JqueryScriptLink" runat="server" 
Name="/_layouts/ContentSliderWebPart/jquery.min.js"></SharePoint:ScriptLink>
<SharePoint:ScriptLink ID="AnythingSliderScriptLink" runat="server" 
Name="/_layouts/ContentSliderWebPart/jquery.anythingslider.js"></SharePoint:ScriptLink>

توجه کنید در کد بالا ما از کنترل‌های "SharePointCssRegistration" و "SharePointScriptLink" برای اضافه نمودن فایلهای Css و JavaScript مورد نیاز و هم چنین از خصیصه "ConditionalExpression" برای اضافه کردن فایل "anythingslider-ie.css" برای مرورگر اینترنت اکسپلورر 7 به بالا استفاده کردیم.

ایجاد کوئری برای لیست سفارشی

برای ایجاد کوئری از کتابخانه jQuery بنام SPService که از این آدرس قابل دریافت است، استفاده می‌کنیم. فایل "jquery.SPServices.min.js" موجود در این کتابخانه را همانند سایر فایل‌های اضافه شده در قسمت قبل را به پروژه اضافه می‌کنیم.

برای استفاده از کتابخانه، کد زیر را در user control قسمت فبلی و در ادامه کدهای قبلی وارد می‌کنیم.

<SharePoint:ScriptLink ID="SPServicesScriptLink" runat="server" 
Name="/_layouts/ContentSliderWebPart/jquery.SPServices.min.js"></SharePoint:ScriptLink>

و برای ایجاد کوئری کد زیر در user control وارد می‌کنیم.

<script language="javascript" type="text/javascript">

$(document).ready(function () {
$().SPServices({
operation: "GetListItems",
async: false,
listName: "ContentSlides",
CAMLViewFields: "<ViewFields<FieldRef Name='SlidesContent' /></ViewFields>",
completefunc: function (xData, Status) {
$(xData.responseXML).SPFilterNode("z:row").each(function () {
var liHtml = "<li>" + $(this).attr("ows_SlidesContent") + "</li>";
$("#slider").append(liHtml);
});
}
});

/*-- Initialize AnythingSlider plugin --*/
    $('#slider').anythingSlider();

});
</script>

<ul id="slider" />

وظیفه این کد انجام کوئری بر روی لیست "ContentSlides" و ایجاد ساختار یک لیست جهت نمایش اسلایدها می‌باشد که آیتم‌های این لیست مقادیر ستون
"Slides Content" می‌باشد، شناسه این لیست "Slider" است.

توجه کنید: ما می‌توانیم CAML Queryهای پیشرفته‌ای برای اعمال فیلتر مناسب و چیدمان اسلایدها استفاده کنیم.

در ادامه، برای مقدار دهی اولیه به پلاگین بوسیله فراخوانی تابع

 $('#slider').anythingSlider();

انجام می‌شود.

در پایان براحتی با Deploy نمودن Solution، امکان استفاده از وب پارت مهیاست.

برای دریافت کد این پروژه از این آدرس استفاده کنید.

مطالب
چگونگی کار با Chart Webpart
بی شک استفاده از نمودار‌ها (چارت) روش بسیار مناسبی برای نمایش اطلاعات به صورت یکجا به کاربران می‌باشد و به درک بهتر مطلب کمک بسیار می‌کند . در شیرپوینت وب پارت قدرتمندی به همین نام وجود دارد که امکانات بسیاری را به شما خواهد داد . در این پست قصد دارم مروری بر امکانات این وب پارت داشته باشم .
قبل از شروع به یک لیست نیاز داریم که اطلاعات را جهت نمایش از آنجا بخوانیم (من این لیست را قبلا ساخته ام و روی آن مثال را بیان می‌کنم ) 

پس از ساخت این لیست داده هایی را برای آزمایش وارد کرده و صفحه ای که می‌خواهیم در آنجا chart را ایجاد کنیم باز می‌کنیم :
 صفحه را در حالت ویرایش قرار داده و Chart Web Part را انتخاب می‌کنیم : 

پس از فشردن دکمه Add کمی صبور باشید تا صفحه بارگذاری شود و تصویر زیر برای شما نمایش داده شود (داده‌های نمایش داده شده آزمایشی و تصادفی هستند و با بازآوری صفحه تغییر میکنند): 

همانطور که مشاهده میکنید دو دکمه در بالای این نمودار نمایش داده شده است (در قسمت تنظیمات web part هم می‌توانید ارجاع‌های لازم به این لینک‌ها را بیابید ) : 


Data & Appearance برای مدیریت منبع داده‌های نمودار و نحوه نمایش داده‌ها که دو لینک اصلی در آن وجود دارد : 

Advanced Propertes برای مدیریت قسمت‌های دیگر از قبیل جزییات رنگ بندی و زمینه و ...

 

یک نمونه از فرم تنظیمات :
 


برای شروع به کار از قسمت Data & Appearance گزینه Connect chart to Data را انتخاب می‌کنیم : 

همانطور که مشاهده می‌کنید (در قدم اول) می‌توانید داده‌های خود را از قسمت‌های مختلف با نمودار بایند کنید : در اینجا همانطور که در ابتدا گفته شد از لیست استفاده می‌کنم (گزینه دوم) :
در لیست‌های نمایش داده شده مانند تصویر زیر می‌توانید مسیر سایت را انتخاب نموده و نام لیست را مشخص کنید : 

در قدم بعد پیش نمایشی برای شما از داده‌ها نمایش داده می‌شود (به همراه توضیحاتی در مورد فیلتر کردن اطلاعات و ستون ها) بعد از زدن Next وارد مرحله آخر می‌شویم : 

همانطور که در شکل زیر مشاهده میکنید می‌توانید مشخص کنید که داده‌های محور‌های نمودار از کدام ستون‌ها داده را بگیرند و در صورت نیاز به عملیات گروه بندی می‌توانید آن را اعمال کنید : 


در دیگر قسمت‌های این فرم می‌توانید تنظیمات دیگری را انجام دهید برای مثال در قسمت Other Fields می‌توانید برای آیتم‌های روی نمودار یک لینک یا Tooltip تعریف کنید . 

پس از زدن دکمه Finish خروجی زیر را خواهید دید : 

حال می‌توانید از قسمت Customize Your Chart به ویرایش نحوه نمایش داده‌ها بپردازید 

همانطور که مشاهده میکنید در این قسمت می‌توانید نوع نمودار را بسته به نیاز خود انتخاب نمایید ، ویژکی‌های نمایشی آن را تغییر داده و برای نمودار خود ویژکی‌های 3 بعدی تنظیم کنید .

(برای نمایش بهتر خاصیت group by را از نمودار حذف کردم ) . نوع نمایش را مانند زیر تغییر می‌دهم : 

در این قسمت Theme نمایشی نمودار و نوع نمایش ستون‌ها و درصد transparency بودن ستون را بعلاوه طول و عرض اندازه نمودار و نوع خروجی نمایش داده شده در صفحه می‌توانید تنظیم کنید . روی Next کلیک می‌کنم تا وارد دیگر تنظیمات شود از جمله عنوان نمودار و راهنمای آن : 

در قسمت بالا تنظیمات عنوان برای نمودار قابل اجرا می‌باشد و در قسمت زیرین ، توضیحات و اختصارات راهنمای نمودار قابل تنظیم است . در این قسمت حتی می‌توانید راهنما را داخل خود نمودار انداخته (Dock to chart Area ) و موقعیت آن را مشخص کنید . کار تقریبا تمام شد . روی Finish کلیک کنید .

حال با رجوع به قسمت Advanced Propertes می‌توانید روی ظاهر این نمودار بیشتر کار کنید . این یک نمونه ساده از خروجی کار با این قسمت است : 

مطالب
اهمیت Controller های ساده در ASP.NET MVC
Controller‌ها به نوعی رابط بین View و Model هستند. ساده ترین محل برای قرار دادن کد‌های تصمیم گیری (decision-making code) ، قرار دادن منطق تجاری و یا فراهم ساختن داده برای View مثل ایجاد یک لیست از Select List برای یک DropDownList می‌باشند. اما انجام این کار‌ها به نرم افزار ما پیچیدگی تحمیل می‌کند. Controller‌ها باید در طول زمان توسعه‌ی یک نرم افزار کم حجم و سبک باقی بمانند. در این مطلب  بحث شد که یکی از اهداف استفاده از ASP.NET MVC نوشتن نرم افزار هایی تست پذیر می‌باشد. نوشتن آزمون واحد و نگهداری Controller هایی که مسئولیت زیادی بر عهده دارند سخت می‌باشد. Controller‌ها باید به نحوی توسعه پیدا کنند که نگهداری آن‌ها ساده باشد ، تست پذیر باشند و اصل SRP  را رعایت کرده باشند.
نگهداری آسان
کدی که درک آن سخت باشد ، تغییر دادن آن نیز سخت است ، هنگامی که درک کدی سخت باشد زمینه برای به وجود آمدن خطاها ، دوباره کاری و سردرد فراهم می‌شود. هنگامی که مسئولیت یک Action به صورت شفاف مشخص نباشد و انواع و اقسام کار‌ها به آن سپرده شده باشد تغییر در آن سخت می‌شود ،  ممکن است این تغییر باید چند جای دیگر هم داده شود در نتیجه فاز نگهداری هزینه و زمان اضافی به نرم افزار تحمیل می‌کند.
تست پذیری
 بهترین راه برای اطمینان از این که درک و تست پذیری سورس کد ما ساده هست انجام و تمرین توسعه‌ی تست محور (TDD) می‌باشد. هنگامی که از روش TDD استفاده می‌کنیم با سورسی کدی کار می‌کنیم که هنوز وجود ندارد. در این مرحله از به وجود آمدن کلاس هایی که تست آن‌ها دشوار یا غیر ممکن است (به دلیل داشتن مسئولیت‌های اضافی) از جمله Controller‌ها جلوگیری می‌شود. نوشتن آزمون‌های واحد برای Controller‌های کم حجم ساده می‌باشد. 
تمرکز بر روی یک مسئولیت
 بهترین راه برای ساده سازی Controller‌ها گرفتن مسئولیت‌های اضافی از آن می‌باشد  به Action زیر توجه کنید :
  
public RedirectToRouteResult Ship(int orderId)
{
   User user = _userSession.GetCurrentUser();
   Order order = _repository.GetById(orderId);
   if (order.IsAuthorized)                                
   {
      ShippingStatus status = _shippingService.Ship(order);
      if (!string.IsNullOrEmpty(user.EmailAddress))      
      {
         Message message = _messageBuilder
            .BuildShippedMessage(order, user);
         _emailSender.Send(message);
      }
      if (status.Successful)
      {
         return RedirectToAction("Shipped", "Order", new {orderId});
      }
   }
   return RedirectToAction("NotShipped", "Order", new {orderId});
}
 این Action کار زیادی انجام می‌دهد ، تقریبا می‌توان تعداد مسئولیت‌های این Action را با شمارش تعداد If‌ها پیدا کرد . مسئولیت تصمیم گیری درباره این که آیا Order مورد نظر آماده برای تحویل است یا خیر به این Action سپرده شده ، همچنین تصمیم گیری درباره اینکه آیا کاربر دارای آدرس ایمیل جهت ارسال ایمیل می‌باشد یا خیر به این Action سپرده شده است. منطق‌های domain logic و business logic نباید در کلاس‌های presentation همانند Controller‌ها قرار داده شود. تست و نگهداری کدی مثل کد بالا دشوار خواهد بود. Refactoring که باید در این Code اعمال شود Refactor Architecture by Tiers  نام دارد. این Refactoring به توسعه دهنده می‌گوید که منطقی که در لایه‌ی نمایش (Presentation) پیاده کرده را به لایه‌ی Business انتقال دهد. پس از انتقال منطق کد بالا به OrderShippingService ، کد Action ما ساده‌تر می‌شود : 
public RedirectToRouteResult Ship(int orderId)
{
   var status = _orderShippingService.Ship(orderId);
   if (status.Successful)
   {
      return RedirectToAction("Shipped", "Order", new {orderId});
   }
   return RedirectToAction("NotShipped", "Order", new {orderId});
}
پس از انتقال منطق تجاری به محل مناسب خودش تنها مسئولیتی که برای برای Controller باقی مانده این است که کاربر را به کجا Redirect کند. پس از این Refactoring علاوه برا اینکه مسئولیت‌ها در جای مناسب خود قرار گرفتند ، اکنون می‌توان به سادگی منطق کار را بدون تحت تاثیر قرار گرفتن کد‌های لایه‌ی نمایش تغییر داد. 
در آینده به تکنیک‌های ساده سازی Controller‌‌ها خواهیم پرداخت.
مطالب
مباحث تکمیلی مدل‌های خود ارجاع دهنده در EF Code first
در مورد طراحی Self Referencing Entities پیشتر مطلبی را در این سایت مطالعه کرده‌اید .
یک مثال دیگر آن می‌تواند نظرات چند سطحی در یک سایت باشند. نحوه تعریف آن با مطالبی که در قسمت هشتم عنوان شود تفاوتی نمی‌کند؛ اما ... زمانیکه نوبت به نمایش آن فرا می‌رسد، چند نکته اضافی را باید درنظر گرفت. ابتدا مثال کامل زیر را در نظر بگیرید:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;

namespace EFGeneral
{
    public class BlogComment
    {
        public int Id { set; get; }

        [MaxLength]
        public string Body { set; get; }

        public virtual BlogComment Reply { set; get; }
        public int? ReplyId { get; set; }
        public ICollection<BlogComment> Children { get; set; }
    }

    public class MyContext : DbContext
    {
        public DbSet<BlogComment> BlogComments { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Self Referencing Entity
            modelBuilder.Entity<BlogComment>()
                        .HasOptional(x => x.Reply)
                        .WithMany(x => x.Children)
                        .HasForeignKey(x => x.ReplyId)
                        .WillCascadeOnDelete(false);

            base.OnModelCreating(modelBuilder);
        }
    }

    public class Configuration : DbMigrationsConfiguration<MyContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
        }

        protected override void Seed(MyContext context)
        {
            var comment1 = new BlogComment { Body = "نظر من این است که" };
            var comment12 = new BlogComment { Body = "پاسخی به نظر اول", Reply = comment1 };
            var comment121 = new BlogComment { Body = "پاسخی به پاسخ به نظر اول", Reply = comment12 };

            context.BlogComments.Add(comment121);
            base.Seed(context);
        }
    }

    public static class Test
    {
        public static void RunTests()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());

            using (var ctx = new MyContext())
            {
                var list = ctx.BlogComments
                          //.where ...
                          .ToList() // fills the childs list too
                          .Where(x => x.Reply == null) // for TreeViewHelper                        
                          .ToList();

                if (list.Any())
                {

                }
            }
        }
    }
}

در مثال فوق کلاس نظرات به صورت خود ارجاع دهنده (خاصیت Reply به همین کلاس اشاره می‌کند) تعریف شده است تا کاربران بتوانند تا هر چند سطح لازم، به یک نظر خاص، پاسخ دهند.
در اینجا یک چنین جدولی با اطلاعاتی که ملاحظه می‌کنید تشکیل خواهند شد:


یک نظر ارائه شده و سپس دو نظر تو در توی دیگر برای این نظر ثبت شده است.

اولین نکته اضافه‌‌تری که نسبت به قسمت هشتم قابل ملاحظه است، تعریف خاصیت جدید Children به نحو زیر می‌باشد:
    public class BlogComment
    {
        // ... 
        public ICollection<BlogComment> Children { get; set; }
    }
این خاصیت تاثیری در نحوه تشکیل جدول ندارد. علت تعریف آن به توانمندی EF در پرکردن خودکار آن بر می‌گردد.
اگر به کوئری نوشته شده در متد RunTests دقت کنید، ابتدا یک ToList نوشته شده است. این مورد سبب می‌شود که تمام رکوردهای مرتبط دریافت شوند. مثلا در اینجا سه رکورد دریافت می‌شود. سپس برای اینکه حالت درختی آن حفظ شود، در مرحله بعد ریشه‌ها فیلتر می‌شوند (مواردی که reply آن‌ها null است). سپس این مورد تبدیل به list خواهد شد. اینبار اگر خروجی را بررسی کنیم، به ظاهر فقط یک رکورد است اما ... به نحو زیبایی توسط EF به شکل یک ساختار درختی، بدون نیاز به کدنویسی خاصی، منظم شده است:


سؤال:
برای نمایش این اطلاعات درختی و تو در تو در یک برنامه وب چکار باید کرد؟
تا اینجا که توانستیم اطلاعات را به نحو صحیحی توسط EF مرتب کنیم، برای نمایش آن‌ها در یک برنامه ASP.NET MVC می‌توان از یک TreeViewHelper سورس باز استفاده کرد.
البته کد آن در اصل برای استفاده از EF Code first طراحی نشده و نیاز به اندکی تغییر به نحو زیر دارد تا با EF هماهنگ شود (متد ToList و Count موجود در سورس اصلی آن باید به نحو زیر حذف و اصلاح شوند):
private void AppendChildren(TagBuilder parentTag, T parentItem, Func<T, IEnumerable<T>> childrenProperty)
        {
            var children = childrenProperty(parentItem);
            if (children == null || !children.Any())
            {
                return;
            }
//...

 
مطالب
قسمت سوم : بررسی تعدادی از ویژگی های Telerik Reporting
در این بخش قصد داریم به طور خلاصه تعدادی از ویژگی‌های Telerik Reporting را جهت ساخت گزارشات مورد بررسی قرار دهیم.
ویژگی‌های مورد بحث شامل موارد زیر می‌باشند:
•  ویرایش TextBox‌ها در محیط Designer
•  Copy و Paste کردن Style‌ها از یک کنترل به کنترل دیگر
•  قالب بندی شرطی
•  پیمایش و تغییر اندازه گزارش و آیتم‌های آن
•  تغییر اندازه بخش‌های مختلف گزارش نظیر Page Header ، Detail و ...
•  افزودن TextBox Shape و PictureBox درون Designer گزارش
•  استفاده از پنجره Data Explorer
•  استفاده از پنجره Report Explorer
در ادامه به بررسی موارد ذکر شده جهت طراحی گزارش می‌پردازیم.
1.  جهت تغییر محتوای یک TextBox می‌توان روی آن دوبار کلیک نمود. پس از آن TextBox به حالت ویرایش می‌رود و می‌توان متن درون آن را به سادگی تغییر داد.این کار در محیط Designer انجام می‌شود و نیازی نیست برای تغییر محتوای TextBox به پنجره Properties بروید و متن آن را تغییر دهید.
2.  در محیط Designer به راحتی می‌توانید گزارش خود را Zoom نمایید.این کار توسط ComboBox مربوطه در پایین سمت چپ Designer انجام می‌شود.

3. اگر قرار باشد TextBox جدیدی به گزارش خود اضافه نمایید، کافی است آن را از بخش ToolBox به محیط گزارش بکشید و سپس به چینش آن بپردازید. 

4. در محیط طراح گزارش Telerik می‌توانید به راحتی یک قالب بندی و یا Style را از کنترلی به کنترل دیگر کپی نمایید و در وقت خود جهت طراحی گزارش صرفه جویی نمایید. برای انجام اینکار کافی است روی کنترلی که قرار است Style آن را کپی نمایید راست کلیک نموده و پس از آن از منوی ظاهر شده گزینه Copy Style را انتخاب نمایید. در ادامه می‌توانید کنترل و یا کنترل هایی که قرار است قالب بندی را به آنها اعمال کنید انتخاب نموده ، روی آنها راست کلیک نمایید و گزینه Paste Style را انتخاب کنید.با این کار Style ی که در مرحله قبل از کنترلی دیگر کپی کرده بودید به کنترل یا کنترل‌های انتخاب شده اعمال می‌شود.  

5. یکی دیگر از امکانات Telerik Reporting امکان قالب بندی شرطی (Conditional Formating) می‌باشد. یعنی Style یک کنترل توسط شرط‌ها تعیین می‌شود. برای مثال می‌توانیم بگوییم که اگر مقدار فروش بیشتر از مبلغ خاصی بود ، عدد نمایش داده شده با رنگ سبز نمایش داده شود و یا اینکه از فونت و یا اندازه دیگری جهت نمایش آن استفاده شود (به طور کلی با توجه به شرط‌های تعیین شده  نمایش آن کنترل با یک Style متفاوت صورت گیرد). در قسمت‌های آینده به بررسی کامل این قابلیت نیز خواهیم پرداخت.  

6. یکی از امکاناتی که در هنگام طراحی گزارش در اختیار ما قرار میگیرد پنجره Data Explorer می‌باشد. توسط این پنجره می‌توان فیلدهای یک منبع داده (DataSource) را مشاهده نمود و برای اینکه بتوان از آنها در محیط طراحی استفاده کرد بر روی محیط طراحی درگ نمود. DataSource‌ها انواع مختلفی دارند که در قسمت اول این آموزش به معرفی آنها پرداختیم و از نمونه Sql آن نیز جهت طراحی یک گزارش ساده استفاده کردیم. در ادامه نیز با این موارد بیشتر آشنا خواهید شد.در تصویر زیر نحوه‌ی درگ کردن یک فیلد تصویر را از پنجره Data Explorer مشاهده می‌نمایید.  

7. یکی دیگر از اجزای Reporting پنجره‌ی Report Explorer می‌باشد. توسط این پنجره می‌توان دسترسی سریعی به اجزای درون گزارش داشت. برای مثال می‌توان به راحتی یک بخش درون گزارش را انتخاب نمود و در پنجره‌ی Properties تغییراتی در آن اعمال نمود.  


ادامه دارد ...

مطالب
آموزش مهندسی نرم افزار و UML - جلسه سوم

جلسه سوم :

در جلسه قبل به بررسی مشکلات تولید و توسعه سیستم‌های اطلاعاتی یا همان بسته‌های نرم افزاری پرداختیم در این جلسه به راهکاری که IT  برای فایق آمدن بر این مشکلات پیش روی ما قرار داده یا همان متدولوژی می‌پردازیم.

متدولوژی چیست ؟

          متدولوژی در واقع مجموعه ای از روش‌ها ، اصول و قواعدی است که برای قانونمند کردن تولید و توسعه نرم افزار ارائه می‌شود؛می توان گفت متدولوژی فرمولی جهت ساخت نرم افزار می‌باشد یا به عبارت دیگر متدولوژی چرخه حیات نرم افزار را مشخص می‌کند.

-چرخه حیات تولید وتوسعه نرم افزار یا SDLC(System Development Life Cycle)

مراحلی را که در طی تولید و توسعه نرم افزار سپری می‌شوند را SDLC می‌گویند.

انواع SDLC

1.     چرخه حیات سیستم‌های قدیمی یا TLC

2.     چرخه حیات سیستم‌های شی گرا یا OODLC

TLC(Traditional Life Cycle)-

در گذشته به دلیل اینکه اکثر برنامه‌ها بصورت فرآیندگرا یا Process Oriented نوشته می‌شدند از روش TLC  استفاده می‌شد . در روش‌های فرآیند گرا تمرکز اصلی بر روی فعالیت‌های سیستم بود در این روش بیشتر از نمودارهای ERD و DFD استفاده می‌شد .

البته اینا اضافه کنم که هنوز هم در بعضی از شرکت‌ها از این روش استفاده می‌شه هر چند که خودشونم نمی‌دونند یکی از دلایل اصلی هم فقر سواد شرکت‌های کار فرما می‌باشد . در ادامه به بررسی یکی از مدل‌های معروف TLC  یعنی مدل آبشاری می‌پردازیم.

-مدل آبشاری یا Water Fall  :

مدل آبشاری هر چند مدلی قدیمی می‌باشد اما مبنای اساسی مدل‌های شی گرا می‌باشد.

فازهای مختلف مدل آبشاری :

1.     مهندسی سیستم یا System Engineering

معرفی نیازمندی‌های کلی و مشخص نمودن کلیات سیستم به صورت سخت افزاری و نرم افزاری و تعاریف اصلی سیستم به طور مثال در پروژه وب سایت از Asp  استفاده کنیم یا Php 

2.     آنالیز نیازمندی‌ها یا Requirement Analysis

در این فاز به نیازمندی‌های کاربران می‌پردازیم یعنی در این فاز ما با چه یا What می‌پردازیم 

3.     طراحی یا Design

در این فاز ما به دنبال چگونه یا Who می‌رویم یعنی اینکه سیستم چگونه در جهت بر آوردن نیازمندی‌ها گام بردارد. 

4.     ساخت یا Construction

در این فاز آنچه را که در فاز طراحی مطرح کردیم به کد تبدیل می‌کنیم 

5.     تست Testing

در این مرحله سیستم از لحاظ کمی و کیفی تست میشوند. 

6.     نصب یا Installation

7.     نگهداری یا Maintenance

این فاز طولانی‌ترین و پرهزینه‌ترین قسمت چرخه عمر یک نرم افزار

معایب مدل آبشاری :

1.     مدل آبشاری تکرار بین فاز‌ها را در نظر نمی‌گیرد و خروجی هر فاز را قطعی در نظر می‌گیرد که اگر مثلا در فاز طراحی باشیم و یک نیازمندی در نظر گرفته نشده باشد این مدل هیچ راهکاری را برای در ج این نیازمندی در سیستم ارائه نمی‌دهد بعد‌ها برای رفع این مشکل مدل آبشاری با تکرار (Water Fall with Iteration)  معرفی گردید.

2.     دراین روش هر فاز هنگامی آغاز می‌شود که فاز قبل از خودش به پایان رسیده باشد که این امر مانع از Overlap  یا به اشتراک گذاری بین فازها می‌شد.

3.     سیستم‌های محاوره ای نیستند و در برابر تغییرات مقاوم نمی‌باشند.

مزایای مدل‌های آبشاری

1.     واگذاری هر فاز به یک تیم مشخص

2.     پیشرفت پروژه بیشتر به چشم می‌خورد.

 در جلسه آینده به بررسی مدل‌های شی گرا خواهیم پرداخت.

 

مطالب
MVVM و فراخوانی متدهای اشیاء View از طریق ViewModel

ما در ViewModel دسترسی مستقیمی به هیچ یک از اشیاء موجود در View نداریم (و درستش هم همین است). الان فرض کنید که می‌خواهیم از طریق ViewModel یک View را ببندیم؛ مثلا متد Close آن پنجره را فراخوانی کنیم. به عبارتی در حالت کلی می‌خواهیم یکی از متدهای تعریف شده یکی از عناصر بصری موجود در View را از طریق ViewModel فراخوانی نمائیم.
برای حل این مساله از فایل‌های همان SDK‌ مرتبط با Expression blend استفاده خواهیم کرد.

ابتدا ارجاعاتی را به اسمبلی‌های System.Windows.Interactivity.dll و Microsoft.Expression.Interactions.dll اضافه می‌کنیم.
سپس دو فضای نام مرتبط هم باید اضافه شوند:

  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"  
  xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

یک مثال عملی:
قصد داریم از طریق ViewModel ، پنجره‌ای را ببندیم. کدهای XAML این مثال را در ادامه مشاهده خواهید کرد:

<Window x:Class="WpfCallMethodActionSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:vm="clr-namespace:WpfCallMethodActionSample.ViewModels"
Name="ThisWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:MainWindowViewModel x:Key="vmMainWindowViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource vmMainWindowViewModel}}">

<Button Content="Save &amp; Close" VerticalAlignment="Top" Margin="5">
<i:Interaction.Triggers>
<!--فراخوانی متدی در ویوو مدل-->
<i:EventTrigger EventName="Click">
<ei:CallMethodAction
TargetObject="{Binding}"
MethodName="SaveButtonClicked" />
</i:EventTrigger>

<!--فراخوانی متدی در شیء جاری از طریق ویوو مدل-->
<i:EventTrigger SourceObject="{Binding}" EventName="CloseMainWindow">
<ei:CallMethodAction
TargetObject="{Binding ElementName=ThisWindow}"
MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</Window>

همچنین ViewModel تعریف شده نیز همین چند سطر زیر است:

using System;

namespace WpfCallMethodActionSample.ViewModels
{
public class MainWindowViewModel
{
public void SaveButtonClicked()
{
close();
}

public event EventHandler CloseMainWindow;
private void close()
{
if (CloseMainWindow != null) CloseMainWindow(this, EventArgs.Empty);
}
}
}

توضیحات:
اگر به ViewModel دقت کنید خبری از DelegateCommand در آن نیست. بله، به کمک ترکیبی از EventTrigger و CallMethodAction می‌توان جایگزینی را جهت DelegateCommand معرفی شده در قسمت‌های قبل این سری مباحث MVVM ارائه داد.
EventTrigger در اینجا به این معنا است که اگر EventName ذکر شده رخ داد، آنگاه این اعمال را انجام بده. مثلا در اینجا CallMethodAction را فراخوانی کن.
CallMethodAction در اسمبلی Microsoft.Expression.Interactions.dll تعریف شده است و تنها متدی از نوع void و بدون پارامتر را می‌تواند به صورت خودکار فراخوانی کند (محدودیت مهم آن است).
اینکه این متد کجا قرار دارد، توسط TargetObject آن مشخص می‌شود. اگر TargetObject را مساوی Binding قرار دادیم، یعنی به دنبال متدی که در DataContext گرید وجود دارد بگرد. به عبارتی به صورت خودکار به SaveButtonClicked تعریف شده در ViewModel ما متصل خواهد شد و آن‌را فراخوانی می‌کند.

تا اینجا رخداد Click دکمه تعریف شده را به متد SaveButtonClicked موجود در ViewModel سیم کشی کردیم.

در مرحله بعد می‌خواهیم از طریق ViewModel ، متدی را در View فراخوانی کنیم. نکته آن هم پیشتر ذکر شد؛ TargetObject صحیحی را باید انتخاب کرد. در اینجا برای پنجره جاری نام ThisWindow تعریف شده است و از طریق تعریف:

TargetObject="{Binding ElementName=ThisWindow}"

به CallMethodAction خواهیم گفت که قرار است متد Close را در شیء ThisWindow فراخوانی کنی.
همچنین نحوه تعریف EventTrigger ما هم در اینجا برعکس شده است:

<i:EventTrigger SourceObject="{Binding}"  EventName="CloseMainWindow">

قبلا به دنبال مثلا رخداد Click یک دکمه بودیم، اکنون با توجه به SourceObject تعریف شده، در ViewModel به دنبال این رخداد که برای نمونه در اینجا CloseMainWindow نام گرفته خواهیم گشت.

بنابراین View اینبار به رخداد CloseMainWindow تعریف شده در ViewModel سیم کشی خواهد شد. اکنون اگر این رخداد در ViewModel فراخوانی شود، CallMethodAction متناظر فعال شده و متد Close پنجره را فراخوانی می‌کند.