Наблюдаемая коллекция - CollectionChanged Binding
Во время программирования я столкнулся со следующими вопросами:
Реализует ли наблюдаемая коллекция событие CollectionChanged само по себе? (Из-за того, что разные книги ссылаются на то, что это так, но Google показывает иначе)
У меня есть следующий код, и я хочу, чтобы мой интерфейс обновлялся с помощью привязки (код для windowsPhone 7.1). Кроме того, привязка работает для отдельных элементов в моей наблюдаемой коллекции, но когда я пытаюсь добавить новый объект в свою коллекцию, Событие CollectionChanged не срабатывает.
namespace Phone.lib.ViewModel {открытый класс DeviceViewModel: ViewModelBase {
DeviceModelInfo InfoList = new DeviceModelInfo(); public DeviceViewModel() { } public DeviceViewModel(int index) { // Here I add a new item to the collection, But the ui only shows: Beckhoff, ver.... InfoList.Add(new DeviceInfo("name1", "name2", "name3")); } } public class DeviceModelInfo : ObservableCollection<DeviceInfo> { public DeviceModelInfo() : base() { Add(new DeviceInfo("Beckhoff", "Ver. 1A2B3C", "Stopped")); } } public class DeviceInfo : ViewModelBase { private string devicename; private string deviceid; private string devicestatus; public DeviceInfo(string first, string second, string third) { devicename = first; deviceid = second; devicestatus = third; } public string DeviceName { get { return devicename; } set { devicename = value; RaisePropertyChanged("DeviceName"); } } public string DeviceID { get { return deviceid; } set { deviceid = value; } } public string DeviceStatus { get { return devicestatus; } set { devicestatus = value; } } }
Примечание. Класс наследуется от базы viewmodel, в которой есть измененный интерфейс Inotify.
Код из моего Xaml:
<phone:PhoneApplicationPage
x:Class="WindowsPhone.View.Device_Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModel="clr-namespace:Phone.lib.ViewModel;assembly=Phone.lib"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True">
<!-- Static Resource area for binding -->
<phone:PhoneApplicationPage.Resources>
<ViewModel:DeviceModelInfo x:Key="deviceinfo"></ViewModel:DeviceModelInfo>
<ViewModel:DeviceModelSensor x:Key="devicesensors"></ViewModel:DeviceModelSensor>
<ViewModel:DeviceModelActuator x:Key="deviceactuators"></ViewModel:DeviceModelActuator>
</phone:PhoneApplicationPage.Resources>
<!-- LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="Kremer app" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox Height="100" HorizontalAlignment="Left" Margin="-4,6,0,0" Name="Device_ListBox" VerticalAlignment="Top" Width="460" ItemsSource="{Binding Source={StaticResource deviceinfo}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="100">
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextExtraLargeStyle}" Text="{Binding Path=DeviceName, Mode=TwoWay}" />
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Path=DeviceID, Mode=TwoWay}" />
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Path=DeviceStatus, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Height="261" HorizontalAlignment="Left" Margin="-4,138,0,0" Name="Sensor_ListBox" VerticalAlignment="Top" Width="460" ItemsSource="{Binding Source={StaticResource devicesensors}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="78">
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextExtraLargeStyle}" Text="{Binding Path=SensorName}" />
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Path=SensorType}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Height="261" HorizontalAlignment="Left" Margin="-4,429,0,0" Name="Actuator_ListBox" ItemsSource="{Binding Source={StaticResource deviceactuators}}" VerticalAlignment="Top" Width="460">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Height="78" Margin="0,0,0,17" Width="432">
<TextBlock Margin="12,-6,12,0" Style="{StaticResource PhoneTextExtraLargeStyle}" Text="{Binding Path=ActuatorName}" TextWrapping="Wrap" />
<TextBlock Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Path=ActuatorType}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
Я надеюсь, что кто-то сможет помочь мне с этой проблемой, потому что я занимаюсь этим уже два дня.
Кроме того, мои извинения за мой "плохой" английский, но английский не мой родной язык
Ура-Барт
РЕДАКТИРОВАТЬ: сделали небольшой тест
Я сам выполнил небольшой отладочный тест, чтобы увидеть, добавляет ли операция добавления к нужной коллекции en, увеличивая значение счетчика.
public DeviceViewModel(int index)
{
// Here I add a new item to the collection, But the ui only shows: Beckhoff, ver....
Debug.WriteLine("number of added items " + InfoList.Count.ToString());
InfoList.Add(new DeviceInfo("1", "2", "3"));
Debug.WriteLine("number of added items " + InfoList.Count.ToString());
InfoList.Add(new DeviceInfo("1", "2", "3"));
InfoList.Add(new DeviceInfo("1", "2", "3"));
InfoList.Add(new DeviceInfo("1", "2", "3"));
Debug.WriteLine("number of added items " + InfoList.Count.ToString());
}
выход:
количество добавленных предметов 1
количество добавленных предметов 2
количество добавленных предметов 5
Редактировать 2 (19-03-2012)
В прошлую пятницу я пытался заставить его работать так, как ты предложил. Но почему-то XAML не может найти InfoList, и я не знаю почему. Может быть, я что-то не так делаю в самом XAML или в коде или в DeviceViewModel. Итак, вот что я имею на данный момент:
DeviceViewModel:
namespace Phone.lib.ViewModel
{
public class DeviceViewModel : ViewModelBase
{
public DeviceModelInfo InfoList = new DeviceModelInfo();
public DeviceViewModel()
{
//DeviceModelInfo InfoList = new DeviceModelInfo();
InfoList.Add(new DeviceInfo("1", "2", "3"));
}
public DeviceViewModel(int index)
{
}
}
public class DeviceModelInfo : ObservableCollection<DeviceInfo>
{
public DeviceModelInfo() : base()
{
Add(new DeviceInfo("Beckhoff", "Ver. 1A2B3C", "Stopped"));
//this.CollectionChanged += (e, s) => { Debug.WriteLine("event Fired " + e.ToString()); };
}
}
public class DeviceInfo : ViewModelBase
{
private string devicename;
private string deviceid;
private string devicestatus;
public DeviceInfo(string first, string second, string third)
{
devicename = first;
deviceid = second;
devicestatus = third;
}
public string DeviceName
{
get { return devicename; }
set
{
devicename = value;
RaisePropertyChanged("DeviceName");
}
}
public string DeviceID
{
get { return deviceid; }
set { deviceid = value; }
}
public string DeviceStatus
{
get { return devicestatus; }
set { devicestatus = value; }
}
}
Код за страницей:
namespace WindowsPhone.View
{
public partial class Device_Page : PhoneApplicationPage
{
private DeviceViewModel _DV;
public Device_Page()
{
InitializeComponent();
_DV = new DeviceViewModel();
DataContext = _DV;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
//_DV = new DeviceViewModel(index);
//DataContext = _DV;
Debug.WriteLine("index:" + index.ToString());
}
}
}
}
Код XAML:
<phone:PhoneApplicationPage
x:Class="WindowsPhone.View.Device_Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModel="clr-namespace:Phone.lib.ViewModel;assembly=Phone.lib"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True">
<!-- Static Resource area for binding -->
<phone:PhoneApplicationPage.Resources>
<ViewModel:DeviceViewModel x:Key="deviceinfo"></ViewModel:DeviceViewModel>
<ViewModel:DeviceModelSensor x:Key="devicesensors"></ViewModel:DeviceModelSensor>
<ViewModel:DeviceModelActuator x:Key="deviceactuators"></ViewModel:DeviceModelActuator>
</phone:PhoneApplicationPage.Resources>
<!-- LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="Kremer app" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox Height="100" HorizontalAlignment="Left" Margin="-4,6,0,0" Name="Device_ListBox" VerticalAlignment="Top" Width="460" ItemsSource="{Binding InfoList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="100">
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextExtraLargeStyle}" Text="{Binding Path=DeviceName, Mode=TwoWay}" />
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Path=DeviceID, Mode=TwoWay}" />
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Path=DeviceStatus, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Height="261" HorizontalAlignment="Left" Margin="-4,138,0,0" Name="Sensor_ListBox" VerticalAlignment="Top" Width="460" ItemsSource="{Binding Source={StaticResource devicesensors}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="78">
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextExtraLargeStyle}" Text="{Binding Path=SensorName}" />
<TextBlock TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Path=SensorType}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Height="261" HorizontalAlignment="Left" Margin="-4,429,0,0" Name="Actuator_ListBox" ItemsSource="{Binding Source={StaticResource deviceactuators}}" VerticalAlignment="Top" Width="460">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Height="78" Margin="0,0,0,17" Width="432">
<TextBlock Margin="12,-6,12,0" Style="{StaticResource PhoneTextExtraLargeStyle}" Text="{Binding Path=ActuatorName}" TextWrapping="Wrap" />
<TextBlock Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Text="{Binding Path=ActuatorType}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
1 ответ
1) ObservableCollection
реализует интерфейс INotifyCollectionChanged, который определяет CollectionChanged
событие.
2) Когда вы добавляете новый элемент в DeviceViewModel
вы делаете это с новым экземпляром DeviceModelInfo
Таким образом, экземпляр отличается от того, который вы объявили в своем XAML
<ViewModel:DeviceModelInfo x:Key="deviceinfo"></ViewModel:DeviceModelInfo>
Вы должны либо привязать к DeviceModelInfo
экземпляр в DeviceViewModel
или использовать экземпляр DeviceViewModel
заявлено в вашем XAML
редактировать
В вашем XAML у вас есть То же самое, что набрать 'new DeviceModelInfo()' и затем зарегистрировать этот экземпляр в ресурсах вашего элемента управления. PhoneApplicationPage
, И вы связываете ItemsSource
вашей ListBox
к этому конкретному случаю.
ItemsSource="{Binding Source={StaticResource deviceinfo}}"
Теперь в вашем DeviceViewModel
класс, который вы объявляете InfoList
как это
DeviceModelInfo InfoList = new DeviceModelInfo();
Вы создаете новый экземпляр DeviceModelInfo
, так InfoList
это не тот же экземпляр / объект, что и экземпляр / объект в вашем XAML.
Вы должны либо 1) связать свой ItemsSource
из ListBox
к примеру, у вас есть в DeviceViewModel
, Для этого вы должны сначала выставить InfoList
, то есть сделать его публичным, предпочтительно через свойство (но это просто соглашение, а не обязательно). Затем убедитесь, что DataContext
вашего контроля установлен на экземпляр DeviceViewModel
Вы работаете с. И тогда вы можете установить привязку, как это
ItemsSource="{Binding InfoList}"
Если предположить, InfoList
является публичным
2) Получить экземпляр deviceinfo
создан в вашем XAML следующим образом:
DeviceViewModel deviceinfo = phoneApplicationPage.FindResource("deviceinfo") as DeviceViewModel;
при условии, что экземпляр вашего контроля называется phoneApplicationPage
, Если вы делаете это в коде вашего контроля, то phoneApplicationPage
было бы this
,
И теперь вы можете передать этот экземпляр (deviceinfo
) к вашему случаю DeviceViewModel
,
Исходя из названия, я предполагаю, что вы пытаетесь использовать шаблон MVVM, и в этом случае вы должны перейти с 1)
редактировать
Публичная публикация достаточно хороша.
Теперь вам нужно привязать его к ItemsSource
собственность ListBox
, Который может быть так просто, как
ItemsSource="{Binding InfoList}"
Но для этого необходимо, чтобы DataContext
свойство вашей страницы (PhoneApplicationPage
) устанавливается на экземпляр DeviceViewModel
, Не зная точно, как вы в данный момент создаете экземпляр DeviceViewModel
Мне трудно объяснить, как именно вы можете сделать это. Но я предполагаю, что вы создаете экземпляр DeviceViewModel
в коде вашей страницы, так что это выглядит примерно так:
public partial class PhoneApplicationPage : Page
{
private DeviceViewModel _deviceViewModel;
//...
public PhoneApplicationPage()
{
InitializeComponent();
// I assume you do something like this
_deviceViewModel = new DeviceViewModel();
// You need to set the DataContext to the DeviceViewModel instance you have created.
DataContext = _deviceViewModel;
}
//...
}
Как только вы убедились, что DataContext
настроен на ваш DeviceViewModel
Например, вы можете изменить привязку в вашем XAML, как указано выше. Таким образом, вы должны изменить строку
<ListBox Height="100" HorizontalAlignment="Left" Margin="-4,6,0,0" Name="Device_ListBox" VerticalAlignment="Top" Width="460" ItemsSource="{Binding Source={StaticResource deviceinfo}}">
в
<ListBox Height="100" HorizontalAlignment="Left" Margin="-4,6,0,0" Name="Device_ListBox" VerticalAlignment="Top" Width="460" ItemsSource="{Binding ListInfo}">