使用MVVM

时间:2018-06-12 12:20:13

标签: c# wpf mvvm

我正在使用Visual Studio 2017 Communitiy版本/ .NET Framework 4.6.1 / WPF。顺便说一句。我使用MVVM。

我的目标是在 Combobox 中显示所有可用的串行端口。我已经实现了这个,但是现在我想在打开Dropdow菜单或连接新设备时自动更新Combobox。因此,我需要一些属性才能Binding到我的ViewModel

在MSDN上我找到了对我来说听起来不错的Combobox.DroppedDown-Property,但是我无法使用它,找不到DroppedDown ...(System.Windows.Forms是正确的参考?)。

您是否发现了我的错误,或者您是否有更好的解决方案?

谢谢!

<UserControl x:Class="HC_SR04_MPU6050.View.SerialView_MPU_6050"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:HC_SR04_MPU6050.View"
         mc:Ignorable="d">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
    </Grid.RowDefinitions>

    <Label Grid.Row="0" Content="MPU-6050" HorizontalAlignment="Center"/>
    <Label Grid.Row="0" Grid.Column="3" Content="Y-ROT[°]" HorizontalAlignment="Center"/>
    <Button Command="{Binding Connect_Clicked}" Grid.Row="1" Grid.Column="0" Content="Connect" Height="20" Width="60" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="6" ToolTip="Opens/Connects the selected COM-PORT"/>
    <Button Command="{Binding Measure_Clicked}" Grid.Row="1" Grid.Column="1" Content="MEASURE" Height="20" Width="60" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="6" ToolTip="Starts the Measurement routine"/>
    <Button Command="{Binding Stop_Clicked}" Grid.Row="1" Grid.Column="2" Content="STOP" Height="20" Width="60" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="6" ToolTip="Stops the communication/ Closes/Disconnects the selected COM-PORT"/>
    <Label Content="{Binding Rotation.Y_Rotation}" Grid.Row="1" Grid.Column="3" Height="25" Width="50" Margin="6" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="1" BorderBrush="Black" ToolTip="Output from Arduino/HC-SR04" VerticalContentAlignment="Center" FontSize="11"/>

    <ComboBox Grid.Column="4" Grid.Row="1" Width="62" Height="25" Margin="6" Text="{Binding Rotation.Port_Name}" ItemsSource="{Binding Rotation.AvailablePorts}"/>
</Grid>
</UserControl>

3 个答案:

答案 0 :(得分:1)

您还可以查看互动触发器是否可以为您提供帮助。

只需在用户控件打开标记中输入类似内容:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

然后在你的组合框标签内放置:

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

这样,每次下拉打开时,都会调用UpdateCommListCommand。

您可能会遇到一些难以解决的问题。我不确定列表更新是否会在实际显示打开的组合框之前触发。

答案 1 :(得分:0)

我没有看到你在哪里设置ViewModel所以我会假设它在代码背后。 (我个人也喜欢在XAML中设置它,并且从不接触MVVM中的代码,但这与此无关。)

如果您正在使用ViewModel进行绑定,那么ViewModel负责翻译您希望视图做出反应的任何内容并通知侦听视图。但是,业务/应用程序逻辑实际上并不存在于ViewModel中。我建议的可能是Singleton类,它会为您监听和传递端口。让它管理端口列表等。然后您的ViewModel可以监听该类并在设备连接时自行更新等。一旦ViewModel更新(如果连线正确),那么视图也应该更新。因此,解决了设备连接后您的视图更新...到目前为止超级简单。现在,当您单击ComboBox下拉列表时,让它也进行更新。

为了实现这一点,我们希望向ViewModel添加一个ICommand(我会以RelayCommand的形式执行此操作,您需要自己创建或尝试使用像SpeckyWPF nuget包这样的第三方版本。)

在ViewModel中有命令后,它指向用于更新串行端口的方法。

现在在视图中,当您单击ComboBox时,您有一个事件。我建议不要使用后面的代码,这可能看起来更容易,但是要创建一个AttachableProperty或自定义ComboBox控件,以便您可以将命令附加到此事件。 (还有第三方工具可以帮助您这样做。)

在所有情况下,您的ViewModel都会成为通知。您只需绑定到命令即可更新它并在需要时调用它,或者只是等待设备插入。除了添加命令和所有通知都在VM中之外,View本身不需要任何更改。它所属的类中的逻辑用于串行端口。

答案 2 :(得分:0)

当串口更改(添加/删除)时,您可以使用ManagementEventWatcher对象启动事件。

ManagementEventWatcher位于System.Management库中。

例如在ViewModel

using System.Collections.ObjectModel;
using System.IO.Ports;
using System.Management;

public class ViewModel
{
    private ManagementEventWatcher _watcher;

    public ViewModel()
    {
        WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent");
        _watcher = new ManagementEventWatcher(query);
        _watcher.EventArrived += (s, e) => RefreshPortNames();
        _watcher.Start();
    }

    public ObservableCollection<string> PortNames { get; } = new ObservableCollection<string>(SerialPort.GetPortNames());

    private void RefreshPortNames()
    {
        // The `ManagementEventWatcher.EventArrived` event is fired from a different thread than the UI thread
        // so we need to return to UI thread to manipulate the `PortNames` property.
        System.Windows.Application.Current.Dispatcher.Invoke(() =>
        {
            PortNames.Clear();

            foreach (string portName in SerialPort.GetPortNames())
            {
                PortNames.Add(portName);
            }
        });
    }

    //TODO: Dispose `_watcher` object if this `ViewModel` is short-lived.
}

然后您只需像往常一样将PortNames属性绑定到ComboBox.ItemsSource

<ComboBox ItemsSource="{Binding PortNames}" />