可观察集合 - CollectionChanged绑定

时间:2012-03-16 09:02:09

标签: c#-4.0 binding collections inotifycollectionchanged

在编程期间,我遇到了以下问题:

  1. 可观察集合是否实现了CollectionChanged事件? (因为不同的书指的是它确实存在,但谷歌则另有说明)

  2. 我有以下代码,我希望我的UI通过绑定更新(代码适用于windowsPhone 7.1)此外,绑定适用于我的observable集合中的单个项目,但是当我尝试添加新的时对象是我的集合,CollectionChanged事件不会触发。

    命名空间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; }
        }
    
    }
    
  3. 注意:该类继承自viewmodel base,其中包含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>
    

    我希望有人能够帮助我解决这个问题,因为我已经在这两天了。

    另外,我为我的“坏”英语道歉,但英语不是我的母语

    干杯 - 巴特

    编辑:做了一点测试

    我自己运行了一些调试,看看添加操作是否会添加到正确的集合中,从而增加计数值

            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 个答案:

答案 0 :(得分:2)

1)ObservableCollection实现了INotifyCollectionChanged接口,该接口定义了CollectionChanged事件。

2)当您在DeviceViewModel中添加新项目时,您将其添加到DeviceModelInfo的新实例中,因此该实例与您在XAML中声明的实例不同

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

您必须绑定DeviceModelInfo中的DeviceViewModel个实例 或使用在XAML中声明的DeviceViewModel实例

<小时/> 编辑

在您的XAML中       这与键入“new DeviceModelInfo()”然后在控件PhoneApplicationPage的资源中注册该实例相同。然后将ItemsSource的{​​{1}}绑定到该特定实例。

ListBox

现在,您在ItemsSource="{Binding Source={StaticResource deviceinfo}}" 课程中宣布DeviceViewModel就像这样

InfoList

您创建了DeviceModelInfo InfoList = new DeviceModelInfo(); 实例,因此DeviceModelInfo与XAML中的实例/对象不是同一个实例/对象

你必须要么 1)InfoList的{​​{1}}绑定到ItemsSource中的实例。要做到这一点,你必须首先公开ListBox,最好通过一个属性公开它(但这只是惯例,不是必需的)。然后确保您的控件的DeviceViewModel设置为您正在使用的InfoList实例。然后你可以像这样设置绑定

DataContext

假设DeviceViewModel是公开的

2)获取您在XAML中创建的实例ItemsSource="{Binding InfoList}" ,如下所示:

InfoList

假设您的控件实例名为deviceinfo。如果您在控件后面的代码中执行此操作,则DeviceViewModel deviceinfo = phoneApplicationPage.FindResource("deviceinfo") as DeviceViewModel; 将为phoneApplicationPage

现在,您可以将此实例(phoneApplicationPage)传递给this的实例。

从命名我假设您尝试使用MVVM模式,在这种情况下,您应该使用 1)

<小时/> 编辑

让这个领域公开是足够好的。

现在您需要将其绑定到deviceinfo的{​​{1}}属性。这可以像

一样简单
DeviceViewModel

但是此要求将您网页的ItemsSource属性(ListBox)设置为ItemsSource="{Binding InfoList}" 的实例。 如果不确切知道你当前如何实例DataContext,我很难准确地解释你如何做到这一点。但我假设您在页面的代码隐藏中实例化PhoneApplicationPage,所以它看起来像这样:

DeviceViewModel

确定DeviceViewModel设置为DeviceViewModel实例后,您可以更改XAML中的绑定,如上所述。 所以你应该改变这一行

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