Наблюдаемая коллекция - CollectionChanged Binding

Во время программирования я столкнулся со следующими вопросами:

  1. Реализует ли наблюдаемая коллекция событие CollectionChanged само по себе? (Из-за того, что разные книги ссылаются на то, что это так, но Google показывает иначе)

  2. У меня есть следующий код, и я хочу, чтобы мой интерфейс обновлялся с помощью привязки (код для 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}">
Другие вопросы по тегам