.net 项目更改时更新 WPF 列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/553150/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:09:06  来源:igfitidea点击:

Updating WPF list when item changes

.netwpflistbox

提问by Benjol

I have a WPF ListBox, and I've added some 'FooBar' objects as items (by code). FooBars aren't WPF objects, just dumb class with an overwritten ToString() function.

我有一个 WPF ListBox,并且我添加了一些“FooBar”对象作为项目(通过代码)。FooBars 不是 WPF 对象,只是具有覆盖 ToString() 函数的愚蠢类。

Now, when I change a property which influences the ToString, I'd like to get the ListBox to update.

现在,当我更改影响 ToString 的属性时,我想让 ListBox 更新。

  1. How can I do this 'quick and dirty' (like repaint).
  2. Is dependency properties the way to go on this?
  3. Is it worth it/always advisable, to create a wpf wrapper class for my FooBars?
  1. 我怎么能做到这个“又快又脏”(比如重新粉刷)。
  2. 依赖属性是解决这个问题的方法吗?
  3. 为我的 FooBars 创建一个 wpf 包装类是否值得/总是可取的?

Thanks...

谢谢...

回答by Drew Noakes

Your type should implement INotifyPropertyChangedso that a collection can detect the changes. As Sam says, pass string.Emptyas the argument.

您的类型应该实现,INotifyPropertyChanged以便集合可以检测到更改。正如 Sam 所说,string.Empty作为参数传递。

You alsoneed to have the ListBox's data source be a collection that provides change notification. This is done via the INotifyCollectionChangedinterface (or the not-so-WPF IBindingListinterface).

需要将ListBox的数据源设为提供更改通知的集合。这是通过INotifyCollectionChanged接口(或非 WPFIBindingList接口)完成的。

Of course, you need the INotifyCollectionChangedinterface to fire whenever one of the member INotifyPropertyChangeditems fires its event. Thankfully there are a few types in the framework that provide this logic for you. Probably the most suitable one is ObservableCollection<T>. If you bind your ListBoxto an ObservableCollection<FooBar>then the event chaining will happen automatically.

当然,您需要INotifyCollectionChanged在成员INotifyPropertyChanged项之一触发其事件时触发该接口。幸运的是,框架中有几种类型可以为您提供这种逻辑。可能最合适的一个是ObservableCollection<T>。如果您将您的绑定ListBox到 anObservableCollection<FooBar>那么事件链将自动发生。

On a related note, you don't have to use a ToStringmethod just to get WPF to render the object in the way that you want. You can use a DataTemplatelike this:

在相关说明中,您不必使用ToString方法来让 WPF 以您想要的方式呈现对象。你可以使用DataTemplate这样的:

<ListBox x:Name="listBox1">
    <ListBox.Resources>
        <DataTemplate DataType="{x:Type local:FooBar}">
            <TextBlock Text="{Binding Path=Property}"/>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>

In this way you can control the presentation of the object where it belongs -- in the XAML.

通过这种方式,您可以控制对象所属的表示形式——在 XAML 中。

EDIT 1I noticed your comment that you're using the ListBox.Itemscollection as your collection. This won't do the binding required. You're better off doing something like:

编辑 1我注意到您的评论,您正在使用该ListBox.Items集合作为您的集合。这不会执行所需的绑定。你最好做这样的事情:

var collection = new ObservableCollection<FooBar>();
collection.Add(fooBar1);

_listBox.ItemsSource = collection;

I haven't checked that code for compilation accuracy, but you get the gist.

我尚未检查该代码的编译准确性,但您已了解要点。

EDIT 2Using the DataTemplateI gave above (I edited it to fit your code) fixes the problem.

编辑 2使用DataTemplate我上面给出的(我编辑它以适合您的代码)解决了问题。

It seems strange that firing PropertyChangeddoesn't cause the list item to update, but then using the ToStringmethod isn't the way that WPF was intended to work.

触发PropertyChanged不会导致列表项更新似乎很奇怪,但是使用该ToString方法并不是 WPF 旨在工作的方式。

Using this DataTemplate, the UI binds correctly to the exact property.

使用此 DataTemplate,UI 可以正确绑定到确切的属性。

I asked a question on here a while back about doing string formatting in a WPF binding. You might find it helpful.

不久前我在这里问了一个关于在 WPF 绑定中进行字符串格式化的问题。你可能会发现它很有帮助。

EDIT 3I'm baffled as to why this is still not working for you. Here's the complete source code for the window I'm using.

编辑 3我很困惑为什么这仍然不适合你。这是我正在使用的窗口的完整源代码。

Code behind:

后面的代码:

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

namespace StackOverflow.ListBoxBindingExample
{
    public partial class Window1
    {
        private readonly FooBar _fooBar;

        public Window1()
        {
            InitializeComponent();

            _fooBar = new FooBar("Original value");

            listBox1.ItemsSource = new ObservableCollection<FooBar> { _fooBar };
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            _fooBar.Property = "Changed value";
        }
    }

    public sealed class FooBar : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string m_Property;

        public FooBar(string initval)
        {
            m_Property = initval;
        }

        public string Property
        {
            get { return m_Property; }
            set
            {
                m_Property = value;
                OnPropertyChanged("Property");
            }
        }

        private void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

XAML:

XAML:

<Window x:Class="StackOverflow.ListBoxBindingExample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:StackOverflow.ListBoxBindingExample"
    Title="Window1" Height="300" Width="300">
    <DockPanel LastChildFill="True">
        <Button Click="button1_Click" DockPanel.Dock="Top">Click Me!</Button>
        <ListBox x:Name="listBox1">
            <ListBox.Resources>
                <DataTemplate DataType="{x:Type local:FooBar}">
                    <TextBlock Text="{Binding Path=Property}"/>
                </DataTemplate>
            </ListBox.Resources>
        </ListBox>
    </DockPanel>
</Window>

回答by JaredPar

The correct solution here is to use an ObservableCollection<>for your ListBox IetmsSource property. WPF will automatically detect any changes in this collection's contents and force the corresponding ListBox to update to reflect the changes.

此处正确的解决方案是为 ListBox IetmsSource 属性使用ObservableCollection<>。WPF 将自动检测此集合内容中的任何更改,并强制相应的 ListBox 更新以反映更改。

You may want to read this MSDN article for more information. It was written to specifically explain how to handle this scenario

您可能需要阅读这篇 MSDN 文章以获取更多信息。写它是为了专门解释如何处理这种情况

http://msdn.microsoft.com/en-us/magazine/dd252944.aspx?pr=blog

http://msdn.microsoft.com/en-us/magazine/dd252944.aspx?pr=blog

回答by hughdbrown

Here is the C# code I have working for this:

这是我为此工作的 C# 代码:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace ListboxOfFoobar
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ObservableCollection<FooBar> all = (ObservableCollection<FooBar>)FindResource("foobars");
            all[0].P1 = all[0].P1 + "1";
        }
    }
    public class FooBar : INotifyPropertyChanged
    {
        public FooBar(string a1, string a2, string a3, string a4)
        {
            P1 = a1;
            P2 = a2;
            P3 = a3;
            P4 = a4;
        }

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

        private String p1;
        public string P1
        {
            get { return p1; }
            set
            {
                if (value != this.p1)
                {
                    this.p1 = value;
                    NotifyPropertyChanged("P1");
                }
            }
        }
        private String p2;
        public string P2
        {
            get { return p2; }
            set
            {
                if (value != this.p2)
                {
                    this.p2 = value;
                    NotifyPropertyChanged("P2");
                }
            }
        }
        private String p3;
        public string P3
        {
            get { return p3; }
            set
            {
                if (value != this.p3)
                {
                    this.p3 = value;
                    NotifyPropertyChanged("P3");
                }
            }
        }
        private String p4;
        public string P4
        {
            get { return p4; }
            set
            {
                if (value != this.p4)
                {
                    this.p4 = value;
                    NotifyPropertyChanged("P4");
                }
            }
        }
        public string X
        {
            get { return "Foooooo"; }
        }
    }
    public class Foos : ObservableCollection<FooBar>
    {
        public Foos()
        {
            this.Add(new FooBar("a", "b", "c", "d"));
            this.Add(new FooBar("e", "f", "g", "h"));
            this.Add(new FooBar("i", "j", "k", "l"));
            this.Add(new FooBar("m", "n", "o", "p"));
        }
    }
}

Here is the XAML:

这是 XAML:

<Window x:Class="ListboxOfFoobar.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ListboxOfFoobar"
    xmlns:debug="clr-namespace:System.Diagnostics;assembly=System"

    Title="Window1" Height="300" Width="300"        
        >
    <Window.Resources>
        <local:Foos x:Key="foobars" />
        <DataTemplate x:Key="itemTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock MinWidth="80" Text="{Binding Path=P1}"/>
                <TextBlock MinWidth="80" Text="{Binding Path=P2}"/>
                <TextBlock MinWidth="80" Text="{Binding Path=P3}"/>
                <TextBlock MinWidth="80" Text="{Binding Path=P4}"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>

    <DockPanel>
        <ListBox DockPanel.Dock="Top"
         ItemsSource="{StaticResource foobars}"
         ItemTemplate="{StaticResource itemTemplate}" Height="229" />
        <Button  Content="Modify FooBar" Click="Button_Click" DockPanel.Dock="Bottom" />
    </DockPanel>
</Window>

Pressing the Button causes the first property of the first FooBar to be updated and for it to show in the ListBox.

按下 Button 会导致第一个 FooBar 的第一个属性被更新并显示在 ListBox 中。

回答by Samuel Hyman

Try implementing the INotifyPropertyChanged interface on your FooBar objects. When they change, raise PropertyChanged events, passing string.Empty as the property name. That should do the trick.

尝试在您的 FooBar 对象上实现 INotifyPropertyChanged 接口。当它们改变时,引发 PropertyChanged 事件,传递 string.Empty 作为属性名称。这应该够了吧。

回答by Blounty

If the collection object you use to store the items is an observablecollection<> then this is handled for you.

如果您用来存储项目的集合对象是 observablecollection<>,那么这将为您处理。

i.e if the collection is changed any controls databound to it will be updated and vice versa.

即,如果集合发生更改,则绑定到它的任何控件数据都将更新,反之亦然。