برنامه نویسی

چگونه یک WPF DateRangePicker ایجاد کنیم؟

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

کنترل کاربر WPF تمام شده را می توان در نمایه GitHub من در XamlDateRangePicker پیدا کرد.

مقاله زیر فرآیند ایجاد و نحوه استفاده از کنترل را شرح خواهد داد.

برنامه ریزی

ابتدا باید برنامه‌ریزی کنم که کنترل کاربر به چه بخش‌هایی نیاز دارد تا عملکرد مورد نظر من را ارائه دهد. بنابراین من یک نمونه اولیه کاغذی سریع انجام دادم.

نمونه اولیه کاغذ کنترل کاربر

قطعات

  1. TextBox
    من می خواستم تاریخ (محدوده) انتخاب شده را شبیه به DateTimePicker نشان دهم، بنابراین فکر کردم که به یک Textbox نیاز دارم.

  2. دکمه تصویر
    یک دکمه برای باز کردن تقویم باید با TextBox ترکیب شود. من فکر کردم که ToggleButton بهترین انتخاب برای این قابلیت است، زیرا آن را دارد IsChecked حالت.

  3. پنجره بازشو
    من می خواستم زمانی که کاربر روی دکمه Toggle کلیک می کند، تقویم از TextBox به پایین اسلاید شود، بنابراین تصمیم گرفتم یک پنجره بازشو اضافه کنم.

  4. تقویم
    کنترل واقعی تقویم که در پنجره بازشو تو در تو قرار دارد.

ایجاد کنترل کاربر WPF

یک کنترل کاربر WPF معمولی از دو فایل تشکیل شده است.

  • آ .xaml فایل
  • و الف .xaml.cs فایل

UI کنترل ها در ایجاد می شود .xaml فایل و منطق آن در .xaml.cs فایل.

ایجاد رابط کاربری

ابتدا یک کنترل کاربر WPF به پروژه خود اضافه کردم تا دو فایل مورد نیاز را ایجاد کنم. سپس شروع به ویرایش کردم DateRangePicker.xaml فایل و کنترل های مورد نیاز را اضافه کرد.

<UserControl x:Class="UserControls.DateRangePicker"
             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" 
             xmlns:local="clr-namespace:UserControls"
             mc:Ignorable="d" 
             x:Name="this"
             d:DesignHeight="30" d:DesignWidth="160">

    <StackPanel>
        <Border
            BorderThickness="1"
            BorderBrush="{Binding ElementName=DateRangePicker_TextBox, Path=BorderBrush}">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="30" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition Width="30" />
                </Grid.ColumnDefinitions>
                <TextBox
                    x:Name="DateRangePicker_TextBox"
                    Grid.Column="0"
                    Margin="0"
                    MinHeight="30"
                    BorderThickness="0"
                    d:Text="14.03.2023 - 20.03.2023"
                    Text="{Binding ElementName=this, Path=DisplayValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Delay=300}"
                    KeyUp="Handle_Escape_Button" />
                <ToggleButton
                    x:Name="DateRangePicker_ArrowButton"
                    Grid.Column="1"
                    Height="{Binding ElementName=DateRangePicker_TextBox, Path=Height}"
                    Focusable="False"
                    ClickMode="Press"
                    IsChecked="{Binding ElementName=this, Path=IsDropDownOpen, Mode=TwoWay}"
                    MinWidth="30"
                    Background="{Binding ElementName=DateRangePicker_TextBox, Path=Background}"
                    BorderBrush="{Binding ElementName=DateRangePicker_TextBox, Path=Background}"
                    BorderThickness="0"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Stretch">
                    <Path
                        x:Name="DateRangePicker_Arrow"
                        Data="M 0 0 L 4 4 L 8 0 Z">
                        <Path.Fill>
                            <SolidColorBrush Color="#FF444444"/>
                        </Path.Fill>
                    </Path>
                </ToggleButton>
            </Grid>
        </Border>
        <Popup
            x:Name="DateRangePicker_Popup"
            Placement="Bottom"
            IsOpen="{Binding ElementName=this, Path=IsDropDownOpen}"
            AllowsTransparency="True"
            Focusable="False"
            PopupAnimation="Slide">

            <Viewbox
                x:Name="DateRangePicker_DropDown"
                SnapsToDevicePixels="True"
                Stretch="Fill">
                <Calendar
                    x:Name="DateRangePicker_Calendar"
                    SelectionMode="SingleRange"
                    SelectedDatesChanged="DateRangePicker_Calendar_SelectedDatesChanged"/>
            </Viewbox>
        </Popup>
    </StackPanel>
</UserControl>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اطمینان از الزام آور بودن کنترل ها به کد پشت (.xaml.cs) به کاربر خود کنترل دادم x:Name="this". بنابراین من می توانم از آن برای مثال استفاده کنم Text صحافی TextBox

Text="{Binding ElementName=this, Path=DisplayValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Delay=300}"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این برای a جستجو می کند DisplayValue دارایی در من DateRangePicker.xaml.cs فایل.

ایجاد منطق کنترل ها

سپس شروع به ویرایش کردم .xaml.cs فایل برای اضافه کردن منطق کنترل های کاربر. در اینجا من EventHandler و Properties را تعریف کردم که در XAML کنترل استفاده می شود و همچنین یک DependencyProperty که باید از بیرون قابل دسترسی باشد تا بتوان تاریخ (محدوده) انتخابی را دریافت کرد.

تعریف یک ویژگی وابستگی

یک ویژگی وابستگی را می توان از خارج از کنترل محدود کرد. شما باید مانند قطعه کد زیر یک ویژگی را به عنوان DependencyProperty ثبت کنید.

public static readonly DependencyProperty SelectedDateRangeProperty
    = DependencyProperty.Register(
        nameof(SelectedDateRange),
        typeof(ObservableCollection<DateTime>),
        typeof(DateRangePicker),
        new FrameworkPropertyMetadata(default(ObservableCollection<DateTime>), FrameworkPropertyMetadataOptions.None));

public ObservableCollection<DateTime> SelectedDateRange
{
    get => (ObservableCollection<DateTime>)GetValue(SelectedDateRangeProperty);
    set => SetValue(SelectedDateRangeProperty, value);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی / تعامل با UI

به منظور به روز رسانی یا تعامل با UI از کد پشت خود، باید مطمئن شوید که آن را پیاده سازی کرده اید INotifyPropertyChanged رابط. به طوری که می توانید هنگام تغییر هر ویژگی که در UI شما استفاده می شود، یک اعلان دریافت کنید.

public event PropertyChangedEventHandler PropertyChanged;

public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName] string propertyName = "", Action onChanged = null)
{
    var output = false;

    if (EqualityComparer<T>.Default.Equals(backingStore, value) == false)
    {
        backingStore = value;
        onChanged?.Invoke();
        RaisePropertyChanged(propertyName);

        output = true;
    }

    return output;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس می توانید از SetProperty روشی برای افزایش تغییر ویژگی که UI را به روز می کند.

در اینجا یک نمونه از SelectedDates ویژگی که از کنترل های تقویم به روز می شود SelectedDatesChanged رویداد.

public ObservableCollection<DateTime> SelectedDates
{
    get => _selectedDate;
    set => SetProperty(ref _selectedDate, value, onChanged: () =>
    {
        IsDropDownOpen = false;
        if (value.Any())
        {
            if (value.Count == 1)
            {
                DisplayValue = value.First().ToString("dd.MM.yyyy");
                SelectedDateRange = new ObservableCollection<DateTime> { value.First() };
            }
            else
            {
                DisplayValue = $"{value.First():dd.MM.yyyy} - {value.Last():dd.MM.yyyy}";
                SelectedDateRange = new ObservableCollection<DateTime> { value.First(), value.Last() };
            }
        }
        else
        {
            SelectedDateRange = new ObservableCollection<DateTime>();
        }
    });
}
private ObservableCollection<DateTime> _selectedDate = new ObservableCollection<DateTime>();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با استفاده از کنترل

برای استفاده از کنترل باید مراحل بعدی را دنبال کنید.

  1. پروژه یا کنترل کاربر را به راه حل/پروژه خود اضافه کنید.
  2. فضای نام کنترل کاربر را به XAML خود اضافه کنید
xmlns:userControls="clr-namespace:UserControls;assembly=DateRangePicker"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. از کنترل در XAML خود استفاده کنید
<userControls:DateRangePicker 
    SelectedDateRange="{Binding SelectedDates, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

می توانید ببندید SelectedDateRange ویژگی ViewModel شما. این ویژگی دارای یک ObservableCollection<DateTime> از یک یا دو تاریخ یا تاریخ انتخابی تکی یا اولین و آخرین تاریخ محدوده تاریخ انتخاب شده.

اسکرین شات از DateRangePicker

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا