在通用 Windows 平台 C# 中使用 DeviceWatcher 填充 ComboBox

Posted

技术标签:

【中文标题】在通用 Windows 平台 C# 中使用 DeviceWatcher 填充 ComboBox【英文标题】:Populating ComboBox with DeviceWatcher in Universal Windows Platform C# 【发布时间】:2021-10-14 11:04:20 【问题描述】:

我从 Microsoft UWP 示例库(CameraFrames 应用程序)中获取了示例,并根据我的需要采用了它。启动应用程序时,通过 Devicewatcher 类使用可用设备填充 ComboBox(参见下面的代码)。

我的特殊需要是在页面首次显示给用户之前在组合框中选择特定设备(通过 GroupComboBox.SelectedIndex 属性)。问题是我不知道什么时候(在哪个事件上)我可以做到。我尝试了一些事件(Page.Loaded、Page.OnNavigatedTo、Combobox.Loaded、Devicewatcher.EnumerationCompleted 等),但在所有这些事件中组合框都是空的,因为我无法使用组合框数据进行操作(但是当页面显示组合框已填充)。

我在 Microsoft 网站上发现了一些信息,表明它可能是多线程问题,但我无法理解如何处理它。

代码

在页面构造函数中:

_groupCollection = new SourceGroupCollection(this.Dispatcher);
GroupComboBox.ItemsSource = _groupCollection.FrameSourceGroups;

还有课程本身

public class SourceGroupCollection : IDisposable

    private CoreDispatcher _dispatcher;
    private DeviceWatcher _watcher;
    private ObservableCollection<FrameSourceGroupModel> _sourceCollection;

    public SourceGroupCollection(CoreDispatcher uiDispatcher)
    
        _dispatcher = uiDispatcher;
        _sourceCollection = new ObservableCollection<FrameSourceGroupModel>();

        // Only listen to devices with type of MediaFrameSourceGroup
        var deviceSelector = MediaFrameSourceGroup.GetDeviceSelector();
        _watcher = DeviceInformation.CreateWatcher(deviceSelector);
        _watcher.Added += Watcher_Added;
        _watcher.Removed += Watcher_Removed;
        _watcher.Updated += Watcher_Updated;
        _watcher.Start();
    

    public void Dispose()
    
        _watcher.Stop();
        _watcher.Updated -= Watcher_Updated;
        _watcher.Removed -= Watcher_Removed;
        _watcher.Added -= Watcher_Added;
    

    public IReadOnlyList<FrameSourceGroupModel> FrameSourceGroups
    
        get  return _sourceCollection; 
    

    /// <summary>
    /// Updates a device when a change occurs.
    /// </summary>
    private async void Watcher_Updated(DeviceWatcher sender, DeviceInformationUpdate args)
    
        await RemoveDevice(args.Id);
        await AddDeviceAsync(args.Id);
    

    /// <summary>
    /// Removes a device from the collection when one disconnected.
    /// </summary>
    private async void Watcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
    
        await RemoveDevice(args.Id);
    

    /// <summary>
    /// Adds a device to the collection when one connected.
    /// </summary>
    private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
    
        await AddDeviceAsync(args.Id);
    

    /// <summary>
    /// Adds a SourceGroup with given Id to the collection.
    /// </summary>
    private async Task AddDeviceAsync(string id)
    
        var group = await MediaFrameSourceGroup.FromIdAsync(id);
        if (group != null)
        
            await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                
                    _sourceCollection.Add(new FrameSourceGroupModel(group));
                );
        
    

    /// <summary>
    /// Removes a SourceGroup with given id from the collection if it exists
    /// </summary>
    private async Task RemoveDevice(string id)
    
        await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            
                var existing = _sourceCollection.FirstOrDefault(item => item.Id == id);
                if (existing != null)
                
                    _sourceCollection.Remove(existing);
                
            );
    

【问题讨论】:

【参考方案1】:

我的特殊需要是在显示页面之前选择组合框中的特定设备(通过 GroupComboBox.SelectedIndex 属性)

请勾选Combobox style,listview部分放在popup控件中,表示listview会延迟加载。如果你不打开下拉菜单。 listview 部分将不会呈现。所以你不能得到这样的 ComboBoxItem comboBox.ContainerFromIndex(0) as ComboBoxItem;和here 是类似的情况。

对于您的方案,您可以将 xaml 中的 SelectedIndex 设置为特定的索引项。如果你想显示特定的项目

var list = new string[]  "1", "2", "3" ;
TestBox.ItemsSource = list;
TestBox.SelectedItem = list.Where(p => p == "3").ToArray()[0];

请注意以上需要拨打UI thread。

更新 请在 UI 线程中获取 Count 属性,如下所示。

private async void _watcher_EnumerationCompleted(DeviceWatcher sender, object args)

    await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    
        var count = _sourceCollection.Count;
    );
   

【讨论】:

我需要澄清以下几点……在选择项目之前,我需要了解要选择哪个项目,因此我需要先读取组合框中的数据。如果组合框在打开之前没有填充数据,那么我如何定义要选择的项目? 您的 itemsSource 在下拉菜单打开之前获取值,您可以检查 itemsSource 以找到您想要选择的。 但是什么时候(哪个事件)我不能检查它?正如我在上面写的那样,Page_Loaded 和其他事件的组合框中没有任何数据 要获取完整的设备列表,最好的地方是EnumerationCompleted event。 是的,我也检查过了 private void Watcher_EnumerationCompleted(DeviceWatcher sender, Object args) var c = _sourceCollection.Count; _isEnumerationCompleted = true; _sourceCollection 中没有数据【参考方案2】:

如果您将SourceGroupCollection 类的FrameSourceGroups 属性的类型更改为ObservableCollection&lt;FrameSourceGroupModel&gt;,您可以处理其CollectionChanged 以确定何时添加了项目。

然后您可以处理该事件并设置ComboBoxSelectedItem 属性,例如:

_groupCollection = new SourceGroupCollection(this.Dispatcher);
GroupComboBox.ItemsSource = _groupCollection.FrameSourceGroups;
_groupCollection.FrameSourceGroups.CollectionChanged += (ss, ee) =>

    if (obs.Count > 0)
    
        //select the first item...
        GroupComboBox.SelectedItem = _groupCollection.FrameSourceGroups[0];
    
;

【讨论】:

以上是关于在通用 Windows 平台 C# 中使用 DeviceWatcher 填充 ComboBox的主要内容,如果未能解决你的问题,请参考以下文章

我想在unity的通用windows平台开发中使用windows runtime API

Handlebars.Compile() 函数中的 C# 车把错误

字符串数组中的文件名 c# Universal Windows

如何从 C++ 后台任务(Windows 通用应用程序)调用 C# 函数?

如何在通用 Windows 平台中连接组合框和图像?

C#入门随手笔记