通过绑定填充 ListView

Populating a ListView via binding

基于 Windows Universal Samples,我正在尝试使用可用蓝牙设备列表填充 ListView

然而,虽然我使用的代码似乎可以提取我想要列出的设备,但它不会填充 ListView。我相信这是因为我试图错误地绑定数据,但我不确定我做错了什么。

对于解决问题的任何帮助,我将不胜感激

XAML

<Page
    x:Class="Sample_Android_Connect.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Sample_Android_Connect"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <DataTemplate x:Key="ResultsListViewTemplate">
            <Grid Margin="5">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*" MinWidth="100"/>
                </Grid.ColumnDefinitions>
                <Border Grid.Column="0" Height="40" Width="40" Margin="5" VerticalAlignment="Top">
                    <Image Source="{Binding Path=GlyphBitmapImage}"
                           Stretch="UniformToFill"/>
                </Border>
                <Border Grid.Column="1" Margin="5">
                    <StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Name:" Margin="0,0,5,0"/>
                            <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" TextWrapping="WrapWholeWords"/>
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Id:" Margin="0,0,5,0"/>
                            <TextBlock Text="{Binding Path=Id}" TextWrapping="Wrap"/>
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="CanPair:" Margin="0,0,5,0"/>
                            <TextBlock Text="{Binding Path=CanPair}"/>
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="IsPaired:" Margin="0,0,5,0"/>
                            <TextBlock Text="{Binding Path=IsPaired}"/>
                        </StackPanel>
                    </StackPanel>
                </Border>
            </Grid>
        </DataTemplate>
    </Page.Resources>


    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid Margin="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="60" />
                <RowDefinition />
            </Grid.RowDefinitions>

            <RelativePanel Background="#0063B1">
                <TextBlock FontSize="30" Foreground="White" RelativePanel.AlignVerticalCenterWithPanel="True" RelativePanel.AlignHorizontalCenterWithPanel="True" Text="Bluetooth Devices" />
            </RelativePanel>

            <RelativePanel Padding="5" Grid.Row="1" Background="#0078D7">
                <TextBlock Name="connectionStatus" FontSize="16" Foreground="White" Padding="2" FontWeight="Medium" RelativePanel.AlignHorizontalCenterWithPanel="True" Text="Connection Status: Not Connected" />

                <Border BorderBrush="AntiqueWhite" BorderThickness="1" RelativePanel.Below="connectionStatus" Name="discoveredDevices" Width="500" Height="500" RelativePanel.AlignHorizontalCenterWithPanel="True" Margin="10">
                    <ListView x:Name="resultsListView"
                                  ItemTemplate="{StaticResource ResultsListViewTemplate}"
                                  ItemsSource="{Binding Path=ResultCollection}"
                                  SelectionChanged="ResultsListView_SelectionChanged"
                                  Height="500"
                                  Width="500">
                    </ListView>
                </Border>

                <RelativePanel RelativePanel.Below="discoveredDevices" RelativePanel.AlignHorizontalCenterWithPanel="True">
                    <Button Name="connectButton" Content="Connect" Click="ConnectButton_Click" IsEnabled="False"/>
                    <Button Name="disconnectButton" RelativePanel.RightOf="connectButton" Content="Disconnect" Click="DisconnectButton_Click" IsEnabled="False"/>
                </RelativePanel>
            </RelativePanel>
        </Grid>
    </Grid>
</Page>

C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;

namespace Sample_Android_Connect
{
    public sealed partial class MainPage : Page
    {
        private DeviceWatcher deviceWatcher = null;
        private TypedEventHandler<DeviceWatcher, DeviceInformation> handlerAdded = null;
        private TypedEventHandler<DeviceWatcher, DeviceInformationUpdate> handlerUpdated = null;
        private TypedEventHandler<DeviceWatcher, DeviceInformationUpdate> handlerRemoved = null;
        private TypedEventHandler<DeviceWatcher, Object> handlerEnumCompleted = null;
        private TypedEventHandler<DeviceWatcher, Object> handlerStopped = null;

        public MainPage()
        {
            InitializeComponent();

            ResultCollection = new ObservableCollection<DeviceInformationDisplay>();
            StartWatcher();
        }

        public ObservableCollection<DeviceInformationDisplay> ResultCollection {
            get;
            private set;
        }

        private void ConnectButton_Click(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("Connect Clicked");
        }

        private void DisconnectButton_Click(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("Disconnect Clicked");
        }

        private void ResultsListView_SelectionChanged(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("Results List View Selection Changed");
        }

        private void StartWatcher()
        {
            ResultCollection.Clear();

            // Get the device selector chosen by the UI then add additional constraints for devices that 
            // can be paired or are already paired.
            //DeviceSelectorInfo deviceSelectorInfo = "";//(DeviceSelectorInfo)selectorComboBox.SelectedItem;
            //string selector = "(" + deviceSelectorInfo.Selector + ")" + " AND (System.Devices.Aep.CanPair:=System.StructuredQueryType.Boolean#True OR System.Devices.Aep.IsPaired:=System.StructuredQueryType.Boolean#True)";
            deviceWatcher = DeviceInformation.CreateWatcher(
                    "System.Devices.Aep.ProtocolId:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\"",
                    null, // don't request additional properties for this sample
                    DeviceInformationKind.AssociationEndpoint);

            // Hook up handlers for the watcher events before starting the watcher

            handlerAdded = new TypedEventHandler<DeviceWatcher, DeviceInformation>(async (watcher, deviceInfo) =>
            {
                // Since we have the collection databound to a UI element, we need to update the collection on the UI thread.
                await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
                {
                    ResultCollection.Add(new DeviceInformationDisplay(deviceInfo));
                    Debug.WriteLine("Watcher Added");
                });
            });
            deviceWatcher.Added += handlerAdded;

            handlerUpdated = new TypedEventHandler<DeviceWatcher, DeviceInformationUpdate>(async (watcher, deviceInfoUpdate) =>
            {
                // Since we have the collection databound to a UI element, we need to update the collection on the UI thread.
                await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
                {
                    // Find the corresponding updated DeviceInformation in the collection and pass the update object
                    // to the Update method of the existing DeviceInformation. This automatically updates the object
                    // for us.
                    Debug.WriteLine("Device Count: " + ResultCollection.Count);
                    foreach (DeviceInformationDisplay deviceInfoDisp in ResultCollection)
                    {
                        if (deviceInfoDisp.Id == deviceInfoUpdate.Id)
                        {
                            deviceInfoDisp.Update(deviceInfoUpdate);

                            // If the item being updated is currently "selected", then update the pairing buttons
                            DeviceInformationDisplay selectedDeviceInfoDisp = (DeviceInformationDisplay)resultsListView.SelectedItem;
                            if (deviceInfoDisp == selectedDeviceInfoDisp)
                            {
                                //UpdatePairingButtons();
                            }
                            break;
                        }
                    }
                });
            });
            deviceWatcher.Updated += handlerUpdated;

            handlerRemoved = new TypedEventHandler<DeviceWatcher, DeviceInformationUpdate>(async (watcher, deviceInfoUpdate) =>
            {
                // Since we have the collection databound to a UI element, we need to update the collection on the UI thread.
                await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
                {
                    // Find the corresponding DeviceInformation in the collection and remove it
                    foreach (DeviceInformationDisplay deviceInfoDisp in ResultCollection)
                    {
                        if (deviceInfoDisp.Id == deviceInfoUpdate.Id)
                        {
                            ResultCollection.Remove(deviceInfoDisp);
                            break;
                        }
                    }
                    Debug.WriteLine("Removed");
                });
            });
            deviceWatcher.Removed += handlerRemoved;

            handlerEnumCompleted = new TypedEventHandler<DeviceWatcher, Object>(async (watcher, obj) =>
            {
                await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
                {
                    Debug.WriteLine("Completed");
                });
            });
            deviceWatcher.EnumerationCompleted += handlerEnumCompleted;

            handlerStopped = new TypedEventHandler<DeviceWatcher, Object>(async (watcher, obj) =>
            {
                await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
                {
                    Debug.WriteLine("Stopped");
                });
            });
            deviceWatcher.Stopped += handlerStopped;

            deviceWatcher.Start();
        }

        public class DeviceInformationDisplay : INotifyPropertyChanged
        {
            private DeviceInformation deviceInfo;

            public DeviceInformationDisplay(DeviceInformation deviceInfoIn)
            {
                deviceInfo = deviceInfoIn;
                UpdateGlyphBitmapImage();
            }

            public DeviceInformationKind Kind {
                get {
                    return deviceInfo.Kind;
                }
            }

            public string Id {
                get {
                    return deviceInfo.Id;
                }
            }

            public string Name {
                get {
                    return deviceInfo.Name;
                }
            }

            public BitmapImage GlyphBitmapImage {
                get;
                private set;
            }

            public bool CanPair {
                get {
                    return deviceInfo.Pairing.CanPair;
                }
            }

            public bool IsPaired {
                get {
                    return deviceInfo.Pairing.IsPaired;
                }
            }

            public IReadOnlyDictionary<string, object> Properties {
                get {
                    return deviceInfo.Properties;
                }
            }

            public DeviceInformation DeviceInformation {
                get {
                    return deviceInfo;
                }

                private set {
                    deviceInfo = value;
                }
            }

            public void Update(DeviceInformationUpdate deviceInfoUpdate)
            {
                deviceInfo.Update(deviceInfoUpdate);

                OnPropertyChanged("Kind");
                OnPropertyChanged("Id");
                OnPropertyChanged("Name");
                OnPropertyChanged("DeviceInformation");
                OnPropertyChanged("CanPair");
                OnPropertyChanged("IsPaired");

                UpdateGlyphBitmapImage();
            }

            private async void UpdateGlyphBitmapImage()
            {
                DeviceThumbnail deviceThumbnail = await deviceInfo.GetGlyphThumbnailAsync();
                BitmapImage glyphBitmapImage = new BitmapImage();
                await glyphBitmapImage.SetSourceAsync(deviceThumbnail);
                GlyphBitmapImage = glyphBitmapImage;
                OnPropertyChanged("GlyphBitmapImage");
            }

            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged(string name)
            {
                Debug.WriteLine("PropertyChanged");
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}

您需要设置页面的DataContex,例如在构造函数的末尾:

this.DataContext = this;

DataContext 是 MVVM 知道应该在哪里搜索 Bindings 的方式。 属性 继承自 XAML 树(除非您在下面设置了不同的 DataContext),因此您可以为页面本身设置它,它将适用于所有页面绑定。

通常你实际上创建了一个单独的 ViewModel class,它在视图和模型之间创建了一个额外的抽象层,并在其中公开了属性,ObservableCollectionsCommands 你的观点使用。

使用代码隐藏作为视图模型是可能的,但理想情况下,如果您创建专用的 ViewModel class.

,则可以实现最佳的关注点分离