通过绑定填充 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,它在视图和模型之间创建了一个额外的抽象层,并在其中公开了属性,ObservableCollections
和 Commands
你的观点使用。
使用代码隐藏作为视图模型是可能的,但理想情况下,如果您创建专用的 ViewModel
class.
,则可以实现最佳的关注点分离
基于 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,它在视图和模型之间创建了一个额外的抽象层,并在其中公开了属性,ObservableCollections
和 Commands
你的观点使用。
使用代码隐藏作为视图模型是可能的,但理想情况下,如果您创建专用的 ViewModel
class.