एक जटिल वस्तु के संपादन रूप के पीछे क्या छिपा है?

इस लेख में, हम आपको XtraScheduler अनुसूचक में लागू किए गए दृष्टिकोण से परिचित कराना जारी रखते हैं। पिछले लेख में, हमने एक डेटा सिंक्रोनाइज़र के बारे में बात की थी, इस बार आइए रूपों के बारे में बात करते हैं।



अक्सर अनुप्रयोगों में आप उन रूपों को पा सकते हैं जो बड़ी संख्या में निर्भर गुणों के साथ वस्तुओं को दर्ज करने या संपादित करने के लिए डिज़ाइन किए गए हैं। ऐसे इनपुट फॉर्म के निर्माण से डेवलपर्स के लिए "सिरदर्द" का कारण बनता है: संपादकों को पोस्ट करने, आरंभिक कोड लिखने, सत्यापन, घटना संचालकों के नियमित काम ...

तो ऐसे रूपों को जल्दी और मज़बूती से कैसे बनाया जाए?

संपादन योग्य वस्तु


एक संपादित वस्तु के एक उदाहरण के रूप में, अपॉइंटमेंट ले लो, एक ऑब्जेक्ट जो अनुसूचक की एक निश्चित घटना का प्रतिनिधित्व करता है। इस वर्ग की संरचना नीचे दिए गए चित्र में दिखाई गई है:



हमारे द्वारा चुनी गई वस्तु में गुणों का एक बड़ा समूह है। उसी समय, पहले से ही निर्धारित मूल्यों के आधार पर, गुणों का केवल एक निश्चित हिस्सा संपादन योग्य होना चाहिए। इसके अलावा, ऑब्जेक्ट मूल्यों को स्थापित करने के लिए एक व्यावसायिक तर्क को लागू करता है - एक संपत्ति को बदलने से पहले के आधार पर किसी अन्य को सेटिंग / शून्यिंग हो सकती है।

इसलिए, एक फॉर्म इंटरफ़ेस विकसित करते समय, इन सभी कारकों को ध्यान में रखना और इंटरफ़ेस तत्वों को संपादित वस्तु की स्थिति में बदलाव के लिए एक निश्चित तरीके से प्रतिक्रिया करना आवश्यक है।

संपादन विधि


इस ऑब्जेक्ट को संपादित करने के लिए, हमने निम्नलिखित दृष्टिकोण का उपयोग किया: फॉर्म पर सभी संपादक ऑब्जेक्ट के गुणों को संपादित या प्रदर्शित नहीं करेंगे, लेकिन एडिट किए गए से जुड़े एक निश्चित ऑब्जेक्ट के गुण।

आप शायद पहले से ही अनुमान लगा चुके हैं कि हम नियंत्रक और एडेप्टर पैटर्न के उपयोग के बारे में बात करेंगे, क्योंकि फॉर्म इंटरफ़ेस ऑब्जेक्ट के इंटरफ़ेस से भिन्न हो सकता है।

एक स्रोत स्रोत संपादन योग्य वस्तु, साथ ही संपादन के लिए आवश्यक अन्य वस्तुओं के रूप में इसे कंस्ट्रक्टर को पास करके एक नियंत्रक बनाएं।
public class AppointmentFormControllerBase : INotifyPropertyChanged { InnerSchedulerControl innerControl; Appointment sourceApt; public AppointmentFormControllerBase(InnerSchedulerControl innerControl, Appointment apt) { this.innerControl = innerControl; this.sourceApt = apt; //... } protected internal Appointment SourceAppointment { get { return sourceApt; } } //... } 

हमारे मामले में, संपादन प्रक्रिया के दौरान मूल वस्तु को बदलना बेहद अवांछनीय था और जब आप फॉर्म में रद्द करें बटन पर क्लिक करते हैं, तो आपको इसके सभी गुणों के मूल मूल्यों को पुनर्स्थापित करने की आवश्यकता होगी। इसलिए, स्रोत ऑब्जेक्ट के साथ, नियंत्रक में कंस्ट्रक्टर में बनाई गई इस ऑब्जेक्ट की एक प्रति भी शामिल है:
 public AppointmentFormControllerBase(InnerSchedulerControl innerControl, Appointment apt) { //... CreateAppointmentCopies(); } public Appointment EditedAppointmentCopy { get { return editedAptCopy; } } protected internal virtual void CreateAppointmentCopies() { editedAptCopy = sourceApt.Copy(); //... } 

क्या देता है एक प्रति? जब संपादन फ़ॉर्म में किया जाता है, तो कॉपी ऑब्जेक्ट के गुणों को बदल दिया जाता है, और केवल जब परिवर्तन लागू होते हैं, तो कॉपी से गुण मूल ऑब्जेक्ट पर "लुढ़का" होते हैं। यह एक विशेष नियंत्रक विधि द्वारा किया जाता है:
 public virtual void ApplyChanges() { // … sourceApt.BeginUpdate(); try { ApplyChangesCore(); } finally { sourceApt.EndUpdate(); } } protected internal virtual void ApplyChangesCore() { AppointmentFormAppointmentCopyHelper helper = new AppointmentFormAppointmentCopyHelper(this); helper.AssignSimpleProperties(editedAptCopy, sourceApt); helper.AssignCollectionProperties(editedAptCopy, sourceApt); } 

किसी कार्रवाई की आवश्यकता को बदलने के बिना फॉर्म को बंद करने के मामले में गायब हो जाता है, क्योंकि मूल वस्तु अछूती रह गई है।

ध्यान दें कि एक अलग आर्किटेक्चर के साथ, वापस बदलने / लागू करने की योजना यहाँ वर्णित से भिन्न हो सकती है और प्रतिलिपि की कोई आवश्यकता नहीं हो सकती है।

नियंत्रक इंटरफ़ेस


गुणों पर चलते हैं। नियंत्रक में, हम मुखौटा पैटर्न को लागू करते हैं और प्रतिलिपि गुणों से नियंत्रक गुणों के लिए आवश्यक गुणों की नकल करते हैं:
 public string Subject { get { return editedAptCopy.Subject; } set { editedAptCopy.Subject = value; NotifyPropertyChanged("Subject"); } } 

यदि इनपुट संपादकों और ऑब्जेक्ट संपत्तियों के इंटरफेस असंगत हैं, तो हम नियंत्रक गुणों में सीधे आवश्यक परिवर्तन करते हैं:
 public DateTime Start { get { return InnerControl.TimeZoneHelper.ToClientTime(editedAptCopy.Start); } set { editedAptCopy.Start = InnerControl.TimeZoneHelper.FromClientTime(value); } } 

यदि आवश्यक हो, उदाहरण के लिए, दिनांक और समय को अलग-अलग प्रदर्शित करने और संपादित करने के लिए, हम ऑब्जेक्ट की संपत्ति को दो नियंत्रक गुणों में विभाजित करते हैं:
 public DateTime StartDate { get { return editedAptCopy.Start.Date; } set { editedAptCopy.Start = value.Date + editedAptCopy.Start.TimeOfDay; NotifyPropertyChanged("StartDate"); } } public TimeSpan StartTime { get { return editedAptCopy.Start.TimeOfDay; } set { editedAptCopy.Start = editedAptCopy.Start.Date + value; NotifyPropertyChanged("StartTime"); } } 

हम आवश्यक सेवा गुणों के साथ नियंत्रक को पूरक करते हैं जो संपादित ऑब्जेक्ट में नहीं हैं, लेकिन फॉर्म इंटरफ़ेस को डिज़ाइन करते समय इसकी आवश्यकता हो सकती है:
 public virtual bool IsNewAppointment { get { … } } public bool CanDeleteAppointment { get { … } } 


इसके अलावा, हम नियंत्रक में विधियों का एक सेट बनाते हैं जो सभी आवश्यक तर्क को लागू करते हैं, जिसमें सत्यापन के तरीके, अतिरिक्त ऑब्जेक्ट प्राप्त करना, आदि शामिल हैं, जो फॉर्म ऑब्जेक्ट का अनुरोध करेंगे।

प्रपत्र नियंत्रण के ईवेंट हैंडलर्स में सत्यापन किया जाता है और नियंत्रक गुणों के लिए सही मान लिखे जाते हैं। नियंत्रक में निर्भर गुणों को बदलते समय, आपको संबंधित संपादकों में डेटा को अपडेट करने की आवश्यकता होती है, और यह इनपुट के लिए इसे छिपाने या दुर्गम बनाने के लिए भी संभव है।
WinForms में, यह निम्नानुसार किया जाता है: संपादकों की घटनाओं से सदस्यता समाप्त करें, नियंत्रक से नए डेटा को फिर से पढ़ें, और फिर से घटनाओं की सदस्यता लें।
 protected internal virtual void edtStartDate_Validated(object sender, EventArgs e) { controller.DisplayStart = edtStartDate.DateTime.Date + edtStartTime.Time.TimeOfDay; UpdateIntervalControls(); } protected internal virtual void UpdateIntervalControls() { UnsubscribeControlsEvents(); try { UpdateIntervalControlsCore(); } finally { SubscribeControlsEvents(); } } protected virtual void UpdateIntervalControlsCore() { edtEndDate.EditValue = controller.DisplayEnd.Date; edtEndTime.EditValue = new DateTime(controller.DisplayEnd.TimeOfDay.Ticks); //... bool enableTime = !controller.AllDay; edtEndTime.Visible = enableTime; edtEndTime.Enabled = enableTime; } 

वेब पर मूल्यों का सत्यापन फॉर्म में नहीं, बल्कि कॉलबैक कमांड में किया जा सकता है, जिसे संपादकों के मूल्यों तक पहुंच मिलती है। दर्ज किए गए मूल्यों का विश्लेषण करने के बाद, फॉर्म को बंद करने या न करने और सूचना संदेश जारी करने का निर्णय लिया जा सकता है।

नतीजतन, हमारा फॉर्म कंट्रोलर सभी आवश्यक कार्यक्षमता को समाप्‍त कर देता है और, एक साधारण वर्ग होने के नाते, इसका पुन: उपयोग किया जा सकता है, चाहे वह WinForms, वेब या WPF / SL हो।

यदि आवश्यक हो, तो प्रत्येक प्लेटफ़ॉर्म के लिए, नियंत्रक के लिए एक उत्तराधिकारी हो सकता है जो प्लेटफ़ॉर्म-विशिष्ट कार्यों को लागू करता है।

एक फार्म नियंत्रक का उपयोग करना


हम कोड टुकड़े के साथ विभिन्न प्लेटफार्मों पर AppointmentFormControllerBase का उपयोग करने के उदाहरणों का वर्णन करेंगे।

1. विंडोज फॉर्म

नियंत्रक उदाहरण को फॉर्म कंस्ट्रक्टर में बनाया जाता है, संपादकों को नियंत्रक गुणों के साथ आरंभ किया जाता है, जब संपादक मान बदलते हैं, तो नियंत्रक गुण संशोधित होते हैं, और परिवर्तनों को लागू करने के लिए एक कोड होता है।
 public partial class AppointmentForm : DevExpress.XtraEditors.XtraForm { readonly AppointmentFormController controller; public AppointmentForm(SchedulerControl control, Appointment apt, bool openRecurrenceForm) { // … this.controller = CreateController(control, apt); UpdateForm(); } protected internal AppointmentFormController Controller { get { return controller; } } protected virtual AppointmentFormController CreateController(SchedulerControl control, Appointment apt) { return new AppointmentFormController(control, apt); } protected virtual void UpdateForm () { tbSubject.Text = controller.Subject; edtShowTimeAs.Status = controller.GetStatus(); bool resourceSharing = controller.ResourceSharing; edtResource.Visible = !resourceSharing; bool canEditResource = controller.CanEditResource; edtResource.Enabled = canEditResource; //… } protected internal virtual void tbSubject_EditValueChanged(object sender, EventArgs e) { controller.Subject = tbSubject.Text; } protected internal virtual void OnOkButton() { if (controller.IsConflictResolved()) { controller.ApplyChanges(); } //… } 

2. ASP.NET

वेब पर, इनपुट फॉर्म एक UserControl है और इसे एक टेम्प्लेट के रूप में लोड किया जाता है। नियंत्रक के गुणों के लिए संपादकों के मूल्यों को "बाध्य" करने के लिए, डेटा बाइंडिंग एक्सप्रेशंस का उपयोग "<% #%>" के सिंटैक्स के माध्यम से किया जाता है। एक नियंत्रक उदाहरण टेम्पलेट कंटेनर वर्ग में बनाया गया है और कंटेनर प्रॉपर्टी के माध्यम से फॉर्म से सुलभ है।

Ascx फॉर्म फाइल इस प्रकार है

 <%@ Control Language="C#" AutoEventWireup="true" Inherits="AppointmentForm" CodeFile="AppointmentForm.ascx.cs" %> <%@ Register Assembly="DevExpress.Web.ASPxEditors.v10.2, … Namespace="DevExpress.Web.ASPxEditors" TagPrefix="dxe" %> <table class="dxscAppointmentForm"> </table> <tr> //… <td> <dxe:ASPxDateEdit ID="edtStartDate" runat="server" Date='<%# ((AppointmentFormTemplateContainer)Container).Start %>' EditFormat="DateTime" /> </td> <td> <dxe:ASPxComboBox ID="edtResource" runat="server" DataSource='<%# ResourceDataSource %>' Enabled='<%# ((AppointmentFormTemplateContainer)Container).CanEditResource %>' /> </td> //… </tr> </table> <dxsc:AppointmentRecurrenceForm ID="AppointmentRecurrenceForm1" runat="server" Start='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceStart %>' > </dxsc:AppointmentRecurrenceForm> //… <dxe:ASPxButton runat="server" ID="btnOk" Text="OK" UseSubmitBehavior="false" /> 

नीचे प्रारूप टेम्पलेट और कंटेनर के लिए कोड है:
 using System.Web.UI; public partial class AppointmentForm : UserControl { public override void DataBind() { base.DataBind(); AppointmentFormTemplateContainer container = (AppointmentFormTemplateContainer)Parent; AppointmentRecurrenceForm1.Visible = container.ShouldShowRecurrence; //… btnOk.ClientSideEvents.Click = container.SaveHandler; } public class AppointmentFormTemplateContainer : Control, IDataItemContainer, INamingContainer { AppointmentFormController controller; public AppointmentFormTemplateContainer(ASPxScheduler control) { this.controller = CreateController(control, Appointment); // … } public DateTime Start { get { return TimeZoneHelper.ToClientTime(Controller.EditedAppointmentCopy.Start); } } public bool CanEditResource { get { return Controller.CanEditResource; } } public bool ShouldShowRecurrence { get { return Controller.SourceAppointment.IsOccurrence && Controller.ShouldShowRecurrenceButton; } } public DateTime RecurrenceStart { get { return TimeZoneHelper.ToClientTime(Controller.EditedPattern != null ? Controller.EditedPattern.RecurrenceInfo.Start : DateTime.Now); } } public string SaveHandler { get { return String.Format("function() {{ aspxAppointmentSave(\"{0}\"); }}", ControlClientId); } } // … } 


3. WPF / SL

इस प्लेटफ़ॉर्म पर, फार्म टेम्पलेट का प्रतिनिधित्व System.Windows.Controls.UserControl द्वारा किया जाता है। WinForms की तरह, फार्म कंस्ट्रक्टर में एक नियंत्रक उदाहरण बनाया जाता है। लेकिन संपादकों के मूल्यों के लिए "बंधन" दो तरफा बंधन के तंत्र के माध्यम से xaml में किया जाता है।
 <UserControl x:Class="DevExpress.Xpf.Scheduler.UI.AppointmentForm" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors" xmlns:dxsch="clr-namespace:DevExpress.Xpf.Scheduler" x:Name="AptForm" MinWidth="600" > // … <Grid> // … <dxe:TextEdit Text="{Binding Controller.Subject, UpdateSourceTrigger=PropertyChanged}" /> <dxe:DateEdit Name="edtStartDate" EditValue ="{Binding Controller.DisplayStartDate}" /> <!--Resources--> <TextBlock IsEnabled="{Binding Controller.CanEditResource}"/> <ContentControl Content="{Binding ElementName=AptForm, Path=Controller}" ContentTemplateSelector="{StaticResource ResTemplateSelector}" IsEnabled="{Binding Controller.CanEditResource}"> </ContentControl> <Button Click="OnDeleteButtonClick" Visibility="{Binding Controller.CanDeleteAppointment, Converter={local:BoolToVisibilityConverter}}" /> // … </Grid> </UserControl> 

इसलिए संक्षेप में


प्रपत्र नियंत्रक का उपयोग करने से निम्नलिखित लाभ मिलते हैं:

हमें उम्मीद है कि लेख में प्रस्तुत सामग्री आपके लिए उपयोगी होगी।

Source: https://habr.com/ru/post/In107359/


All Articles