M-V-VM - هل هناك أي أمثلة لاستخدام الأوامر في ViewModel؟

StackOverflow https://stackoverflow.com/questions/424927

  •  05-07-2019
  •  | 
  •  

سؤال

لقد قمت بتطوير تطبيق LOB كبير جدًا باستخدام نكهة M-V-VM الخاصة بي والتي أسميها M-V-MC (Model-View-ModelController)، وهو نوع من الجمع بين M-V-C وM-V-VM.كنت قد نشرت هذه الإجابة فيما يتعلق بكيفية إنشاء مثيل للمشاهدات في M-V-VM للسؤال "ما هي الأخطاء الأكثر شيوعًا في تطوير wpf".

سام أدليت بالتعليق التالي بخصوص إجابتي:

يؤدي هذا إلى إنشاء سؤال متابعة:كيف تنشئ وجهات النظر؟أستخدم RelayCommands لربط الإجراءات من العرض إلى ViewModel ، وبالتالي فإن العرض لا يعرف حتى أن إجراء تم إطلاقه ، لا يعلم أنه يجب أن يفتح وجهة نظر جديدة.حل:إنشاء حدث في VM للعرض للاشتراك؟

عندما بدأت في تطوير M-V-VM، كانت لدي فكرة مفادها أن كل شيء يجب أن يكون موجودًا في ViewModel، وقد درست الكثير من الأمثلة من أشخاص مثل جوش سميث و كارل شيفليت.ومع ذلك، لم أتوصل بعد إلى مثال جيد عندما يحتاج الأمر إلى البقاء في ViewModel.

على سبيل المثال، لنفترض أن لدي ListView يعرض العملاء، وزرًا أنقر عليه للسماح لي بتحرير العميل المحدد حاليًا.يرتبط ListView (عرض) بـ CustomerVM (ViewModel).يؤدي النقر فوق الزر إلى تشغيل EditCustomerCommand الذي يفتح نافذة منبثقة تسمح لي بتحرير جميع خصائص CustomerVM.أين يعيش أمر EditCustomerCommand هذا؟إذا كان الأمر يتضمن فتح نافذة (وظيفة واجهة المستخدم)، ألا ينبغي تعريفها في الكود الموجود خلف العرض؟alt text

هل لدى أي شخص أي أمثلة على متى يجب علي تحديد أمر في العرض مقابل ViewModel؟

ماثيو رايت الدول أدناه:

الجديد وحذف من القائمة سيكون أمثلة جيدة.في هذه الحالات ، تتم إضافة سجل فارغ أو يتم حذف السجل الحالي بواسطة ViewModel.يجب أن يكون أي إجراء اتخذته الرأي استجابةً لتلك الأحداث التي تحدث.

لذا، إذا قمت بالنقر فوق الزر الجديد، ماذا يحدث؟تم إنشاء مثيل جديد لـ CustomerVM بواسطة Parent ViewModel وإضافته إلى مجموعته، أليس كذلك؟إذن كيف سيتم فتح شاشة التحرير الخاصة بي؟يجب أن يقوم العرض بإنشاء مثيل جديد لـ Customer ViewModel وتمريره إلى طريقة ParentVM.Add(newlyCreatedVM) أليس كذلك؟

لنفترض أنني قمت بحذف سجل عميل من خلال برنامج RemoveCommand الموجود على الجهاز الظاهري.يتصل VM بطبقة الأعمال ويحاول حذف السجل.لا يمكن ذلك، لذا فهو يُرجع رسالة إلى الجهاز الافتراضي.أريد عرض هذه الرسالة في مربع الحوار.كيف يقوم العرض بإخراج الرسالة من إجراء الأمر؟

هل كانت مفيدة؟

المحلول

لم أعتقد أبدًا أنني سأرى نفسي مقتبسًا في سؤال.

لقد فكرت في هذا السؤال بنفسي لبعض الوقت، واتخذت قرارًا عمليًا إلى حد ما فيما يتعلق بقاعدة التعليمات البرمجية الخاصة بي:

في قاعدة التعليمات البرمجية الخاصة بي، يتم استدعاء ViewModel عند حدوث الإجراءات، وأردت أن يظل على هذا النحو.بالإضافة إلى ذلك، لا أريد أن يتحكم ViewModel في طرق العرض.

ماذا فعلت؟
أضفت وحدة تحكم للملاحة:

public interface INavigation
{
  void NewContent(ViewModel viewmodel);
  void NewWindow(ViewModel viewmodel);
}

تحتوي وحدة التحكم هذه على إجراءين:يعرض NewContent() محتوى جديدًا في النافذة الحالية، بينما يقوم NewWindow() بإنشاء نافذة جديدة، ويملأها بالمحتوى ويعرضه.
بالطبع ليس لدى نماذج العرض الخاصة بي أي فكرة عن العرض الذي سيتم عرضه.لكنهم يعرفون نموذج العرض الذي يريدون عرضه، لذلك وفقًا لمثالك عند تنفيذ برنامج RemoveCommand، فإنه سيتم استدعاء وظيفة خدمة التنقل NewWindow (جديد ValidateCustomerDeletedViewModel ()) لإظهار نافذة تفيد بأنه "تم حذف العميل" (مبالغة في استخدام صندوق الرسائل البسيط هذا، ولكن سيكون من السهل الحصول على وظيفة متصفح خاصة لصناديق الرسائل البسيطة).

كيف يحصل نموذج العرض على خدمة الملاحة؟

تحتوي فئة viewmodel الخاصة بي على خاصية لوحدة التحكم في التنقل:

public class ViewModel
{
  public INavigation Navigator { get; set; }
  [...]
}

عندما يتم إرفاق نموذج عرض بنافذة (أو أي شيء يعرض العرض)، ستقوم النافذة بتعيين خاصية Navigator، بحيث يمكن لنموذج العرض الاتصال بها.

كيف يقوم المستكشف بإنشاء طريقة العرض لنموذج العرض؟

يمكن أن يكون لديك قائمة بسيطة للعرض الذي سيتم إنشاؤه لنموذج العرض، وفي حالتي يمكنني استخدام انعكاس بسيط نظرًا لأن الأسماء متطابقة:

public static FrameworkElement CreateView(ViewModel viewmodel)
{
  Type vmt = viewmodel.GetType();
  // big bad dirty hack to get the name of the view, but it works *cough*
  Type vt = Type.GetType(vmt.AssemblyQualifiedName.Replace("ViewModel, ", "View, ")); 
  return (FrameworkElement)Activator.CreateInstance(vt, viewmodel);
}

بالطبع يحتاج العرض إلى مُنشئ يقبل نموذج العرض كمعلمة:

public partial class ValidateCustomerDeletedView : UserControl
{
  public ValidateCustomerDeletedView(ValidateCustomerDeletedViewModel dac)
  {
    InitializeComponent();
    this.DataContext = dac;
  }
}

كيف تبدو نافذتي؟

بسيط:تنفذ نافذتي الرئيسية واجهة INavigation، وتعرض صفحة البداية عند الإنشاء.انظر بنفسك:

public partial class MainWindow : Window, INavigation
{
  public MainWindow()
  {
    InitializeComponent();
    NewContent(new StartPageViewModel());
  }

  public MainWindow(ViewModel newcontrol)
  {
    InitializeComponent();
    NewContent(newcontrol);
  }

  #region INavigation Member
  public void NewContent(ViewModel newviewmodel)
  {
    newviewmodel.Navigator = this;
    FrameworkElement ui = App.CreateView(newviewmodel);
    this.Content = ui;
    this.DataContext = ui.DataContext;
  }

  public void NewWindow(ViewModel viewModel)
  {
    MainWindow newwindow = new MainWindow(viewModel);
    newwindow.Show();
  }
  #endregion
}

(يعمل هذا بشكل جيد مع NavigationWindow والتفاف العرض في صفحة)

بالطبع هذا قابل للاختبار، حيث يمكن الاستهزاء بوحدة التحكم في الملاحة بسهولة.

لست متأكدًا حقًا مما إذا كان هذا هو الحل الأمثل، لكنه يعمل بشكل جيد بالنسبة لي الآن.أي أفكار وتعليقات هي موضع ترحيب!

نصائح أخرى

لديك الحذف حالة مربع رسالة، ومجردة من رسائل للعبر واجهة. مشابهة لهذه. وأود أيضا أن حقن هذه الواجهات لبلدي التطبيق WPF.

ومنشئ

    public MyViewModel(IMessage msg)
    {
      _msg = msg;
    }

وبعد ذلك، في طريقة حذف الأسلوب على ViewModel ... شيء من هذا القبيل

    public void Delete()
    {
      if(CanDelete)
      {
        //do the delete 
      }
      else
      {
        _msg.Show("You can't delete this record");
      }
    }

وهذا سيجعل من قابلة للاختبار، يمكنك سد العجز في مختلف وتطبيقات الكتروني التي لا تظهر في الواقع MESSAGEBOX. تلك قد طباعة فقط إلى وحدة التحكم، لأغراض الاختبار. ومن الواضح أن التطبيق WPF الخاص بك قد يكون لها تنفيذ مثل

public class MessageBoxQuestion : IMessage
{
   public void Show(string message)
   {
     MessageBox.Show(message);
   }
}

والقيام بذلك يجعل اختبار طرق مختلفة (اعتقد نعم / لا الحوارات) سهلة جدا ومباشرة إلى الأمام. يمكنك أن تتخيل تأكيدا الحذف. هل يمكن استخدام إما مثيل ملموسة من الكتروني للعودة صح / خطأ لتأكيد أو يسخر من الحاوية أثناء الاختبار.

[Test]
public void Can_Cancel_Delete()
{
  var vm = new ProductViewModel(_cancel);
  ...

}
[Test]
public void Can_Confirm_Delete()
{
  var vm = new ProductViewModel(_yes);
  ...

}

لديك سؤال آخر حول موعد استخدام الأوامر، I مثيل إضافة وجهات النظر الجديدة أو تفاصيل من عرض في السؤال. كما أن لديك في المثال الخاص بك. يتم إنشاء مثيل على المشاهدات فقط من المشاهدات الأخرى في التطبيق لدينا. أنا لا أستعمل أمر في تلك الحالات. ولكن أنا لا تستخدم خصائص ViewModel من وجهة نظر الأم إلى عرض الطفل.

public void Object_DoubleClick(object sender, EventArgs e)
{
  var detailView = new DetailView(ViewModel.Product);
  detailView.Show();
}

وآمل أن يساعد هذا!

والجديدة وحذف من قائمة سيكون أمثلة جيدة. في هذه الحالات، يتم إضافة سجل فارغ أو يتم حذف السجل الحالي من قبل ViewModel. وينبغي أن يكون أي إجراء من جانب وجهة النظر المتخذة استجابة لتلك الأحداث التي وقعت.

وطريقة واحدة سيكون لاستخدام كائن معلمة الأمر أن طبقة رجال الأعمال يمكن تعديل وVM الخاص بك يمكن معالجة الأمر بعد في تنفيذها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top