简体   繁体   English

将可观察集合绑定到XAML中的ListBox

[英]Binding observable collection to ListBox in XAML

I've spent lots of hours with this problem. 我花了很多时间来解决这个问题。

I have a class with data: 我有一个数据类:

public class User : INotifyPropertyChanged
{
    private int _key;
    private string _fullName;
    private string _nick;

    public int Key
    {
        get{return _key;}
        set { _key = value; NotifyPropertyChanged("Key"); }
    }
    public string Nick
    {
        get { return _nick; }
        set { _nick = value; NotifyPropertyChanged("Nick"); }
    }
    public string FullName
    {
        get { return _fullName; }
        set { _fullName = value; NotifyPropertyChanged("FullName"); }
    }


    public User()
    {
        Nick = "nickname";
        FullName = "fullname";
    }

    public User(String nick, String name, int key)
    {
        Nick = nick;
        FullName  = name;
    }


    //INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public override string ToString() 
    { 
        return string.Format("{0} {1}, {2}", Key, Nick, FullName); 
    }

}

Next I have a class with observablecollection of userClass class: 接下来我有一个带有observablecollection userClass类的类:

public class UserList : ObservableCollection<UserList>
{
    public UserList (){}

    ~UserList ()
    {
        //Serialize();
    }

    public void Serialize(ObservableCollection<UserList> usersColl) 
    {
        FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
        BinaryFormatter formatter = new BinaryFormatter();
        try
        {
            formatter.Serialize(fs, usersColl);
        }
        catch (SerializationException e)
        {
            Console.WriteLine("Failed to serialize. Reason: " + e.Message);
            throw;
        }
        finally
        {
            fs.Close();
        }
    }

    public void Deserialize() 
    {
        FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
        try 
        {
            BinaryFormatter formatter = new BinaryFormatter();
            //users = (Hashtable) formatter.Deserialize(fs);
            //usersColl = (ObservableCollection<userClass>)formatter.Deserialize(fs);
        }
        catch (SerializationException e) 
        {
            MessageBox.Show(" Error: " + e.Message);
            throw;
        }
        finally 
        {
            fs.Close();
        }
    }

}

In fact, after lots of testing an editing, big part of code doesn't work, like serialization. 事实上,经过大量的编辑测试后,大部分代码都无法正常工作,比如序列化。 But it is not necessary for data binding and binding is what i am solving now. 但是数据绑定和绑定不是我现在要解决的问题。

So i have this collection and want to bind it to listBox. 所以我有这个集合,并希望将它绑定到listBox。 I tried several ways, but haven't got it to work. 我尝试了几种方法,但还没有开始工作。

The last one I tried gave me the write error: 我试过的最后一个给了我写错误:

The resource 'users' cannot be resolved. 无法解析资源“用户”。

<ListBox Grid.Column="0" Name="userViewLeft" ItemsSource="{Binding Source={StaticResource users} }" />

Some points to be noted 有些要点需要注意

  • Make Properties public and not private . 将属性设为public而非private
  • Make Variables private . 将变量设为private
  • Follow Naming Conventions and don't append class behind the class. 遵循命名约定,不要在class后面添加课程。
  • ItemsSource you supply should be as per scope of the data, In my example the userlist in class scope and I have provided the ItemSource on Window Loaded event. 你提供的ItemsSource应该按照数据的范围,在我的例子中,类范围内的用户列表和我在Window Loaded事件上提供了ItemSource。

Here is the an complete example code, In this I have nested the Grid Control inside ListBox because later on you can change the ListBox property for VirtualizingStackPanel. 这是一个完整的示例代码,在这里我嵌套了ListBox中的Grid Control,因为稍后您可以更改VirtualizingStackPanel的ListBox属性。 So that it would give huge performance gain when you have heavy updates on the list. 因此,当您在列表上进行大量更新时,它会带来巨大的性能提升。 Also you can use BindingList which is in my opinion better than ObservableCollection performance wise. 你也可以使用BindingList ,这在我看来比ObservableCollection性能更好。

User class: 用户类:

    public class User : INotifyPropertyChanged
    {
        private int _key;
        private string _fullName;
        private string _nick;

        public int Key
        {
            get { return _key; }
            set { _key = value; NotifyPropertyChanged("Key"); }
        }
        public string NickName
        {
            get { return _nick; }
            set { _nick = value; NotifyPropertyChanged("NickName"); }
        }
        public string Name
        {
            get { return _fullName; }
            set { _fullName = value; NotifyPropertyChanged("Name"); }
        }

        public User(String nick, String name, int key)
        {
            this.NickName = nick;
            this.Name = name;
            this.Key = key; 
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public override string ToString()
        {
            return string.Format("{0} {1}, {2}", Key, NickName, Name);
        }
    }

User List class: 用户列表类:

    public class Users : ObservableCollection<User>
    {
        public Users()
        {
            Add(new User("Jamy", "James Smith", Count));
            Add(new User("Mairy", "Mary Hayes", Count));
            Add(new User("Dairy", "Dary Wills", Count));
        }
    }

XAML: XAML:

   <Grid>
        <Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="416,12,0,0" x:Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
        <ListBox x:Name="UserList" HorizontalContentAlignment="Stretch" Margin="12,41,12,12">
            <ListBox.ItemTemplate>
                <DataTemplate>
                        <Grid Margin="10">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="20" />
                                <ColumnDefinition Width="150" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Text="{Binding Key}" Margin="3" Grid.Column="0" />
                            <TextBlock Text="{Binding NickName}" Margin="3" Grid.Column="1" />
                            <TextBlock Text="{Binding Name}" Margin="3" Grid.Column="2" />
                        </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>

XAML Code behind: XAML代码背后:

public partial class MainWindow : Window
{
    public static Users userslist = new Users();
    DispatcherTimer timer = new DispatcherTimer();

    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        timer.Interval = DateTime.Now.AddSeconds(10) - DateTime.Now;
        timer.Tick += new EventHandler(timer_Tick);
        UserList.ItemsSource = userslist;
    }

    void timer_Tick(object sender, EventArgs e)
    {
        userslist.Add(new User("Jamy - " + userslist.Count, "James Smith", userslist.Count));
        userslist.Add(new User("Mairy - " + userslist.Count, "Mary Hayes", userslist.Count));
        userslist.Add(new User("Dairy - " + userslist.Count, "Dary Wills", userslist.Count));
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        if (button1.Content.ToString() == "Start")
        {
            button1.Content = "Stop";
            timer.Start();
        }
        else
        {
            button1.Content = "Start";
            timer.Stop();
        }
    }

}

You need to do 2 things: 你需要做两件事:

Firstly, set the DataContext of whatever element ( Window / UserControl /whatever) contains your ListBox to an object that looks like: 首先,将包含ListBox的任何元素( Window / UserControl / whatever)的DataContext设置为如下所示的对象:

public class ViewModel
{
    public ViewModel() { this.users = new userListClass(); }
    public userListClass users { get; private set; }
}

This is your view model, and it is what you want to bind to. 这是您的视图模型,它是您要绑定的内容。

Secondly, change your binding to ItemsSource="{Binding Path=users}" . 其次,将您的绑定更改为ItemsSource="{Binding Path=users}" This translates into "set the value of my ItemsSource property to the value of the property users on this.DataContext . Because the DataContext is inherited from the parent, and you set this to the ViewModel class above, your ListBox will now display your user list. 这转换为“将我的ItemsSource属性的值设置为this.DataContext上的属性users的值。因为DataContext是从父级继承的,并且您将其设置为上面的ViewModel类,所以ListBox现在将显示您的用户列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM