将列表框绑定到 observablecollection [英] Binding listbox to observablecollection

查看:34
本文介绍了将列表框绑定到 observablecollection的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要一些帮助才能将 ObservableCollection 正确绑定到 xaml.我可以正确绑定数据,但是当数据更改时,更改没有反映在屏幕上.我已经阅读了相关的博客并且似乎已经理解了,但是当我尝试将我所知道的应用到我自己的示例中时,它并没有像我想象的那样奏效.

I need some help to correctly bind an ObservableCollection to xaml. I can bind the data correctly but when the data changed, the changes did not reflect on the screen. I have read related blogs and seemed to have understood, but when trying to apply what I know into my own sample, it does not work quite as I have thought.

我有 2 个类 Fruits 和 Fruit,其中 Fruits 是一个可观察的水果集合,它实现了 INotifyPropertyChanged

I have 2 classes Fruits and Fruit, where Fruits is a observablecollection of fruit which implements INotifyPropertyChanged

namespace TestCommand.Models
{
    public class Fruit:INotifyPropertyChanged
    {
        private string _fruitname;
        public string FruitName
        { get
            {
                return _fruitname;
            }
            set
            {
                if (_fruitname!=value)
                {
                    _fruitname = value;
                    OnPropertyChanged("FruitName");
                }
            }
        }

        private string _fruitcolor;
        public string FruitColor
        {
            get
            {
                return _fruitcolor;
            }
            set
            {
                if (_fruitcolor != value)
                {
                    _fruitcolor = value;
                    OnPropertyChanged("FruitColor");
                }
            }
        }

        private bool _selected;
        public bool bSelected
        {
            get
            {
                return _selected;
            }
            set
            {
                if (_selected != value)
                {
                    _selected = value;
                    OnPropertyChanged("bSelected");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string name)
        {
            if (PropertyChanged!=null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}

    namespace TestCommand.Models
    {
        public class Fruits
        {
            private static ObservableCollection<Fruit> _fruitList;
            public static void Add(string f, string c)
            {
                _fruitList.Add(new Fruit
                {
                    FruitName = f,
                    FruitColor = c,
                    bSelected = false
                });
            }
            static Fruits()
            {
                _fruitList = new ObservableCollection<Fruit>();
                _fruitList.Add(new Fruit
                {
                    FruitName = "Mango",
                    FruitColor = "Yellow",
                    bSelected = false
                });
                _fruitList.Add(new Fruit
                {
                    FruitName = "Mango",
                    FruitColor = "Yellow",
                    bSelected = false
                });
                _fruitList.Add(new Fruit
                {
                    FruitName = "Water Melon",
                    FruitColor = "Green",
                    bSelected = false
                });
                _fruitList.Add(new Fruit
                {
                    FruitName = "Apple",
                    FruitColor = "Red",
                    bSelected = false
                });
                _fruitList.Add(new Fruit
                {
                    FruitName = "Banana",
                    FruitColor = "Yellow",
                    bSelected = false
                });
                _fruitList.Add(new Fruit
                {
                    FruitName = "Orange",
                    FruitColor = "Orange",
                    bSelected = false
                });
            }
            public static ObservableCollection<Fruit> getAllFruit(bool bSelected = false)
            {
                var result = (bSelected ?
                                            _fruitList.Where(x => x.bSelected = true).ToList<Fruit>()
                                            : _fruitList.ToList<Fruit>());
                return new ObservableCollection<Fruit>(result);
            }
        }
    }

我的 xaml:

<Window x:Class="TestCommand.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:local="clr-namespace:TestCommand"
                xmlns:MyCommands='clr-namespace:TestCommand.Commands'
                mc:Ignorable="d"
                Title="MainWindow"
                Height="350"
                Width="525">
    <StackPanel Orientation='Vertical' Margin='10'>
        <ListBox x:Name='MyList' ItemTemplate='{StaticResource FruitTemp}'>
        </ListBox>
        <Button x:Name='AddFruit'
                        Height='auto'
                        Width='auto'
                        Content='Add New Fruit 2'
                        Margin='0,10,0,0'
                        Command='{x:Static MyCommands:TestButtonCommand.AddFruit}'>
            <Button.CommandBindings>
                <CommandBinding Command='{x:Static MyCommands:TestButtonCommand.AddFruit}'
                                                Executed='CommandBinding_Executed'
                                                CanExecute='CommandBinding_CanExecute' />
            </Button.CommandBindings>
        </Button>
    </StackPanel>
</Window>

和背后的代码:

namespace TestCommand
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            MyList.ItemsSource = Fruits.getAllFruit();
        }

        private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Fruits.Add("Durian", "Green");
        }

        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
    }
}

我的项目模板

<Application x:Class="TestCommand.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:TestCommand"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
            <DataTemplate x:Key='FruitTemp'>
                <StackPanel Orientation='Horizontal'
                                        Margin='5'>
                    <TextBlock x:Name='tbName'
                                         Text='{Binding FruitName}' 
                                         Margin='10,0,0,0'
                                         Width='100'/>
                    <TextBlock x:Name='tbColor'
                                     Text='{Binding FruitColor}'
                                     Margin='10,0,0,0'
                                     Width='100' />
                    <!--<CheckBox x:Name='cbSelected'
                                        Content='Selected'
                                        Checked='{Binding bSelected}' />-->
                </StackPanel>
            </DataTemplate>
    </Application.Resources>
</Application>

当我单击按钮时,我看到项目已添加到集合中,但该集合未在列表中刷新.我一定没有正确绑定集合,或者可能错过了一些东西,因为我对 wpf 很陌生.

When I clicked on the button, I saw item added to the collection but the collection was not refreshed on the list. I must have not tied the collection correctly or may have missed something as I am very new to wpf.

非常感谢您帮助指出我的疏忽.

Your help to point out my overlook is very much appreciated.

推荐答案

此方法创建一个新的 ObservableCollection 并返回它.您将其分配给 MyList.ItemsSource(这不是绑定,这只是一个分配),然后将项目添加到其他位置的不同 ObservableCollection.

This method creates a new ObservableCollection and returns it. You assign that to MyList.ItemsSource (that's not a binding, that's just an assignment), and then you add an item to a different ObservableCollection somewhere else.

MyList.ItemsSource = Fruits.getAllFruit();

...

public static ObservableCollection<Fruit> getAllFruit(bool bSelected = false)
{
    var result = (bSelected ? _fruitList.Where(x => x.bSelected = true).ToList<Fruit>()
        : _fruitList.ToList<Fruit>());
    return new ObservableCollection<Fruit>(result);
}

当然,您在交给 ListBox_fruitListcopy 中看不到任何新项目.

Of course you don't see any new items in the copy of _fruitList that you handed to the ListBox.

ListBox 必须具有与您添加对象相同的实际集合对象.

The ListBox has to have the same actual collection object that you're adding objects to.

public static ObservableCollection<Fruit> getAllFruit(bool bSelected = false)
{
    return _fruitList;
}

糟糕,没有过滤.

这仍然不是正确的方法.使用返回 ObservableCollection 的公共 Fruits 属性编写视图模型,并在 XAML 中使用 CollectionViewSource 进行过滤.如果需要,我们可以遍历所有这些东西.您已经知道如何实现 INotifyPropertyChanged,所以您已经在路上了.

And that's still not the right way to do this. Write a viewmodel with a public Fruits property that returns an ObservableCollection<Fruit>, and use a CollectionViewSource in your XAML for filtering. We can walk through all that stuff if need be. You already know how to implement INotifyPropertyChanged so you're on your way.

我将 Fruits 应用程序快速重写为 MVVM.通常我会将 AddNewFruit 命令作为带有委托命令的视图模型的属性来执行,但我不想编写委托命令类并将其粘贴进去.不过,这里的内容是可以的.

I did a quick rewrite of the Fruits application as an MVVM thing. Usually I'd do the AddNewFruit command as a property of the viewmodel with a delegate command, but I didn't feel like writing a delegate command class and pasting it in. What's here is OK though.

ViewModels.cs

ViewModels.cs

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Media;

namespace Fruits.ViewModels
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }

    public class Fruit : ViewModelBase
    {
        public Fruit()
        {
        }

        public Fruit(string name, String clrString)
        {
            FruitName = name;
            //  Parse colors like so: (Color)ColorConverter.ConvertFromString(clrString);
            FruitColor = clrString;
        }

        public Fruit(string name, Color clr)
        {
            FruitName = name;
            FruitColor = clr.ToString();
        }

        private string _fruitname;
        public string FruitName
        {
            get
            {
                return _fruitname;
            }
            set
            {
                if (_fruitname != value)
                {
                    _fruitname = value;
                    OnPropertyChanged("FruitName");
                }
            }
        }

        private String _fruitcolor;
        public String FruitColor
        {
            get
            {
                return _fruitcolor;
            }
            set
            {
                if (_fruitcolor != value)
                {
                    _fruitcolor = value;
                    OnPropertyChanged("FruitColor");
                }
            }
        }

        private bool _isSelected = true;
        //  NOTE: I renamed this property
        public bool IsSelected
        {
            get
            {
                return _isSelected;
            }
            set
            {
                if (_isSelected != value)
                {
                    _isSelected = value;
                    OnPropertyChanged("IsSelected");
                }
            }
        }
    }

    #region MainViewModel Class
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            Fruits = new ObservableCollection<Fruit>();

        }

        #region ShowSelectedFruitOnly Property
        private bool _showSelectedFruitOnly = true;
        public bool ShowSelectedFruitOnly
        {
            get { return _showSelectedFruitOnly; }
            set
            {
                if (value != _showSelectedFruitOnly)
                {
                    _showSelectedFruitOnly = value;
                    FruitsView.Refresh();
                    OnPropertyChanged("ShowSelectedFruitOnly");
                }
            }
        }
        #endregion ShowSelectedFruitOnly Property

        #region Add Methods
        public void AddNewFruit()
        {
            Fruits.Add(new Fruit(NewFruitName, NewFruitColor));

            NewFruitName = "";
            NewFruitColor = "";
        }

        public void AddNewFruit(string name, string color)
        {
            Fruits.Add(new Fruit(name, color));
        }

        public void AddNewFruit(string name, Color color)
        {
            Fruits.Add(new Fruit(name, color));
        }
        #endregion Add Methods

        #region NewFruitName Property
        private String _newFruitName = default(String);
        public String NewFruitName
        {
            get { return _newFruitName; }
            set
            {
                if (value != _newFruitName)
                {
                    _newFruitName = value;
                    OnPropertyChanged("NewFruitName");
                }
            }
        }
        #endregion NewFruitName Property

        #region NewFruitColor Property
        private String _newFruitColor = default(String);
        public String NewFruitColor
        {
            get { return _newFruitColor; }
            set
            {
                if (value != _newFruitColor)
                {
                    _newFruitColor = value;
                    OnPropertyChanged("NewFruitColor");
                }
            }
        }
        #endregion NewFruitColor Property

        public ICollectionView FruitsView { get; private set; }

        #region Fruits Property
        private ObservableCollection<Fruit> _fruits;
        public ObservableCollection<Fruit> Fruits
        {
            get { return _fruits; }
            private set
            {
                if (value != _fruits)
                {
                    _fruits = value;

                    FruitsView = CollectionViewSource.GetDefaultView(Fruits);

                    FruitsView.Filter = FruitFilterPredicate;
                    FruitsView.Refresh();

                    OnPropertyChanged("Fruits");
                }
            }
        }

        protected bool FruitFilterPredicate(Object o)
        {
            if (ShowSelectedFruitOnly)
            {
                return (o as Fruit).IsSelected;
            }

            return true;
        }
        #endregion Fruits Property
    }
    #endregion MainViewModel Class
}

应用程序.xaml

<Application 
    x:Class="Fruits.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Fruits"
    StartupUri="MainWindow.xaml"
    >
    <Application.Resources>
        <Style x:Key="ColorSwatch" TargetType="ContentControl">
            <Setter Property="Width" Value="24" />
            <Setter Property="Height" Value="24" />
            <Setter Property="IsTabStop" Value="false" />
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Rectangle
                            HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            Stroke="Gray"
                            StrokeThickness="1"
                            >
                            <Rectangle.Fill>
                                <SolidColorBrush Color="{Binding}" />
                            </Rectangle.Fill>
                        </Rectangle>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <DataTemplate x:Key='FruitTemp'>
            <StackPanel 
                Orientation='Horizontal'
                Margin='5'>
                <TextBlock 
                    x:Name='tbName'
                    Text='{Binding FruitName}' 
                    Margin='10,0,0,0'
                    Width='100'/>
                <TextBlock 
                    x:Name='tbColor'
                    Text='{Binding FruitColor}'
                    Margin='10,0,0,0'
                    Width='100' />
                <ContentControl
                    Width="16"
                    Height="16"
                    Style="{StaticResource ColorSwatch}"
                    Content="{Binding FruitColor}"
                    />
                <!-- The problem here was you were trying to bind Checked, an event, 
                instead if IsChecked, a bool? property. 
                -->
                <CheckBox 
                    x:Name='cbSelected'
                    Content='Selected'
                    Margin='10,0,0,0'
                    IsChecked='{Binding IsSelected}' 
                    />
            </StackPanel>
        </DataTemplate>
    </Application.Resources>
</Application>

主窗口.xaml

<Window 
    x:Class="Fruits.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Fruits"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525"
    >
    <Window.Resources>
        <RoutedCommand
            x:Key="AddFruit"
            />
    </Window.Resources>
    <Window.CommandBindings>
        <CommandBinding 
            Command='{StaticResource AddFruit}'
            Executed='AddFruitCommandBinding_Executed'
            CanExecute='AddFruitCommandBinding_CanExecute' 
            />
    </Window.CommandBindings>
    <Grid>
        <StackPanel Orientation='Vertical' Margin='10'>
            <CheckBox IsChecked="{Binding ShowSelectedFruitOnly}">Selected Fruit Only</CheckBox>
            <ListBox 
                x:Name='MyList' 
                ItemsSource="{Binding FruitsView}"
                ItemTemplate='{StaticResource FruitTemp}'
                />
            <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                <Label Width="100">New Name:</Label>
                <TextBox Width="200" Text="{Binding NewFruitName}" />
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                <Label Width="100">New Color:</Label>
                <TextBox Width="200" Text="{Binding NewFruitColor, UpdateSourceTrigger=PropertyChanged}" />

                <ContentControl
                    Style="{StaticResource ColorSwatch}"
                    Margin="2" 
                    VerticalAlignment="Center" 
                    Content="{Binding NewFruitColor}"
                    />
            </StackPanel>
            <Button 
                x:Name='AddFruit'
                Height='auto'
                Width='auto'
                Content='Add New Fruit 2'
                Margin='0,10,0,0'
                Command='{StaticResource AddFruit}'
                />
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

MainWindow.xaml.cs

using System;
using System.Windows;
using System.Windows.Input;
using Fruits.ViewModels;

namespace Fruits
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new MainViewModel();

            ViewModel.AddNewFruit("Jackfruit", "Yellow");
            ViewModel.AddNewFruit("Watermelon", "ForestGreen");
            ViewModel.AddNewFruit("Apple", "Red");
            ViewModel.AddNewFruit("Banana", "Yellow");
            ViewModel.AddNewFruit("Orange", "DeepSkyBlue");

            ViewModel.Fruits[0].IsSelected = false;
            ViewModel.Fruits[1].IsSelected = false;

            ViewModel.FruitsView.Refresh();
        }

        public MainViewModel ViewModel { get { return DataContext as MainViewModel; } }

        private void AddFruitCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            ViewModel.AddNewFruit();
        }

        private void AddFruitCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute =
                ViewModel != null
                && !String.IsNullOrWhiteSpace(ViewModel.NewFruitName)
                && !String.IsNullOrWhiteSpace(ViewModel.NewFruitColor)
                ;
        }
    }
}

截图:

标准 HTML 颜色名称(请参阅 System.Windows.Media.Colors 以了解 WPF 预定义颜色常量) 将适用于颜色,#RRGGBB#AARRGGBB 十六进制也将适用颜色.

Standard HTML color names (see System.Windows.Media.Colors for WPF predefined color constants) will work for the colors, and so will #RRGGBB or #AARRGGBB hex colors.

这篇关于将列表框绑定到 observablecollection的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆