การจัดการกล่องโต้ตอบใน WPF ด้วย MVVM


235

ในรูปแบบ MVVM สำหรับ WPF การจัดการไดอะล็อกเป็นหนึ่งในการดำเนินการที่ซับซ้อนมากขึ้น เนื่องจากโมเดลการดูของคุณไม่ทราบอะไรเกี่ยวกับมุมมองการสื่อสารในกล่องโต้ตอบจึงน่าสนใจ ฉันสามารถแสดงให้เห็นICommandว่าเมื่อมุมมองเรียกมันโต้ตอบสามารถปรากฏขึ้น

ไม่มีใครรู้วิธีที่ดีในการจัดการผลลัพธ์จากการโต้ตอบ? MessageBoxฉันกำลังพูดเกี่ยวกับหน้าต่างไดอะล็อกเช่น

อีกวิธีหนึ่งที่เราทำคือมีเหตุการณ์ใน viewmodel ที่มุมมองจะสมัครเมื่อต้องการไดอะล็อก

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

ไม่เป็นไร แต่หมายความว่ามุมมองต้องใช้รหัสซึ่งเป็นสิ่งที่ฉันต้องการหลีกเลี่ยง


ทำไมไม่ผูกกับวัตถุตัวช่วยในมุมมอง
พอลวิลเลียมส์

1
ไม่แน่ใจคุณหมายถึงอะไร.
Ray Booysen

1
ถ้าฉันเข้าใจคำถามคุณไม่ต้องการให้กล่องโต้ตอบ VM โผล่ขึ้นมาและคุณไม่ต้องการโค้ดที่ล้าสมัยในมุมมอง นอกจากนี้ดูเหมือนว่าคุณต้องการคำสั่งเหตุการณ์ ฉันเห็นด้วยกับสิ่งเหล่านี้ดังนั้นฉันใช้คลาสผู้ช่วยในมุมมองซึ่งแสดงคำสั่งเพื่อจัดการกับกล่องโต้ตอบ ฉันตอบคำถามนี้ในหัวข้ออื่นที่นี่: stackoverflow.com/a/23303267/420400 แต่ประโยคสุดท้ายทำให้เสียงเหมือนคุณไม่ต้องการใด ๆรหัสที่ทุกคนทุกที่ในมุมมอง ฉันเข้าใจว่าความกังวลนั้น แต่รหัสที่เป็นปัญหานั้นเป็นเพียงเงื่อนไขและไม่น่าจะมีการเปลี่ยนแปลง
พอลวิลเลียมส์

4
โมเดลมุมมอง Thje ควรรับผิดชอบต่อตรรกะที่อยู่เบื้องหลังการสร้างกล่องโต้ตอบเสมอนั่นคือเหตุผลทั้งหมดที่มีอยู่ตั้งแต่แรก ที่กล่าวว่ามันไม่ (และไม่ควร) ทำยกยกของการสร้างมุมมองของตัวเอง ฉันเขียนบทความเกี่ยวกับเรื่องนี้ที่codeproject.com/Articles/820324/ซึ่งฉันแสดงให้เห็นว่าวงจรชีวิตทั้งหมดของกล่องโต้ตอบสามารถจัดการได้ผ่านการผูกข้อมูล WPF ปกติและไม่ทำลายรูปแบบ MVVM
Mark Feldman

คำตอบ:


131

ฉันขอแนะนำให้ยกเลิกการใช้งานกล่องโต้ตอบโมดอลในปี 1990 และแทนที่จะใช้การควบคุมเป็นโอเวอร์เลย์ (การวางตำแหน่งผ้าใบ + สัมบูรณ์) โดยการมองเห็นเชื่อมโยงกับบูลีนย้อนกลับใน VM เข้าใกล้การควบคุมประเภท ajax

สิ่งนี้มีประโยชน์มาก:

<BooleanToVisibilityConverter x:Key="booltoVis" />

ในขณะที่:

<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>

นี่คือวิธีที่ฉันใช้การควบคุมผู้ใช้ คลิกที่ 'x' ปิดการควบคุมในบรรทัดของรหัสในรหัสของผู้ใช้ควบคุมที่อยู่เบื้องหลัง (เนื่องจากฉันมี Views ของฉันใน. exe และ ViewModels ใน dll ฉันไม่รู้สึกแย่เกี่ยวกับโค้ดที่ใช้ UI)

กล่องโต้ตอบ Wpf


20
ใช่ฉันชอบความคิดนี้ด้วย แต่อยากเห็นตัวอย่างของการควบคุมนี้ในแง่ของวิธีการแสดงและดึงผลการโต้ตอบจากมันเป็นต้นโดยเฉพาะอย่างยิ่งในสถานการณ์ MVVM ใน Silverlight
Roboblob

16
คุณจะป้องกันผู้ใช้จากการโต้ตอบกับการควบคุมภายใต้การซ้อนทับโต้ตอบนี้ได้อย่างไร?
Andrew Garrison

16
ปัญหาเกี่ยวกับวิธีการนี้คือคุณไม่สามารถเปิดกล่องโต้ตอบโมดอลตัวที่สองจากอันแรกอย่างน้อยก็ไม่ต้องไม่มีการดัดแปลงระบบโอเวอร์เลย์อย่างหนัก ...
Thomas Levesque

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

12
วิธีการนี้ดูน่ากลัวสำหรับฉัน ฉันพลาดอะไรไป มันดีกว่ากล่องโต้ตอบของจริงอย่างไร?
โจนาธานวู้ด

51

คุณควรใช้สื่อกลางสำหรับสิ่งนี้ คนกลางเป็นรูปแบบการออกแบบทั่วไปที่รู้จักกันว่าMessengerในการใช้งานบางอย่าง มันเป็นกระบวนทัศน์ของการลงทะเบียนประเภท / แจ้งเตือนและช่วยให้ ViewModel และ Views ของคุณในการสื่อสารผ่านการส่งข้อความ mecanism ต่ำคู่

คุณควรตรวจสอบกลุ่มสาวก WPF ของ Google และเพียงแค่ค้นหาผู้ไกล่เกลี่ย คุณจะมีความสุขมากกับคำตอบ ...

อย่างไรก็ตามคุณสามารถเริ่มต้นด้วยสิ่งนี้:

http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/

สนุก !

แก้ไข: คุณสามารถดูคำตอบของปัญหานี้ด้วยชุดเครื่องมือ MVVM Light ได้ที่นี่:

http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338


2
Marlon grech เพิ่งโพสต์การใช้งานสื่อกลางแบบใหม่: marlongrech.wordpress.com/2009/04/16/…
Roubachof

21
เพียงหมายเหตุ: รูปแบบผู้ไกล่เกลี่ยไม่ได้ถูกนำเสนอโดยสาวก WPF มันเป็นรูปแบบ GoF แบบคลาสสิก ... ( dofactory.com/Patterns/PatternMediator.aspx ) คำตอบที่ดีเป็นอย่างอื่น)
โทมัส Levesque

10
ได้โปรดพระเจ้าอย่าใช้คนกลางหรือผู้ส่งสารที่ถูกสาป รหัสประเภทนั้นที่มีข้อความจำนวนมากที่บินไปมานั้นยากที่จะทำการดีบั๊กเว้นแต่ว่าคุณจะจำจุดได้ทั้งหมดใน codebase ทั้งหมดของคุณที่สมัครและจัดการกับทุกเหตุการณ์ มันกลายเป็นฝันร้ายสำหรับผู้พัฒนาใหม่ ในความเป็นจริงฉันคิดว่าห้องสมุด MvvMLight ทั้งหมดเป็นรูปแบบการต่อต้านขนาดใหญ่สำหรับการใช้การส่งข้อความแบบอะซิงโครนัสที่แพร่หลายและไม่จำเป็น การแก้ปัญหานั้นง่าย: เรียกใช้บริการโต้ตอบที่แยกต่างหาก (เช่น IDialogService) ของการออกแบบของคุณ อินเทอร์เฟซมีวิธีและเหตุการณ์สำหรับการโทรกลับ
Chris Bordeman

34

กล่องโต้ตอบ MVVM ที่ดีควร:

  1. ถูกประกาศด้วย XAML เท่านั้น
  2. รับพฤติกรรมทั้งหมดจาก databinding

น่าเสียดายที่ WPF ไม่มีคุณสมบัติเหล่านี้ ShowDialog()แสดงโต้ตอบต้องโทรรหัสข้างหลัง ชั้นหน้าต่างซึ่งสนับสนุนไดอะล็อกไม่สามารถประกาศใน XAML ดังนั้นจึงไม่สามารถจะ databound DataContextไป

ในการแก้ปัญหานี้ฉันได้เขียนตัวควบคุมสแลม XAML ที่อยู่ในโลจิคัลทรีและถ่ายทอดข้อมูลไปยัง a Windowและจัดการแสดงและซ่อนกล่องโต้ตอบ คุณสามารถค้นหาได้ที่นี่: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

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

<dialog:Dialog Content="{Binding Path=DialogViewModel}" Showing="True" />

คุณอาจต้องการเพิ่มสไตล์ที่กำหนดShowingไว้ ฉันอธิบายในบทความของฉัน ฉันหวังว่านี่จะช่วยคุณได้


2
นั่นเป็นวิธีที่น่าสนใจมากสำหรับปัญหาในการแสดงหน้าต่างโต้ตอบใน MVVM
dthrasher

2
"Showing a dialog requires a code-behind"mmm คุณสามารถเรียกสิ่งนั้นได้ใน ViewModel
Brock Hensley

ฉันจะเพิ่มจุด 3 - คุณมีอิสระที่จะผูกกับวัตถุอื่น ๆ ในมุมมอง การปล่อยให้รหัสของไดอะล็อกที่อยู่ด้านหลังว่างหมายความว่าไม่มีรหัส C # ใด ๆ ในมุมมองและการเชื่อมโยงฐานข้อมูลไม่ได้หมายถึงการรวมกับ VM
พอลวิลเลียมส์

25

ฉันใช้วิธีนี้สำหรับการโต้ตอบกับ MVVM

สิ่งที่ฉันต้องทำตอนนี้คือเรียกสิ่งต่อไปนี้จากโมเดลมุมมองของฉัน

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

ไลบรารีใดที่ uiDialogService มาจากไหน
aggietech

1
ไม่มีห้องสมุด เป็นเพียงอินเตอร์เฟซที่มีขนาดเล็กและการดำเนินงาน: stackoverflow.com/questions/3801681/... ที่จะเป็น atm ที่เป็นธรรมมันมีบางอย่างเกินความต้องการของฉัน :) (ความสูงความกว้างการตั้งค่าคุณสมบัติและอื่น ๆ )
blindmeis

16

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

ดูโซลูชัน Silverlight 4 ทั้งหมดได้ที่นี่กล่องโต้ตอบ Modal ด้วย MVVM และ Silverlight 4


เช่นเดียวกับวิธีการของ @Elad Katz คำตอบของคุณไม่มีเนื้อหาที่เชื่อมโยง - โปรดปรับปรุงคำตอบของคุณโดยใส่เพราะนั่นคือสิ่งที่ถือว่าเป็นคำตอบที่ดีสำหรับ SO อย่างไรก็ตามขอขอบคุณสำหรับการสนับสนุนของคุณ! :)
Yoda

6

ฉันต่อสู้กับแนวคิดนี้มาระยะหนึ่งเมื่อเรียนรู้ (ยังคงเรียนรู้) MVVM สิ่งที่ฉันตัดสินใจและสิ่งที่ฉันคิดว่าคนอื่นตัดสินใจแล้ว แต่สิ่งนี้ไม่ชัดเจนสำหรับฉันคือ:

ความคิดดั้งเดิมของฉันคือว่า ViewModel ไม่ควรได้รับอนุญาตให้เรียกใช้กล่องโต้ตอบโดยตรงเนื่องจากไม่มีธุรกิจตัดสินใจว่าจะให้กล่องโต้ตอบปรากฏขึ้นอย่างไร ด้วยเหตุนี้ฉันเริ่มคิดเกี่ยวกับวิธีที่ฉันสามารถส่งข้อความเหมือนฉันจะมีใน MVP (เช่น View.ShowSaveFileDialog ()) อย่างไรก็ตามฉันคิดว่านี่เป็นวิธีที่ผิด

ไม่เป็นไรสำหรับ ViewModel ที่จะเรียกไดอะล็อกโดยตรง อย่างไรก็ตามเมื่อคุณทดสอบ ViewModel นั่นหมายความว่าไดอะล็อกจะปรากฏขึ้นระหว่างการทดสอบของคุณหรือล้มเหลวด้วยกัน (ไม่เคยลองเลย)

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

คุณควรใช้ Service Locator หรือ IoC บางประเภทอยู่แล้วซึ่งคุณสามารถกำหนดค่าเพื่อให้รุ่นที่ถูกต้องขึ้นอยู่กับบริบท

การใช้วิธีการนี้ ViewModel ของคุณยังคงสามารถทดสอบได้และขึ้นอยู่กับว่าคุณจำลองข้อความโต้ตอบของคุณอย่างไรคุณสามารถควบคุมพฤติกรรมได้

หวังว่านี่จะช่วยได้


6

มีสองวิธีที่ดีในการทำเช่นนี้ 1) บริการช่วยโต้ตอบ (ง่ายสะอาด) และ 2) มุมมองช่วย View assisted มีคุณสมบัติบางอย่างที่เรียบร้อย แต่โดยปกติแล้วจะไม่คุ้มค่า

บริการ DIALOG

ก) ส่วนต่อประสานบริการโต้ตอบเช่นผ่านตัวสร้างหรือคอนเทนเนอร์ที่พึ่งพา:

interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }

b) การติดตั้ง IDialogService ของคุณควรเปิดหน้าต่าง (หรือฉีดตัวควบคุมบางส่วนเข้าไปในหน้าต่างที่ใช้งานอยู่) สร้างมุมมองที่สอดคล้องกับชื่อของประเภท dlgVm ที่กำหนด (ใช้การลงทะเบียนคอนเทนเนอร์หรือระเบียบหรือ ContentPresenter กับประเภท DataTemplates ที่เกี่ยวข้อง) ShowDialogAsync ควรสร้าง TaskCompletionSource และส่งกลับไปยัง .Task proptery คลาส DialogViewModel นั้นต้องการเหตุการณ์ที่คุณสามารถเรียกใช้ในคลาสที่ได้รับเมื่อคุณต้องการปิดและดูในมุมมองไดอะล็อกเพื่อปิด / ซ่อนไดอะล็อกจริง ๆ และทำ TaskCompletionSource ให้เสร็จสมบูรณ์

b) ในการใช้งานเพียงแค่โทร waitit this.DialogService.ShowDialog (myDlgVm) ในอินสแตนซ์ของคลาส DialogViewModel ที่ได้รับมา หลังจากรอผลตอบแทนให้ดูคุณสมบัติที่คุณได้เพิ่มไว้ในกล่องโต้ตอบ VM ของคุณเพื่อกำหนดสิ่งที่เกิดขึ้น คุณไม่จำเป็นต้องโทรกลับ

ดูการช่วยเหลือ

นี่คือมุมมองของคุณฟังเหตุการณ์ใน viewmodel ทั้งหมดนี้อาจถูกห่อหุ้มด้วยพฤติกรรมแบบผสมผสานเพื่อหลีกเลี่ยงโค้ดที่อยู่เบื้องหลังและการใช้ทรัพยากรหากคุณมีแนวโน้มมาก (FMI, คลาสย่อย "พฤติกรรม" เพื่อดูประเภทของคุณสมบัติที่แนบมาแบบผสมผสานบนเตียรอยด์) สำหรับตอนนี้เราจะทำสิ่งนี้ด้วยตนเองในแต่ละมุมมอง:

a) สร้าง OpenXXXXXDialogEvent ด้วย payload ที่กำหนดเอง (คลาสที่ได้รับ DialogViewModel)

b) ให้มุมมองสมัครสมาชิกเหตุการณ์ในเหตุการณ์ OnDataContextChanged อย่าลืมซ่อนและยกเลิกการเป็นสมาชิกหากค่าเก่า! = null และในเหตุการณ์ Unloaded ของ Window

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

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

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


บริการโต้ตอบง่ายขึ้นมากแน่นอนและสิ่งที่ฉันมักจะทำ นอกจากนี้ยังทำให้ง่ายต่อการปิดไดอะล็อกมุมมองจากโมเดลมุมมองพาเรนต์ซึ่งจำเป็นเมื่อโมเดลมุมมองพาเรนต์ปิดหรือยกเลิก
Chris Bordeman

4

ใช้คำสั่ง freezable

<Grid>
        <Grid.DataContext>
            <WpfApplication1:ViewModel />
        </Grid.DataContext>


        <Button Content="Text">
            <Button.Command>
                <WpfApplication1:MessageBoxCommand YesCommand="{Binding MyViewModelCommand}" />
            </Button.Command>
        </Button>

</Grid>
public class MessageBoxCommand : Freezable, ICommand
{
    public static readonly DependencyProperty YesCommandProperty = DependencyProperty.Register(
        "YesCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register(
        "OKCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register(
        "CancelCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty NoCommandProperty = DependencyProperty.Register(
        "NoCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
        "Message",
        typeof (string),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata("")
        );

    public static readonly DependencyProperty MessageBoxButtonsProperty = DependencyProperty.Register(
        "MessageBoxButtons",
        typeof(MessageBoxButton),
        typeof(MessageBoxCommand),
        new FrameworkPropertyMetadata(MessageBoxButton.OKCancel)
        );

    public ICommand YesCommand
    {
        get { return (ICommand) GetValue(YesCommandProperty); }
        set { SetValue(YesCommandProperty, value); }
    }

    public ICommand OKCommand
    {
        get { return (ICommand) GetValue(OKCommandProperty); }
        set { SetValue(OKCommandProperty, value); }
    }

    public ICommand CancelCommand
    {
        get { return (ICommand) GetValue(CancelCommandProperty); }
        set { SetValue(CancelCommandProperty, value); }
    }

    public ICommand NoCommand
    {
        get { return (ICommand) GetValue(NoCommandProperty); }
        set { SetValue(NoCommandProperty, value); }
    }

    public MessageBoxButton MessageBoxButtons
    {
        get { return (MessageBoxButton)GetValue(MessageBoxButtonsProperty); }
        set { SetValue(MessageBoxButtonsProperty, value); }
    }

    public string Message
    {
        get { return (string) GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public void Execute(object parameter)
    {
        var messageBoxResult = MessageBox.Show(Message);
        switch (messageBoxResult)
        {
            case MessageBoxResult.OK:
                OKCommand.Execute(null);
                break;
            case MessageBoxResult.Yes:
                YesCommand.Execute(null);
                break;
            case MessageBoxResult.No:
                NoCommand.Execute(null);
                break;
            case MessageBoxResult.Cancel:
                if (CancelCommand != null) CancelCommand.Execute(null); //Cancel usually means do nothing ,so can be null
                break;

        }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;


    protected override Freezable CreateInstanceCore()
    {
        throw new NotImplementedException();
    }
}

รหัสนี้ต้องการงานบางอย่าง แต่เป็นความคิดที่ดีที่สุดโดยเฉพาะอย่างยิ่งสำหรับกล่องโต้ตอบของระบบเช่นกล่องโต้ตอบไฟล์หรือเครื่องพิมพ์ ไดอะล็อกเป็นของ View หากมีอะไรเกิดขึ้น สำหรับกล่องโต้ตอบไฟล์ผลลัพธ์ (ชื่อไฟล์ที่เลือก) สามารถส่งผ่านไปยังคำสั่งด้านในเป็นพารามิเตอร์
Anton Tykhyy

3

ฉันคิดว่าการจัดการกล่องโต้ตอบควรเป็นความรับผิดชอบของมุมมองและมุมมองจำเป็นต้องมีรหัสเพื่อสนับสนุนสิ่งนั้น

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

ประเด็นของฉันคือจุดรวมของรูปแบบ MVVM คือการแยกตรรกะทางธุรกิจออกจาก GUI ดังนั้นคุณไม่ควรผสมตรรกะ GUI (เพื่อแสดงข้อความโต้ตอบ) ในชั้นธุรกิจ (ViewModel)


2
VM จะไม่จัดการกับไดอะล็อกในตัวอย่างของฉันมันจะมีเหตุการณ์ที่ต้องใช้ไดอะล็อกเพื่อให้ไฟและส่งข้อมูลกลับในรูปแบบของ EventArgs ถ้ามุมมองมีความรับผิดชอบมันจะส่งข้อมูลกลับไปยัง VM ได้อย่างไร
Ray Booysen

สมมติว่า VM ต้องการลบบางสิ่ง VM เรียกเมธอดบน View Delete ซึ่งส่งคืนบูลีน มุมมองสามารถลบรายการโดยตรงและส่งคืนจริงหรือแสดงกล่องโต้ตอบการยืนยันและส่งกลับจริง / เท็จขึ้นอยู่กับคำตอบของผู้ใช้
คาเมรอน MacFarland

VM ไม่รู้อะไรเกี่ยวกับการโต้ตอบ แต่ขอให้มุมมองเพื่อลบบางสิ่งซึ่งมุมมองนั้นยืนยันหรือปฏิเสธ
คาเมรอน MacFarland

ฉันคิดเสมอว่าจุดของ MVVM คือ Model: ตรรกะทางธุรกิจ, ViewModel: GUI ตรรกะและมุมมอง: ไม่มีตรรกะ ข้อโต้แย้งใดในย่อหน้าสุดท้ายของคุณ กรุณาอธิบาย!
David Schmitt

2
ก่อนอื่นจะต้องมีการพิจารณาหากการขอคำยืนยันล่วงหน้าลบเป็นตรรกะทางธุรกิจหรือตรรกะดู ถ้าเป็นตรรกะทางธุรกิจเมธอด DeleteFile ในรูปแบบต้องไม่ทำ แต่ส่งคืนออบเจ็กต์คำถามการยืนยัน ซึ่งจะรวมถึงการอ้างอิงไปยังผู้รับมอบสิทธิ์ที่ทำการลบจริง หากไม่ใช่ตรรกะทางธุรกิจ VM ต้องสร้าง VM ของคำถามใน DeleteFileCommand โดยมีสมาชิก ICommand สองคน หนึ่งสำหรับใช่และหนึ่งสำหรับไม่ อาจมีข้อโต้แย้งสำหรับทั้งสองมุมมองและใน RL การใช้งานส่วนใหญ่อาจพบทั้งสองอย่าง
Guge

3

ทางเลือกที่น่าสนใจคือการใช้ตัวควบคุมที่รับผิดชอบในการแสดงมุมมอง (กล่องโต้ตอบ)

วิธีการทำงานนี้จะแสดงโดยWPF Application Framework (WAF)


3

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


3

ฉันใช้งานพฤติกรรมที่ฟังข้อความจาก ViewModel มันขึ้นอยู่กับโซลูชันของ Laurent Bugnion แต่เนื่องจากมันไม่ได้ใช้โค้ดด้านหลังและสามารถใช้ซ้ำได้มากกว่าฉันจึงคิดว่ามันดูดีกว่า

วิธีทำให้ WPF ทำงานเหมือนกับว่ารองรับ MVVM นอกกรอบ


1
คุณควรใส่รหัสแบบเต็มที่นี่เพราะนั่นคือสิ่งที่ SO ต้องการเพื่อคำตอบที่ดี อย่างไรก็ตามวิธีการเชื่อมโยงนั้นค่อนข้างเรียบร้อยดังนั้นขอบคุณสำหรับสิ่งนั้น! :)
Yoda

2
@yoda รหัสเต็มค่อนข้างยาวและนั่นเป็นสาเหตุที่ฉันต้องการเชื่อมโยงกับมัน ฉันได้แก้ไขคำตอบของฉันเพื่อสะท้อนการเปลี่ยนแปลงและชี้ไปที่ลิงก์ที่ไม่ขาดตอน
Elad Katz

ขอบคุณสำหรับการปรับปรุง อย่างไรก็ตามจะเป็นการดีกว่าถ้าจะให้โค้ด 3 เต็มหน้าเลื่อนที่นี่ใน SO มากกว่าลิงก์ที่อาจออฟไลน์ในสักวันหนึ่ง บทความที่ดีสำหรับหัวข้อที่ซับซ้อนนั้นค่อนข้างยาว - และฉันไม่เห็นประโยชน์ใด ๆ ในการเปิดแท็บใหม่สลับไปที่มันและเลื่อนไปที่นั่นเหนือการเลื่อนบนหน้า / แท็บเดียวกันกับที่ฉันเคยทำมาก่อนหน้านั้น ;)
Yoda

@EladKatz ฉันเห็นว่าคุณได้แบ่งปันการใช้ WPF ของคุณในลิงก์ที่คุณให้ไว้ คุณมีทางออกสำหรับการเปิดหน้าต่างใหม่จาก ViewModel หรือไม่? โดยทั่วไปฉันมีสองรูปแบบและแต่ละรูปแบบมีหนึ่ง ViewModel ผู้ใช้หนึ่งคลิกที่ปุ่มรูปแบบอื่นปรากฏขึ้นและ viewmodel1 ส่งวัตถุไปที่ viewmodel2 ในรูปแบบที่ 2 ผู้ใช้สามารถเปลี่ยนวัตถุและเมื่อพวกเขาปิดหน้าต่างวัตถุที่ปรับปรุงจะถูกส่งกลับไปยัง ViewModel แรก คุณมีทางออกสำหรับสิ่งนี้หรือไม่?
Ehsan

2

ฉันคิดว่ามุมมองอาจมีรหัสเพื่อจัดการเหตุการณ์จากโมเดลมุมมอง

ขึ้นอยู่กับเหตุการณ์ / สถานการณ์มันอาจมีทริกเกอร์เหตุการณ์ที่สมัครรับข้อมูลเพื่อดูเหตุการณ์จำลองและการกระทำอย่างน้อยหนึ่งอย่างที่จะเรียกใช้เพื่อตอบสนอง


2

ฉันมีสถานการณ์เดียวกันและรวม MessageBox ไว้ในตัวควบคุมที่มองไม่เห็น รายละเอียดอยู่ในบล็อกของฉัน

http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

เดียวกันสามารถขยายไปยังกล่องโต้ตอบ modal ใด ๆ การควบคุมการเรียกดูไฟล์เป็นต้น



1

Karl Shifflett ได้สร้างแอปพลิเคชันตัวอย่างสำหรับการแสดงกล่องโต้ตอบโดยใช้วิธีการบริการและวิธีการ Prism InteractionRequest

ฉันชอบวิธีการบริการ - มีความยืดหยุ่นน้อยลงดังนั้นผู้ใช้จึงมีโอกาสที่จะทำลายบางสิ่งได้น้อยลง :) นอกจากนี้ยังสอดคล้องกับส่วนของ WinForms ในแอปพลิเคชันของฉัน (MessageBox.Show) แต่ถ้าคุณวางแผนที่จะแสดงบทสนทนาที่แตกต่างกันจำนวนมาก วิธีที่ดีกว่าที่จะไป

http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/


1

ฉันรู้ว่ามันเป็นคำถามเก่า แต่เมื่อฉันทำการค้นหานี้ฉันพบคำถามที่เกี่ยวข้องมากมาย แต่ฉันไม่พบคำตอบที่ชัดเจนจริงๆ ดังนั้นฉันจึงใช้งานกล่องโต้ตอบ / messagebox / popin ของตัวเองและฉันแบ่งปันมัน!
ฉันคิดว่ามันเป็น "การพิสูจน์ MVVM" และฉันพยายามทำให้มันง่ายและเหมาะสม แต่ฉันใหม่สำหรับ WPF ดังนั้นอย่าลังเลที่จะแสดงความคิดเห็นหรือแม้แต่ทำการร้องขอการดึง

https://github.com/Plasma-Paris/Plasma.WpfUtils

คุณสามารถใช้สิ่งนี้:

public RelayCommand YesNoMessageBoxCommand { get; private set; }
async void YesNoMessageBox()
{
    var result = await _Service.ShowMessage("This is the content of the message box", "This is the title", System.Windows.MessageBoxButton.YesNo);
    if (result == System.Windows.MessageBoxResult.Yes)
        // [...]
}

หรือเช่นนี้ถ้าคุณต้องการ popin ที่ซับซ้อนมากขึ้น:

var result = await _Service.ShowCustomMessageBox(new MyMessageBoxViewModel { /* What you want */ });

และมันกำลังแสดงสิ่งต่าง ๆ เช่นนี้:

2


1

วิธีการมาตรฐาน

หลังจากใช้เวลาหลายปีการจัดการกับปัญหานี้ใน WPF ในที่สุดผมก็คิดออกมาตรฐานวิธีการในการดำเนินการในไดอะล็อก WPF นี่คือข้อดีของวิธีการนี้:

  1. ทำความสะอาด
  2. ไม่ละเมิดรูปแบบการออกแบบ MVVM
  3. ViewModal ไม่อ้างอิงไลบรารี UI ใด ๆ (WindowBase, PresentationFramework ฯลฯ )
  4. เหมาะสำหรับการทดสอบอัตโนมัติ
  5. สามารถเปลี่ยนไดอะล็อกได้อย่างง่ายดาย

ดังนั้นกุญแจคืออะไร มันเป็นDI + IoC

นี่คือวิธีการทำงาน ฉันกำลังใช้ MVVM Light แต่วิธีนี้อาจขยายไปยังกรอบงานอื่นเช่นกัน:

  1. เพิ่มโปรเจ็กต์ WPF Application ให้กับโซลูชันของคุณ เรียกว่าApp
  2. เพิ่มไลบรารีคลาส ViewModal เรียกว่าVM
  3. แอพอ้างอิงโครงการ VM โครงการ VM ไม่รู้อะไรเกี่ยวกับแอพ
  4. เพิ่มการอ้างอิง NuGet เพื่อ MVVM แสงทั้งสองโครงการ วันนี้ฉันกำลังใช้MVVM Light Standard อยู่แต่คุณก็โอเคกับเวอร์ชันเต็มของ Framework ด้วย
  5. เพิ่มอินเตอร์เฟสIDialogServiceให้กับโครงการ VM:

    public interface IDialogService
    {
      void ShowMessage(string msg, bool isError);
      bool AskBooleanQuestion(string msg);
      string AskStringQuestion(string msg, string default_value);
    
      string ShowOpen(string filter, string initDir = "", string title = "");
      string ShowSave(string filter, string initDir = "", string title = "", string fileName = "");
      string ShowFolder(string initDir = "");
    
      bool ShowSettings();
    }
  6. เปิดเผยคุณสมบัติคงที่สาธารณะของIDialogServiceประเภทในของคุณViewModelLocatorแต่ออกจากส่วนการลงทะเบียนสำหรับชั้นดูเพื่อดำเนินการ นี่คือกุญแจสำคัญ :

    public static IDialogService DialogService => SimpleIoc.Default.GetInstance<IDialogService>();
  7. เพิ่มการใช้งานอินเทอร์เฟซนี้ในโครงการแอพ

    public class DialogPresenter : IDialogService
    {
        private static OpenFileDialog dlgOpen = new OpenFileDialog();
        private static SaveFileDialog dlgSave = new SaveFileDialog();
        private static FolderBrowserDialog dlgFolder = new FolderBrowserDialog();
    
        /// <summary>
        /// Displays a simple Information or Error message to the user.
        /// </summary>
        /// <param name="msg">String text that is to be displayed in the MessageBox</param>
        /// <param name="isError">If true, Error icon is displayed. If false, Information icon is displayed.</param>
        public void ShowMessage(string msg, bool isError)
        {
                if(isError)
                        System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.OK, MessageBoxImage.Error);
                else
                        System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.OK, MessageBoxImage.Information);
        }
    
        /// <summary>
        /// Displays a Yes/No MessageBox.Returns true if user clicks Yes, otherwise false.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public bool AskBooleanQuestion(string msg)
        {
                var Result = System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
                return Result;
        }
    
        /// <summary>
        /// Displays Save dialog. User can specify file filter, initial directory and dialog title. Returns full path of the selected file if
        /// user clicks Save button. Returns null if user clicks Cancel button.
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="initDir"></param>
        /// <param name="title"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public string ShowSave(string filter, string initDir = "", string title = "", string fileName = "")
        {
                if (!string.IsNullOrEmpty(title))
                        dlgSave.Title = title;
                else
                        dlgSave.Title = "Save";
    
                if (!string.IsNullOrEmpty(fileName))
                        dlgSave.FileName = fileName;
                else
                        dlgSave.FileName = "";
    
                dlgSave.Filter = filter;
                if (!string.IsNullOrEmpty(initDir))
                        dlgSave.InitialDirectory = initDir;
    
                if (dlgSave.ShowDialog() == DialogResult.OK)
                        return dlgSave.FileName;
                else
                        return null;
        }
    
    
        public string ShowFolder(string initDir = "")
        {
                if (!string.IsNullOrEmpty(initDir))
                        dlgFolder.SelectedPath = initDir;
    
                if (dlgFolder.ShowDialog() == DialogResult.OK)
                        return dlgFolder.SelectedPath;
                else
                        return null;
        }
    
    
        /// <summary>
        /// Displays Open dialog. User can specify file filter, initial directory and dialog title. Returns full path of the selected file if
        /// user clicks Open button. Returns null if user clicks Cancel button.
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="initDir"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        public string ShowOpen(string filter, string initDir = "", string title = "")
        {
                if (!string.IsNullOrEmpty(title))
                        dlgOpen.Title = title;
                else
                        dlgOpen.Title = "Open";
    
                dlgOpen.Multiselect = false;
                dlgOpen.Filter = filter;
                if (!string.IsNullOrEmpty(initDir))
                        dlgOpen.InitialDirectory = initDir;
    
                if (dlgOpen.ShowDialog() == DialogResult.OK)
                        return dlgOpen.FileName;
                else
                        return null;
        }
    
        /// <summary>
        /// Shows Settings dialog.
        /// </summary>
        /// <returns>true if User clicks OK button, otherwise false.</returns>
        public bool ShowSettings()
        {
                var w = new SettingsWindow();
                MakeChild(w); //Show this dialog as child of Microsoft Word window.
                var Result = w.ShowDialog().Value;
                return Result;
        }
    
        /// <summary>
        /// Prompts user for a single value input. First parameter specifies the message to be displayed in the dialog 
        /// and the second string specifies the default value to be displayed in the input box.
        /// </summary>
        /// <param name="m"></param>
        public string AskStringQuestion(string msg, string default_value)
        {
                string Result = null;
    
                InputBox w = new InputBox();
                MakeChild(w);
                if (w.ShowDialog(msg, default_value).Value)
                        Result = w.Value;
    
                return Result;
        }
    
        /// <summary>
        /// Sets Word window as parent of the specified window.
        /// </summary>
        /// <param name="w"></param>
        private static void MakeChild(System.Windows.Window w)
        {
                IntPtr HWND = Process.GetCurrentProcess().MainWindowHandle;
                var helper = new WindowInteropHelper(w) { Owner = HWND };
        }
    }
  8. ในขณะที่บางส่วนของฟังก์ชั่นเหล่านี้มีทั่วไป ( ShowMessage, AskBooleanQuestionฯลฯ ), อื่น ๆ มีความเฉพาะเจาะจงกับโครงการและการใช้งานที่กำหนดเองนี้Windows คุณสามารถเพิ่มหน้าต่างที่กำหนดเองได้มากขึ้นในแบบเดียวกัน ที่สำคัญคือเพื่อให้องค์ประกอบ UI เฉพาะในชั้นดูและเพิ่งเปิดเผยข้อมูลที่ส่งกลับโดยใช้ POCOs ในชั้น
  9. ทำการลงทะเบียน IoC อินเทอร์เฟซของคุณในมุมมองเลเยอร์โดยใช้คลาสนี้ คุณสามารถทำได้ในตัวสร้างมุมมองหลักของคุณ (หลังการInitializeComponent()โทร):

    SimpleIoc.Default.Register<IDialogService, DialogPresenter>();
  10. ไปแล้ว ตอนนี้คุณสามารถเข้าถึงฟังก์ชั่นโต้ตอบทั้งหมดของคุณได้ทั้งที่ VM และเลเยอร์การดู เลเยอร์ VM ของคุณสามารถเรียกใช้ฟังก์ชันเหล่านี้ดังนี้:

    var NoTrump = ViewModelLocator.DialogService.AskBooleanQuestion("Really stop the trade war???", "");
  11. ดูสะอาดตา เลเยอร์ VM ไม่รู้อะไรเลยเกี่ยวกับวิธีการที่จะแสดงคำถามใช่ / ไม่ใช่สำหรับผู้ใช้โดยเลเยอร์ UI และยังสามารถทำงานกับผลลัพธ์ที่ส่งคืนจากไดอะล็อกได้

สิทธิพิเศษอื่น ๆ ฟรี

  1. สำหรับการเขียนการทดสอบหน่วยคุณสามารถจัดเตรียมการปรับใช้IDialogServiceในโครงการทดสอบของคุณและลงทะเบียนคลาสนั้นใน IoC ใน Constructor คลาสทดสอบของคุณ
  2. คุณจะต้องนำเข้าเนมสเปซบางอย่างเช่นMicrosoft.Win32เพื่อเข้าถึงกล่องโต้ตอบเปิดและบันทึก ฉันปล่อยให้พวกเขาออกเพราะยังมีรุ่น WinForms ของกล่องโต้ตอบเหล่านี้พร้อมบางคนอาจต้องการสร้างรุ่นของตัวเอง โปรดทราบว่าตัวระบุบางตัวที่ใช้DialogPresenterเป็นชื่อหน้าต่างของฉันเอง (เช่นSettingsWindow) คุณจะต้องลบมันออกจากทั้งอินเทอร์เฟซและการติดตั้งหรือเตรียมหน้าต่างของคุณเอง
  3. หาก VM ของคุณทำการมัลติเธรดให้โทรหา MVVM Light DispatcherHelper.Initialize()ก่อนใครในวงจรชีวิตของแอปพลิเคชันของคุณ
  4. ยกเว้นDialogPresenterที่จะถูกฉีดเข้าไปในเลเยอร์มุมมอง ViewModals อื่น ๆ ควรได้รับการลงทะเบียนในViewModelLocatorและจากนั้นควรมีการเปิดเผยคุณสมบัติคงที่สาธารณะของประเภทนั้นเพื่อให้เลเยอร์มุมมองใช้งานได้ บางสิ่งเช่นนี้

    public static SettingsVM Settings => SimpleIoc.Default.GetInstance<SettingsVM>();
  5. ส่วนใหญ่กล่องโต้ตอบของคุณไม่ควรมีโค้ดที่ล้าสมัยใด ๆ เช่นการเชื่อมโยงหรือการตั้งค่า DataContext เป็นต้นคุณไม่ควรส่งสิ่งต่าง ๆ เป็นพารามิเตอร์คอนสตรัคเตอร์ XAML สามารถทำทุกอย่างให้คุณได้เช่นนี้

    <Window x:Class="YourViewNamespace.SettingsWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:local="clr-namespace:YourViewProject"
      xmlns:vm="clr-namespace:YourVMProject;assembly=YourVMProject"
      DataContext="{x:Static vm:ViewModelLocator.Settings}"
      d:DataContext="{d:DesignInstance Type=vm:SettingsVM}" />
  6. การตั้งค่าDataContextด้วยวิธีนี้ช่วยให้คุณได้รับประโยชน์จากการออกแบบทุกประเภทเช่น Intellisense และการเติมภาพอัตโนมัติ

หวังว่าจะช่วยทุกคน


0

ผมกำลังขบคิดปัญหาที่คล้ายกันเมื่อถามว่ารูปแบบมุมมองสำหรับงานหรือโต้ตอบควรมีลักษณะดังนี้

โซลูชันปัจจุบันของฉันมีลักษณะดังนี้:

public class SelectionTaskModel<TChoosable> : ViewModel
    where TChoosable : ViewModel
{
    public SelectionTaskModel(ICollection<TChoosable> choices);
    public ReadOnlyCollection<TChoosable> Choices { get; }
    public void Choose(TChoosable choosen);
    public void Abort();
}

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


0

ฉันต่อสู้กับปัญหาเดียวกัน ฉันคิดวิธีการสื่อสารระหว่าง View และ ViewModel ขึ้นมา คุณสามารถเริ่มส่งข้อความจาก ViewModel ไปยัง View เพื่อบอกให้แสดงกล่องข้อความและจะรายงานกลับพร้อมผลลัพธ์ จากนั้น ViewModel สามารถตอบสนองต่อผลลัพธ์ที่ส่งคืนจากมุมมอง

ฉันแสดงสิ่งนี้ในบล็อกของฉัน :


0

ฉันได้เขียนบทความที่ครอบคลุมเกี่ยวกับหัวข้อนี้มากและพัฒนาไลบรารีป๊อปอินสำหรับ MVVM Dialogs การยึดมั่นอย่างเข้มงวดกับ MVVM นั้นไม่เพียง แต่เป็นไปได้ แต่สะอาดมากเมื่อติดตั้งอย่างถูกต้องและสามารถขยายไปยังไลบรารีของบุคคลที่สามได้อย่างง่ายดายซึ่งไม่ยึดติดกับตัวเอง:

https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM


0

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

สิ่งนี้ไม่สำคัญ แต่ก็ไม่ซับซ้อนเกินไป และมันถูกสร้างขึ้นในปริซึมดังนั้นจึงเป็นการปฏิบัติที่ดีที่สุด (หรือดีกว่า) IMHO

2 เซ็นต์ของฉัน!


-1

แก้ไข: ใช่ฉันยอมรับว่านี่ไม่ใช่วิธี MVVM ที่ถูกต้องและตอนนี้ฉันกำลังใช้บางสิ่งที่คล้ายกับสิ่งที่ blindmeis แนะนำ

หนึ่งในวิธีที่คุณสามารถทำได้คือ

ในโมเดลมุมมองหลักของคุณ (ที่คุณเปิดโมดัล)

void OpenModal()
{
    ModalWindowViewModel mwvm = new ModalWindowViewModel();
    Window mw = new Window();
    mw.content = mwvm;
    mw.ShowDialog()
    if(mw.DialogResult == true)
    { 
        // Your Code, you can access property in mwvm if you need.
    }
}

และใน Modal Window View / ViewModel ของคุณ:

XAML:

<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
<Button Margin="2" VerticalAlignment="Center" Name="cancelButton" IsCancel="True">Cancel</Button>

ViewModel:

public ICommand OkCommand
{
    get
    {
        if (_okCommand == null)
        {
            _okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
        }
        return _okCommand ;
    }
}

void DoOk(Window win)
{
    <!--Your Code-->
    win.DialogResult = true;
    win.Close();
}

bool CanDoOk(Window win) { return true; }

หรือคล้ายกับสิ่งที่โพสต์ไว้ที่นี่WPF MVVM: วิธีปิดหน้าต่าง


2
ฉันไม่ใช่ downvote แต่ฉันสงสัยว่าเป็นเพราะโมเดลวิวมีการอ้างอิงโดยตรงกับมุมมอง
Brian Gideon

@BrianGideon ขอบคุณสำหรับความคิดเห็นของคุณ ฉันยอมรับว่านี่ไม่ใช่วิธีแก้ปัญหาแบบแยกคู่ ในความเป็นจริงฉันไม่ได้ใช้บางสิ่งที่คล้ายกับคำแนะนำจาก blindmeis ขอบคุณอีกครั้ง.
Simone

มันเป็นรูปแบบที่ไม่ดีในการเข้าถึงมุมมองเมื่อมันไม่ง่าย
Chris Bordeman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.