Автоматически обновлять содержимое Combobox в WPF с помощью MVVM - PullRequest
0 голосов
/ 12 июня 2018

Я использую версию Visual Studio 2017 Communitiy / .NET Framework 4.6.1 / WPF.Btw.Я использую MVVM.


Моя цель - отобразить все доступные последовательные порты в Combobox .Я уже реализовал это, но теперь я хочу, чтобы Combobox автоматически обновлялся при открытии Dropdowmenu или при подключении нового устройства.Поэтому мне понадобится какое-то свойство, чтобы сделать 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 голосов
/ 12 июня 2018

Вы также можете посмотреть, может ли вам помочь триггер взаимодействия.

Просто поместите что-то вроде этого в свой открытый тег пользовательского элемента управления:

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.

Вы можете столкнуться с некоторыми проблемами обновления.Я не уверен, что обновление списка будет запущено до того, как будет открыто открытое поле со списком.

0 голосов
/ 12 июня 2018

Вы можете использовать объект 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}" />
0 голосов
/ 12 июня 2018

Я не вижу, где вы устанавливаете ViewModel, поэтому я предполагаю, что это в коде позади.(Лично я предпочитаю установить его в XAML и никогда не трогать код позади в MVVM, но это не имеет к этому никакого отношения.)

Если вы используете вашу ViewModel для привязки, то ViewModel отвечает за перевод всего, что выхочу, чтобы представление реагировало на мнение слушателя и уведомляло его.Тем не менее, логика бизнеса / приложений на самом деле не живет во ViewModel.То, что я предлагаю, может быть классом Singleton, который слушает и сообщает порты для вас.Пусть он управляет списком портов и т. Д. Затем ваш ViewModel может прослушивать этот класс и обновлять себя всякий раз, когда подключено устройство и т. Д. Как только ViewModel обновляется (если он подключен правильно), тогда представление также должно обновляться.Так что это решает, когда устройство подключено, ваш обзор обновляется ... Пока все очень просто.Теперь давайте обновим его, когда вы нажмете раскрывающийся список ComboBox.

Чтобы это произошло, мы хотим добавить ICommand в ViewModel (я бы сделал это в виде RelayCommand, который вам понадобитсясделайте сами или попробуйте стороннюю версию, такую ​​как пакет Nuget SpeckyWPF.)

После того, как у вас есть команда в ViewModel, укажите в методе, который вы используете для обновления последовательных портов.

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

Что произойдет, если ваша ViewModel станет уведомляющей во всех случаях.Вы просто привязываетесь к команде, чтобы обновить ее и вызываете ее, когда это необходимо, или просто ждете, пока устройство будет подключено. Само представление не потребует никаких изменений, за исключением добавления команды, и все уведомления находятся в ВМ иЛогика в классе принадлежит к последовательному порту.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...