WPF MVVM - 单击时更新下拉列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26040851/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF MVVM - Update Dropdown When Clicked
提问by Zeus82
I have a dropdown (ComboBox) that displays all the com ports available on a machine. Now, ports come and go when you connect and disconnect devices.
我有一个下拉列表 ( ComboBox) 显示机器上所有可用的 com 端口。现在,当您连接和断开设备时,端口会来来去去。
For performance reasons I don't want to keep calling System.IO.Ports.SerialPort.GetPortNames(), but rather just call that when the user clicks on the Combobox? Is this possible? Is there an MVVM approach to this problem?
出于性能原因,我不想继续调用System.IO.Ports.SerialPort.GetPortNames(),而是在用户单击组合框时调用它?这可能吗?是否有针对此问题的 MVVM 方法?
回答by Mike Fuchs
Use InvokeCommandAction.
使用InvokeCommandAction.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
DropDownOpenedCommand is an ICommandproperty on your ViewModel.
DropDownOpenedCommand 是ICommand您的 ViewModel 上的一个属性。
<ComboBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="DropDownOpened">
<i:InvokeCommandAction Command="{Binding DropDownOpenedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Edit: obviously DropDownOpened not SelectionChanged, as Patrice commented.
编辑:显然 DropDownOpened 不是 SelectionChanged,正如 Patrice 评论的那样。
回答by Theodosius Von Richthofen
You can use something like MVVMLight's EventToCommand to accomplish this. Basically, the event of clicking the combo would be hooked to your MVVM command binding, which would then fire the method that calls GetPortNames().
您可以使用 MVVMLight 的 EventToCommand 之类的东西来完成此操作。基本上,单击组合的事件将连接到您的 MVVM 命令绑定,然后会触发调用 GetPortNames() 的方法。
Here are some alternatives:
以下是一些替代方案:
MVVM Light: Adding EventToCommand in XAML without Blend, easier way or snippet?(check the accepted answer)
MVVM Light:在没有 Blend 的 XAML 中添加 EventToCommand,更简单的方法还是片段?(检查接受的答案)
http://www.danharman.net/2011/08/05/binding-wpf-events-to-mvvm-viewmodel-commands/(Prism)
http://www.danharman.net/2011/08/05/binding-wpf-events-to-mvvm-viewmodel-commands/(棱镜)
回答by GEEF
What I would recommend is scrapping the 'only update on clicks' idea, and just use binding and notifications for this (unless for some reason you think there will be so many Connect/Disconnect events it will slow your system). The simplest version of that would be a dependency property.
我建议放弃“仅更新点击次数”的想法,为此只使用绑定和通知(除非出于某种原因您认为会有太多的连接/断开事件会减慢您的系统速度)。最简单的版本是依赖属性。
Provide an IObservableList<Port>property as a dependency property on your ViewModel like this:
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>));
Now you may add/remove items to/from that list whenever you connect or disconnect devices, just do not replace the list. This will force the list to send off a ListChangedEventfor each action on the list, and the ComboBox (or any other bound UI) will react to those events.
现在,无论何时连接或断开设备,您都可以在该列表中添加/删除项目,只是不要替换列表。这将强制列表为ListChangedEvent列表上的每个操作发送一个,并且 ComboBox(或任何其他绑定的 UI)将对这些事件做出反应。
This should be performant enough for you, as this will only cause the UI ComboBox to update whenever an event goes through.
这对您来说应该足够高效,因为这只会导致 UI ComboBox 在事件发生时更新。
回答by bdimag
I took a stab at routing events to a command:
我尝试将事件路由到命令:
XAML:
XAML:
<ComboBox
ItemsSource="{Binding Items}"
local:ControlBehavior.Event="SelectionChanged"
local:ControlBehavior.Command="{Binding Update}" />
Code:
代码:
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);
}
}
}

