استفاده از Design-time Databinding در زمان توسعه یک User Control درWPF

جمعه 29 آبان 1394

زمانی که یکی از المان های WPF UI در Microsoft Visual Studio یا Blend طراحی می شود، اگر به صورت نمونه داده (sample) بین کاربران ارائه شود، مفیدتر است. استفاده از نمونه داده، ایجاد layout مناسب را تضمین می کند و اجازه می دهد effect های داده مشخص شده (مثل؛ اثرات long stings در خواص محدود) را بدون اجرای برنامه ببیند.در این مقاله می خواهیم ترفندی برای ایجاد اتصال با داده در حین زمان طراحی حتی برای user control ها ارائه کنیم.

 استفاده از Design-time Databinding در زمان توسعه یک User Control درWPF

ترفند کار در WPF این است که اجازه می دهد یک user control با نمونه داده و زمانی که در حال طراحی آن در  Visual Studio designer هستید، Populate شود.

مقدمه

برای مثال، اگر کسی فرایند گزارش ساده از user control را طراحی کند که یک progress bar(قسمت نمایش پیشرفت فرایند) با یک پیغام overlaid و یک مقدار پیشرفت داشته باشد، ممکن است نتواند مشکل طراحی را تا زمانی که برنامه اجرا شود، کشف کند. شکل زیر نمایشی از کنترل گزارش پیشرفت در visual studio designer را نشان می دهد:


به عبارت دیگر، به محض آن که کنترل در زمان طراحی به داده خود متصل شود، به سادگی می توان  مشاهده کرد که طراحی جاری مشکل دارد.

 

پیش زمینه

Visual Studio 2010 پشتیبانی از  data binding(اتصال داده)در زمان طراحی در  Designer view خود را  ارائه داده است. برای استفاده از آن به یک Window، یک صفحه یا یک فایل User Control XAML یک جفت از namespace ها اضافی و تعدادی صفت های طراحی جدید نیاز داریم که برای استفاده در دسترس باشد:

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"

مهمترین attiribute های design-time(زمان طراحی)، DataContext می باشد. این ویژگی می تواند برای هر نوع FrameworkElement تنظیم شود و design-time مربوط بهDataContext  و فرزندان آن را مشخص می کند.  پس یک طراح از context برای populate کردن  control binding در Design view استفاده می کند و نمونه داده در designer را نشان می دهد. برای مثال کد زیر را مشاهده کنید:

 xmlns:dvm="clr-namespace:Dima.Controls.DesignViewModel"
 d:DataContext ="{d:DesignInstance {x:Type dvm:ProgressReportSample1}, IsDesignTimeCreatable=True}"

این به خوبی برای محتوای WPF/Silverlight Windows و صفحات کار می کند. با این حال user control ها در بسیاری از موارد، DataContext  را در نظر نمی گیرند و به جای آن خواص وابستگی که host  آنها نیاز به اتصال به داده دارد را قرار می دهند. این استفاده مستقیم از صفت DataContext را در user controls ممکن می سازد.

 

طراحی user Control

 به عنوان مثال اجازه دهید فرایند گزارش user control را توضیح دهیم که در شکل های بالا نشان داده شده است. این نمونه برنامه درصد، پیغام و پیغام لغو فرایند را تعریف می کند:

public partial class ProgressReportControl : UserControl
{
    public ProgressReportControl()
    {
        InitializeComponent();
        this.root.DataContext = this;
    }

    public static readonly DependencyProperty PercentageProperty = 
        DependencyProperty.Register("Percentage", typeof(int), typeof(ProgressReportControl));
    public int Percentage
    {
        get { return (int)GetValue(PercentageProperty); }
        set { SetValue(PercentageProperty, value); }
    }

    public static readonly DependencyProperty MessageProperty =
        DependencyProperty.Register("Message", typeof(string), typeof(ProgressReportControl));
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public static readonly DependencyProperty CancelCommandProperty = 
        DependencyProperty.Register("CancelCommand", typeof(ICommand), typeof(ProgressReportControl));
    public ICommand CancelCommand
    {
        get { return (ICommand)GetValue(CancelCommandProperty); }
        set { SetValue(CancelCommandProperty, value); }
    }
}

و المان های خود را به آن دسته از خواص bind (پر می کند) می کند:

<DockPanel x:Name="root" Margin="4">
    <Button Content="Cancel" Command="{Binding CancelCommand, Mode=OneWay}" 
            Width="60" Margin="4,0,4,0" 
            DockPanel.Dock="Right" VerticalAlignment="Center" />
    <Grid Margin="4,0,4,0" Height="32">
        <ProgressBar Value="{Binding Percentage, 
        Mode=OneWay}" Minimum="0" Maximum="100" />
        <TextBlock Text="{Binding ElementName=progressBar, Path=Value, StringFormat={}{0:0}%}" 
                   HorizontalAlignment="Center" VerticalAlignment="Center" />
        <TextBlock Text="{Binding Message, Mode=OneWay}" 
                   Margin="4,0,4,0" VerticalAlignment="Center" DockPanel.Dock="Top" />
    </Grid>
</DockPanel>

در زمان اجرا هنگامی که control بارگذاری می شود، ما باید مطمئن شویم که المان های آن به dependency properties(خواص وابستگی) محدود شده و نه مثل DataContext  که به دلخواه کنترل ها از host خود ارث بری می کنند. بنابراین در سازنده control، ما DataContext را از المان ریشه فرزند به control خود تنظیم می کنیم:

this.root.DataContext = this;

هر نوع ویندوزی که کنترل گزارش فرایند(progress report) را میزبانی می کند نیاز به اتصال (bind کردن( خصوصیت های control به داده دارد. این DataContext که بهcontrol ارسال می شود، در control صرف نظر می شود:

<Controls:ProgressReportControl Grid.Row="1" 
HorizontalAlignment="Stretch" VerticalAlignment="Top"
          Message="{Binding Report.Message}" Percentage="{Binding Report.Percentage}" 
          CancelCommand="{Binding ComputationCancel}" />

در نگاه اول این کار به طور کامل امکان استفاده از  design-time data ارسال شده را به عنوان  design-time data از بین می برد.

 

توضیح ترفند

با این حال ما باید به یاد بیاوریم زمانی که یک user control در Design view طراحی شده باشد، designer نمی تواند این سازنده را اجرا کند( هرچند که این، سازنده تمام المان های فرزند خود را اجرا خواهد کرد). بنابراین اگر یک مدل نمایشdesign-time  ایجاد کنیم که خصوصیات وابستگی کنترل های تطبیق یافته را  شکل می دهد و آن را به عنوان  نمونه داده از طریق DataContext به user control طراحی شده ارسال می کند، المان های فرزند control به صورت زیر مشاهده می شود:

public class ProgressReportSample1
{
    public int Percentage { get; private set; }
    public string Message { get; private set; }
    public ICommand CancelCommand { get; private set; }
    public ProgressReportSample1()
    {
        this.Percentage = 60;
        this.Message = "Computation progress";
        this.CancelCommand = null;
    }
}

با توجه به شکل تطبیق داده شده، designer ، با موفقیت المان های user control را به خواص  design-time view model متصل می کند و به صورت کد زیر می باشد:

<UserControl x:Class="Dima.Controls.ProgressReportControl"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" d:DesignHeight="80" d:DesignWidth="300"
             
     xmlns:dvm="clr-namespace:Dima.Controls.DesignViewModel"
     d:DataContext ="{d:DesignInstance {x:Type dvm:ProgressReportSample1}, 
     IsDesignTimeCreatable=True}">

در زمان مشابه، هنگامی که ما window hosting مربوط به user control خود را طراحی می کنیم، سازنده window دوباره اجرا نخواهد شد، اما سازنده control می توانداجرا شود. پس زمانی که host ویندوز طراحی می شود، کنترل، مدل نمایشی(View model) مربوط به   design-time ویندوز ارسال شده به DataContext  را در نظر نمی گیرد و به درستی به خصوصیت های وابستگی کنترل، متصل می شود. شکل زیر نمایشی از desiner موجود در visual studio می باشد که ویندوز از یک کنترل گزارش گیری فرایند میزبانی می کند، این کنترل، داده زمان طراحی(design-time) رااز طریق خصوصیت ها(propertyها) ارائه می دهد:

 

با اجرای برنامه و فشردن دکمه "شروع" برنامه به صورت زیر عمل می کند:

 

 

و با اتمام آن پیغام success چاپ می شود:

 

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • WPF
  • 2k بازدید
  • 1 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید