【问题标题】:Observable collection - CollectionChanged Binding可观察集合 - CollectionChanged 绑定
【发布时间】:2012-03-16 09:02:09
【问题描述】:

在编程过程中,我遇到了以下问题:

  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; }
        }
    
    }
    

注意:该类继承自 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>

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

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

干杯-巴特

编辑:做了一点测试

我自己运行了一个小调试测试,看看添加操作是否添加到正确的集合中,因此增加计数值

        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 中做错了什么。所以这就是我目前所拥有的:

设备视图模型:

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>

【问题讨论】:

    标签: c#-4.0 binding collections inotifycollectionchanged


    【解决方案1】:

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

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

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

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


    编辑

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

    ItemsSource="{Binding Source={StaticResource deviceinfo}}"
    

    现在在您的 DeviceViewModel 类中,您像这样声明 InfoList

    DeviceModelInfo InfoList = new DeviceModelInfo();
    

    您创建了DeviceModelInfo 实例,因此InfoList 与 XAML 中的实例/对象不同。

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

    ItemsSource="{Binding InfoList}"
    

    假设InfoList 是公开的

    2) 获取在 XAML 中创建的实例 deviceinfo,如下所示:

    DeviceViewModel deviceinfo = phoneApplicationPage.FindResource("deviceinfo") as DeviceViewModel;
    

    假设您的控件实例名为phoneApplicationPage。如果您在控件后面的代码中执行此操作,则 phoneApplicationPage 将是 this

    现在您可以将此实例 (deviceinfo) 传递给您的 DeviceViewModel 实例。

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


    编辑

    公开这个领域就足够了。

    现在您需要将其绑定到ListBoxItemsSource 属性。这可以很简单

    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}">
    

    【讨论】:

    • @BartTeunissen,您确实声明了一个像 DeviceModelInfo InfoList = new DeviceModelInfo(); 这样的实例,这是一个(新)差异实例,而不是您在 XAML 中声明和绑定的实例。
    • 亲爱的西蒙,首先感谢您的回复。但是你的anwser对我不起作用,我也有些不清楚。因为您说的是“不同的实例”,但是如果我没记错的话,我要更改的集合是实例 DeviceModelInfo 的集合,因为当我声明集合时,我会这样做: DeviceModelInfo InfoList = new DeviceModelInfo() ;另外,我自己做了一个小测试。请参阅我的帖子的编辑。如果您可以在之前的解释中添加更多细节,我确实会是一个非常快乐的人。干杯-巴特
    • 感谢您的快速回复,我现在明白您的意思了,几乎一切都清楚了。我无法工作的一件事是使我的:InfoList 在 xaml 中可见,并且能够绑定到它。我像你说的那样公开了,但我担心我做错了。我现在这样做: Public DeviceModelInfo InfoList = new DeviceModelInfo();如果您能再次帮助我,将不胜感激
    • 我在上周五尝试让它工作,但 XAML 找不到 InfoList。我已经编辑了上面的帖子,它现在包括 viemodel、codebehind 和 XAML。也许你可以看到我做错了什么。谢谢!
    • 他西蒙,我终于想通了。虽然它不完全像你解释的那样。谢谢;)
    猜你喜欢
    • 2015-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多