มีกลยุทธ์บางอย่างที่เป็นระบบสำหรับการออกแบบและการใช้ GUI หรือไม่?


18

ฉันใช้ Visual Studio เพื่อสร้างแอปพลิเคชัน GUI ใน C # กล่องเครื่องมือทำหน้าที่เป็นพาเล็ตส่วนประกอบที่ดีที่ทำให้ฉันสามารถลากและวางปุ่มและองค์ประกอบอื่น ๆ (เพื่อความชัดเจนฉันจะพูดว่าปุ่มเมื่อใดก็ตามที่ฉันหมายถึง "การควบคุม") บนแบบฟอร์มของฉันซึ่งทำให้รูปแบบคงที่ค่อนข้างง่าย อย่างไรก็ตามฉันพบปัญหาสองประการ:

  • การสร้างปุ่มในตอนแรกนั้นเป็นการทำงานมากมาย เมื่อฉันมีรูปแบบที่ไม่คงที่ (เช่นปุ่มหรือตัวควบคุมอื่น ๆ จะถูกสร้างขึ้นในเวลาทำงานตามสิ่งที่ผู้ใช้ทำ) ฉันไม่สามารถใช้จานสีได้เลย แต่ฉันต้องสร้างแต่ละปุ่มด้วยตนเองโดยการเรียกตัวสร้างในวิธีการใด ๆ ที่ฉันใช้แล้วเริ่มต้นด้วยตนเองโดยการระบุความสูงของปุ่มความกว้างตำแหน่งป้ายชื่อตัวจัดการเหตุการณ์และอื่น ๆ นี่เป็นเรื่องน่าเบื่ออย่างยิ่งเพราะฉันต้องเดาพารามิเตอร์เครื่องสำอางเหล่านี้โดยไม่สามารถดูว่าแบบฟอร์มจะเป็นอย่างไรและมันยังสร้างรหัสซ้ำ ๆ กันหลายบรรทัดสำหรับแต่ละปุ่ม
  • การทำให้ปุ่มทำบางสิ่งก็เป็นเรื่องที่ต้องทำเยอะ การรับมือกับเหตุการณ์ในแอปพลิเคชั่นเต็มรูปแบบเป็นความเจ็บปวดอย่างมาก วิธีเดียวที่ฉันรู้วิธีการทำเช่นนี้คือการเลือกปุ่มไปที่แท็บกิจกรรมในคุณสมบัติคลิกOnClickเหตุการณ์เพื่อสร้างเหตุการณ์ในFormรหัสของจากนั้นกรอกเนื้อหาของเหตุการณ์ เนื่องจากฉันต้องการแยกลอจิกและการนำเสนอตัวจัดการเหตุการณ์ของฉันทั้งหมดจึงกลายเป็นการเรียกใช้บรรทัดเดียวไปยังฟังก์ชันตรรกะทางธุรกิจที่เหมาะสม แต่การใช้ปุ่มนี้สำหรับปุ่มจำนวนมาก (เช่นลองนึกภาพจำนวนปุ่มที่มีอยู่ในแอปพลิเคชั่นเช่น MS Word) ทำให้รหัสของฉันสกปรกFormด้วยวิธีการจัดการเหตุการณ์สำเร็จรูปจำนวนมากและยากที่จะรักษาสิ่งนี้

เนื่องจากสิ่งเหล่านี้โปรแกรม GUI ใด ๆ ที่ซับซ้อนกว่า Hello World จึงทำไม่ได้จริง ๆ สำหรับฉัน เพื่อความชัดเจนฉันไม่มีปัญหาใด ๆ ที่เกี่ยวข้องกับความซับซ้อนในโปรแกรมที่ฉันเขียนซึ่งมี UI น้อยที่สุด - ฉันรู้สึกว่าฉันสามารถใช้ OOP ด้วยความสามารถในระดับที่เป็นธรรมเพื่อจัดโครงสร้างรหัสตรรกะทางธุรกิจของฉันอย่างเรียบร้อย แต่เมื่อพัฒนา GUI ฉันก็ติดอยู่ ดูเหมือนน่าเบื่อมากที่ฉันรู้สึกเหมือนฉันกำลังสร้างวงล้อใหม่และมีหนังสือเล่มหนึ่งอธิบายถึงวิธีการทำ GUI อย่างถูกต้องที่ฉันไม่ได้อ่าน

ฉันพลาดอะไรไปรึเปล่า? หรือผู้พัฒนา C # ทุกคนแค่ยอมรับรายการตัวจัดการเหตุการณ์ซ้ำ ๆ และรหัสสร้างปุ่มที่ไม่มีที่สิ้นสุด


เป็นคำใบ้ (หวังว่ามีประโยชน์) ฉันคาดหวังว่าคำตอบที่ดีจะพูดถึง:

  • การใช้เทคนิค OOP (เช่นรูปแบบจากโรงงาน) เพื่อทำให้การสร้างปุ่มซ้ำง่ายขึ้น
  • การรวมตัวจัดการเหตุการณ์จำนวนมากเข้ากับวิธีการเดียวที่ตรวจสอบSenderเพื่อพิจารณาว่าปุ่มใดที่เรียกว่าปุ่มนั้นและทำงานตามนั้น
  • XAML และการใช้ WPF แทน Windows Forms

คุณไม่ต้องพูดถึงสิ่งเหล่านี้แน่นอน เป็นเพียงการคาดเดาที่ดีที่สุดสำหรับคำตอบที่ฉันกำลังมองหา


อย่างที่ฉันเห็นใช่โรงงานจะเป็นรูปแบบที่ดี แต่ฉันเห็นรูปแบบ Model-View-Controller อีกมากมายพร้อมคำสั่งที่เกี่ยวกับการควบคุมแทนที่จะเป็นเหตุการณ์โดยตรง WPF มักจะเป็น MVVM แต่ MVC นั้นสามารถทำได้ ขณะนี้เรามีซอฟต์แวร์ขนาดใหญ่ใน WPF ที่ใช้ MVC ที่ 90% คำสั่งทั้งหมดจะถูกส่งต่อไปยังตัวควบคุมและเขาจัดการทุกอย่าง
Franck

คุณไม่สามารถสร้างรูปแบบไดนามิกด้วยปุ่มที่เป็นไปได้ทั้งหมดด้วยตัวแก้ไข GUI และทำสิ่งที่คุณไม่ต้องการให้มองไม่เห็นแบบไดนามิกหรือไม่ ฟังดูเหมือนทำงานน้อยกว่ามาก
Hans-Peter Störr

@hstoerr แต่มันยากที่จะทำให้เค้าโครงทำงาน ปุ่มจำนวนมากจะทับซ้อนกัน
Superbest

คำตอบ:


22

มีวิธีการหลายอย่างที่มีการพัฒนาในช่วงหลายปีที่ผ่านมาเพื่อจัดการกับปัญหาเหล่านี้ที่คุณกล่าวถึงซึ่งก็คือฉันเห็นด้วยสองประเด็นหลักที่กรอบ UI ได้ต้องแก้ไขในปีที่ผ่านมา มาจากพื้นหลัง WPF เหล่านี้เข้าหาดังนี้:

การออกแบบที่ชัดเจนมากกว่าความจำเป็น

เมื่อคุณอธิบายการเขียนรหัสอย่างระมัดระวังเพื่อยกตัวอย่างการควบคุมและตั้งค่าคุณสมบัติของพวกเขาคุณกำลังอธิบายถึงรูปแบบที่จำเป็นของการออกแบบ UI แม้แต่กับผู้ออกแบบ WinForms คุณเพียงแค่ใช้ wrapper เหนือโมเดลนั้น - เปิดไฟล์ Form1.Designer.cs และคุณเห็นโค้ดทั้งหมดที่อยู่ในนั้น

แน่นอนว่าด้วย WPF และ XAML - และโมเดลที่คล้ายกันในเฟรมเวิร์กอื่นจาก HTML เป็นต้นไป - คุณคาดว่าจะอธิบายเลย์เอาต์ของคุณและให้เฟรมเวิร์กทำการยกของหนักขึ้นมาใช้ คุณใช้การควบคุมที่ชาญฉลาดเช่น Panels (Grid หรือ WrapPanel ฯลฯ ) เพื่ออธิบายความสัมพันธ์ระหว่างองค์ประกอบ UI แต่ความคาดหวังคือคุณไม่ต้องจัดตำแหน่งด้วยตนเอง แนวคิดที่ยืดหยุ่นเช่นการควบคุมที่สามารถทำซ้ำได้ - เช่น Repeater ของ ASP.NET หรือ ItemsControl ของ WPF - ช่วยให้คุณสร้าง UI การปรับขนาดแบบไดนามิกโดยไม่ต้องเขียนโค้ดซ้ำช่วยให้เอนทิตีข้อมูลที่เติบโตแบบไดนามิกเป็นตัวแทนการควบคุมแบบไดนามิก

DataTemplates ของ WPF ช่วยให้คุณสามารถกำหนด - อีกครั้งประกาศ - นักเก็ตเล็ก ๆ ของความดี UI และจับคู่กับข้อมูลของคุณ ตัวอย่างเช่นรายการของวัตถุข้อมูลลูกค้าอาจถูกผูกไว้กับ ItemsControl และเรียกใช้แม่แบบข้อมูลที่แตกต่างกันขึ้นอยู่กับว่าเขาเป็นพนักงานปกติ (ใช้แม่แบบแถวตารางมาตรฐานที่มีชื่อและที่อยู่) หรือลูกค้าหลักในขณะที่แม่แบบอื่น ด้วยรูปภาพและปุ่มที่เข้าถึงได้ง่ายจะปรากฏขึ้น อีกครั้งโดยไม่ต้องเขียนโค้ดในหน้าต่างเฉพาะเพียงการควบคุมอย่างชาญฉลาดที่ตระหนักถึงข้อมูลบริบทของพวกเขาและทำให้คุณสามารถผูกไว้กับข้อมูลของคุณและให้พวกเขาดำเนินการที่เกี่ยวข้อง

การผูกข้อมูลและการแยกคำสั่ง

การแตะที่การผูกข้อมูลการเชื่อมโยงข้อมูลของ WPF (และอีกครั้งในกรอบงานอื่น ๆ เช่น AngularJS) ช่วยให้คุณระบุเจตนาของคุณโดยการเชื่อมโยงตัวควบคุม (พูดกล่องข้อความ) กับเอนทิตีข้อมูล (พูดชื่อลูกค้า) และให้ กรอบการจัดการประปา ลอจิกถูกจัดการในทำนองเดียวกัน แทนที่การเดินสายจัดการ event-hander แบบ behind-code ไปยังตรรกะทางธุรกิจของคอนโทรลเลอร์คุณจะใช้กลไก Data Binding เพื่อเชื่อมโยงพฤติกรรมของคอนโทรลเลอร์ (กล่าวคือคุณสมบัติคำสั่งของปุ่ม) ไปยังวัตถุ Command ซึ่งแสดงถึงนักเก็ตกิจกรรม

จะช่วยให้คำสั่งนี้จะใช้ร่วมกันระหว่าง windows โดยไม่ต้องเขียนตัวจัดการเหตุการณ์ทุกครั้ง

ระดับที่สูงขึ้นของสิ่งที่เป็นนามธรรม

โซลูชันทั้งสองสำหรับปัญหาทั้งสองนี้ของคุณแสดงให้เห็นถึงการก้าวไปสู่ระดับที่สูงขึ้นของนามธรรมมากกว่ากระบวนทัศน์ของ Windows Forms ที่คุณพบว่าน่าเบื่อ

แนวคิดคือคุณไม่ต้องการที่จะกำหนดในรหัสทุก ๆ คุณสมบัติที่มีการควบคุมและทุกพฤติกรรมเดียวที่เริ่มต้นจากการคลิกปุ่มและเป็นต้นไป คุณต้องการให้ตัวควบคุมและเฟรมเวิร์กพื้นฐานทำงานได้มากขึ้นสำหรับคุณและให้คุณคิดในแนวคิดที่เป็นนามธรรมมากขึ้นของ Data Binding (ซึ่งมีอยู่ใน WinForms แต่ไม่มีที่ไหนใกล้จะมีประโยชน์เหมือนกับใน WPF) และรูปแบบคำสั่งเพื่อกำหนดลิงก์ระหว่าง UI และพฤติกรรมที่ไม่ต้องการลงไปที่โลหะ

MVVMรูปแบบคือวิธีการที่ไมโครซอฟท์ที่จะกระบวนทัศน์นี้ ฉันแนะนำให้อ่าน

นี่เป็นตัวอย่างคร่าวๆของลักษณะที่ว่ารุ่นนี้จะมีลักษณะอย่างไรและจะช่วยประหยัดเวลาและบรรทัดของโค้ดให้คุณได้อย่างไร สิ่งนี้จะไม่รวบรวม แต่มันคือหลอก-WPF :)

ที่ใดในทรัพยากรแอปพลิเคชันของคุณคุณกำหนดเทมเพลตข้อมูล:

<DataTemplate x:DataType="Customer">
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

<DataTemplate x:DataType="PrimeCustomer">
   <Image Source="GoldStar.png"/>
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

ตอนนี้คุณเชื่อมโยงหน้าจอหลักของคุณกับ ViewModel ซึ่งเป็นคลาสที่เปิดเผยข้อมูลของคุณ สมมติว่าเป็นชุดของลูกค้า (เพียงแค่List<Customer>) และวัตถุคำสั่ง (อีกครั้งเป็นคุณสมบัติสาธารณะแบบง่ายICommand) ลิงก์นี้อนุญาตการรวม:

public class CustomersnViewModel
{
     public List<Customer> Customers {get;}
     public ICommand RefreshCustomerListCommand {get;}  
}

และ UI:

<ListBox ItemsSource="{Binding Customers}"/>
<Button Command="{Binding RefreshCustomerListCommand}">Refresh</Button>

และนั่นคือมัน ไวยากรณ์ของกล่องรายการจะดึงรายชื่อลูกค้าออกจาก ViewModel และพยายามแสดงผลให้กับ UI เนื่องจาก DataTemplates สองรายการที่เรากำหนดไว้ก่อนหน้านี้จะนำเข้าเทมเพลตที่เกี่ยวข้อง (อิงตาม DataType โดยสมมติว่า PrimeCustomer สืบทอดจากลูกค้า) และวางไว้เป็นเนื้อหาของกล่องรายการ ไม่มีการวนซ้ำไม่มีการควบคุมแบบไดนามิกผ่านรหัส

ปุ่มในทำนองเดียวกันมีไวยากรณ์ที่มีอยู่ก่อนหน้าเพื่อเชื่อมโยงพฤติกรรมของมันกับการใช้งาน ICommand ซึ่งน่าจะรู้ว่าจะอัพเดตCustomersคุณสมบัติ - พร้อมท์ให้เฟรมเวิร์กการเชื่อมโยงข้อมูลเพื่ออัพเดต UI อีกครั้งโดยอัตโนมัติ

แน่นอนว่าฉันใช้ทางลัดมาที่นี่ แต่นี่เป็นส่วนสำคัญของมัน


5

ครั้งแรกฉันแนะนำชุดบทความนี้: http://codebetter.com/jeremymiller/2007/07/26/the-build-your-own-cab-series-table-of-contents/-มันไม่ได้อยู่ปัญหารายละเอียด ด้วยรหัสปุ่มของคุณ แต่ให้กลยุทธ์ที่เป็นระบบสำหรับการออกแบบและการใช้งานกรอบงานแอปพลิเคชันของคุณเองโดยเฉพาะอย่างยิ่งสำหรับแอปพลิเคชัน GUI แสดงวิธีการใช้ MVC, MVP, MVVM กับแอปพลิเคชันฟอร์มทั่วไปของคุณ

การตอบคำถามของคุณเกี่ยวกับ "การจัดการกับเหตุการณ์": หากคุณมีหลายรูปแบบที่มีปุ่มทำงานในลักษณะที่คล้ายกันก็อาจจะจ่ายเงินให้กับการดำเนินการกำหนดตัวจัดการเหตุการณ์ใน "กรอบงานแอปพลิเคชัน" ด้วยตัวเอง แทนที่จะกำหนดเหตุการณ์คลิกโดยใช้ตัวออกแบบ GUI ให้สร้างแบบแผนการตั้งชื่อวิธีที่ตัวจัดการ "คลิก" จะต้องมีชื่อสำหรับปุ่มชื่อเฉพาะ ยกตัวอย่างเช่น - ปุ่มสำหรับปุ่มMyNiftyButton_ClickHandler MyNiftyButtonจากนั้นก็ไม่ยากที่จะเขียนรูทีนที่ใช้ซ้ำได้ (ใช้การสะท้อน) ซึ่งวนซ้ำปุ่มทั้งหมดของฟอร์มตรวจสอบว่าฟอร์มนั้นมีตัวจัดการคลิกที่เกี่ยวข้องหรือไม่และกำหนดตัวจัดการให้กับเหตุการณ์โดยอัตโนมัติ ดูที่นี่สำหรับตัวอย่าง

และถ้าคุณต้องการใช้ตัวจัดการเหตุการณ์เพียงปุ่มเดียวสำหรับปุ่มจำนวนมากนั่นก็จะเป็นไปได้เช่นกัน เนื่องจากตัวจัดการเหตุการณ์แต่ละตัวได้รับผู้ส่งผ่านและผู้ส่งเป็นปุ่มที่ถูกกดอยู่เสมอคุณสามารถส่งไปยังรหัสธุรกิจโดยใช้คุณสมบัติ "ชื่อ" ของแต่ละปุ่ม

สำหรับการสร้างแบบไดนามิกของปุ่ม (หรือการควบคุมอื่น ๆ ): คุณสามารถสร้างปุ่มหรือการควบคุมอื่น ๆ ที่มีการออกแบบในรูปแบบที่ไม่ได้ใช้เพียงเพื่อวัตถุประสงค์ในการ beeing เป็นแม่แบบ จากนั้นคุณใช้การสะท้อน (ดูตัวอย่างที่นี่ ) เพื่อโคลนตัวควบคุมเทมเพลตและเปลี่ยนเฉพาะคุณสมบัติเช่นตำแหน่งหรือขนาด นั่นอาจจะง่ายกว่าการเริ่มต้นคุณสมบัติสองหรือสามโหลด้วยตนเองในโค้ด

ฉันเขียนเรื่องนี้โดยใช้ Winforms ในใจ (เช่นเดียวกับคุณฉันเดา) แต่หลักการทั่วไปควรใช้กับสภาพแวดล้อม GUI อื่นเช่น WPF ที่มีความสามารถคล้ายกัน


เป็นไปได้ที่จะทำใน WPF และทำได้ง่ายกว่ามาก คุณเพียงแค่กรอกรายการคำสั่ง (ฟังก์ชั่นเฉพาะที่กำหนดรหัสไว้ล่วงหน้า) และตั้งค่าแหล่งที่มาบนหน้าต่างของคุณ คุณสร้างเทมเพลตแบบวิชวลที่เรียบง่ายและสิ่งที่คุณต้องดูแลก็คือการเติมชุดคำสั่งที่คุณต้องการและการผูก WPF ที่ยอดเยี่ยมจะช่วยดูแลส่วนที่เหลือให้คุณมองเห็น
Franck
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.