2014-09-25 3 views
2

У меня есть выпадающий список (ComboBox), который отображает все COM-порты, доступные на машине. Теперь порты приходят и уходят при подключении и отключении устройств.WPF MVVM - обновление раскрывающегося списка при нажатии

По соображениям производительности я не хочу продолжать звонить System.IO.Ports.SerialPort.GetPortNames(), а просто называть это, когда пользователь нажимает на Combobox? Это возможно? Есть ли подход MVVM к этой проблеме?

+3

Обрабатывать событие DropDownOpened и вызывать 'GetPortsNames' в обработчике для обновления элементов. С MVVM вы бы сделали то же самое с каким-то «Command», а затем обновили данные в ViewModel соответственно. –

ответ

0

Вы можете использовать что-то вроде EventToCommand MVVMLight для этого. В принципе, событие щелчка комбо будет привязано к вашей привязке команды MVVM, которая затем запускает метод, вызывающий GetPortNames().

Вот некоторые варианты:

MVVM Light: Adding EventToCommand in XAML without Blend, easier way or snippet? (проверить принятый ответ)

http://www.danharman.net/2011/08/05/binding-wpf-events-to-mvvm-viewmodel-commands/ (Prism)

0

Что я рекомендовал бы это слом «только обновление по кликам» идеи, и просто использовать привязки и уведомлений для этого (если по какой-то причине вы не думаете, что будет так много событий Connect/Disconnect, это замедлит вашу систему). Простейшей версией этого является свойство зависимости.

Обеспечить IObservableList<Port> свойство как свойства зависимостей ваших ViewModel, как это:

/// <summary> 
    /// Gets or sets... 
    /// </summary> 
    public IObservableList<Port> Ports 
    { 
     get { return (IObservableList<Port>)GetValue(PortsProperty); } 
     set { SetValue(PortsProperty, value); } 
    } 

    public static readonly DependencyProperty PortsProperty = DependencyProperty.Register("Ports", typeof(IObservableList<Port>), typeof(MyViewModelClass), new PropertyMetadata(new ObservableList<Port>)); 

Теперь вы можете добавлять/удалять элементы в/из этого списка при каждом подключении или отсоединении устройства, просто не заменить список , Это заставит список отправить ListChangedEvent для каждого действия в списке, а ComboBox (или любой другой связанный пользовательский интерфейс) будет реагировать на эти события.

Это должно быть достаточно для вас, поскольку это приведет к обновлению пользовательского интерфейса ComboBox всякий раз, когда происходит событие.

+0

IMHO, опасения OP относительно производительности не являются обновлениями пользовательского интерфейса, а часто вызывают 'System.IO.Ports.SerialPort.GetPortNames()'. – quinmars

0

я принял удар на маршрутизации событий в команде:

XAML:

<ComboBox 
    ItemsSource="{Binding Items}" 
    local:ControlBehavior.Event="SelectionChanged" 
    local:ControlBehavior.Command="{Binding Update}" /> 

Код:

using System; 
using System.Reflection; 
using System.Windows; 
using System.Windows.Input; 

namespace StackOverflow 
{ 
    public class ControlBehavior 
    { 
     public static DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(ControlBehavior)); 
     public static DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(ControlBehavior)); 
     public static DependencyProperty EventProperty = DependencyProperty.RegisterAttached("Event", typeof(string), typeof(ControlBehavior), new PropertyMetadata(PropertyChangedCallback)); 

     public static void EventHandler(object sender, EventArgs e) 
     { 
      var s = (sender as DependencyObject); 
      if (s != null) 
      { 
       var c = (ICommand)s.GetValue(CommandProperty); 
       var p = s.GetValue(CommandParameterProperty); 
       if (c != null && c.CanExecute(s)) 
        c.Execute(s); 
      } 
     } 

     public static void PropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs a) 
     { 
      if (a.Property == EventProperty) 
      { 
       EventInfo ev = o.GetType().GetEvent((string)a.NewValue); 
       if (ev != null) 
       { 
        var del = Delegate.CreateDelegate(ev.EventHandlerType, typeof(ControlBehavior).GetMethod("EventHandler")); 
        ev.AddEventHandler(o, del); 
       } 
      } 
     } 

     public string GetEvent(UIElement element) 
     { 
      return (string)element.GetValue(EventProperty); 
     } 
     public static void SetEvent(UIElement element, string value) 
     { 
      element.SetValue(EventProperty, value); 
     } 
     public ICommand GetCommand(UIElement element) 
     { 
      return (ICommand)element.GetValue(CommandProperty); 
     } 
     public static void SetCommand(UIElement element, ICommand value) 
     { 
      element.SetValue(CommandProperty, value); 
     } 
     public object GetCommandParameter(UIElement element) 
     { 
      return element.GetValue(CommandParameterProperty); 
     } 
     public static void SetCommandParameter(UIElement element, object value) 
     { 
      element.SetValue(CommandParameterProperty, value); 
     } 

    } 
} 
+1

Убедитесь, что вы отменили регистрацию своего события, когда свойство изменилось, чтобы предотвратить утечку памяти. Я сделал почти то же самое [здесь] (http: // stackoverflow.com/a/16317999/385995), но только потому, что InvokeCommandAction не поддерживает передачу EventArgs - если вам это не нужно, я думаю, что InvokeCommandAction намного проще (см. мой ответ). –

7

Использование InvokeCommandAction.

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 

DropDownOpenedCommand является ICommand свойство на вашем ViewModel.

<ComboBox> 
    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="DropDownOpened"> 
      <i:InvokeCommandAction Command="{Binding DropDownOpenedCommand}"/> 
     </i:EventTrigger> 
    </i:Interaction.Triggers> 
</ComboBox> 

Редактировать: очевидно, DropDownOpened not SelectionChanged, как прокомментировал Патрис.

+1

Мне это нравится - пришлось выйти и получить пакет NuGet 'System.Windows.Interactivity v4.0 для WPF', чтобы он работал – bdimag

Смежные вопросы