Наблюдаемая коллекция - CollectionChanged Binding - PullRequest
1 голос
/ 16 марта 2012

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

  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>

Я надеюсь, что кто-то сможет помочь мне с этой проблемой, потому что я занимаюсь этим уже два дня.

Кроме того, мои извинения за мой "плохой" английский, но английский не мой родной язык

Приветствия -Барт

РЕДАКТИРОВАТЬ: сделали небольшой тест

Я сам провел небольшой отладочный тест, чтобы проверить, добавляет ли операция add к нужной коллекции 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 Ответ

2 голосов
/ 16 марта 2012

1) ObservableCollection реализует интерфейс INotifyCollectionChanged , который определяет событие CollectionChanged.

2) Когда вы добавляете новый элемент в DeviceViewModel, вы делаете это вновый экземпляр DeviceModelInfo, поэтому экземпляр, отличный от того, который вы объявили в своем XAML

<ViewModel:DeviceModelInfo x:Key="deviceinfo"></ViewModel:DeviceModelInfo>

Вы должны либо привязать экземпляр DeviceModelInfo в DeviceViewModel, либо использовать экземпляр DeviceViewModel заявлено в вашем XAML

Edit

В вашем 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}">
...