我有一个WPF列表框,已经通过代码添加了一些“FooBar”对象作为项目。 FooBar并不是WPF对象,只是带有重写ToString()函数的普通类。
现在,当我更改影响ToString的属性时,我希望列表框更新。
- 我如何快速且简单地做到这一点(例如重新绘制)。
- 依赖属性是否是解决此问题的正确方法?
- 是否值得/总是建议为我的FooBars创建WPF包装类?
谢谢...
你的类型应该实现 INotifyPropertyChanged
接口,这样集合才能检测到更改。正如Sam所说,将string.Empty
作为参数传递。
你还需要让 ListBox
的数据源成为提供更改通知的集合。这可以通过 INotifyCollectionChanged
接口(或不那么WPF的IBindingList
接口)来完成。
当然,当其中一个成员 INotifyPropertyChanged
项触发其事件时,你需要让 INotifyCollectionChanged
接口也触发。幸运的是,框架中有一些类型可以为你提供此逻辑。可能最合适的是 ObservableCollection<T>
。如果你将 ListBox
绑定到一个 ObservableCollection<FooBar>
,则事件链将自动发生。
另外,你不必使用 ToString
方法来使 WPF 以你想要的方式呈现对象。你可以像这样使用 DataTemplate
:
<ListBox x:Name="listBox1">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:FooBar}">
<TextBlock Text="{Binding Path=Property}"/>
</DataTemplate>
</ListBox.Resources>
</ListBox>
通过这种方式,您可以控制对象在XAML中的展示方式。
编辑1 我注意到您的评论,您正在使用ListBox.Items
集合作为您的集合。这不会完成所需的绑定。最好采用以下方法:
var collection = new ObservableCollection<FooBar>();
collection.Add(fooBar1);
_listBox.ItemsSource = collection;
我没有检查过该代码的编译准确性,但你可以理解其大意。
编辑2 使用我上面提供的DataTemplate
(我已将其编辑以适应你的代码)可以解决问题。
触发 PropertyChanged
事件并不会导致列表项更新似乎有些奇怪,但使用 ToString
方法并非 WPF 的预期工作方式。
使用此 DataTemplate,UI 正确地绑定到了确切的属性。
我之前在这里发过一个关于在 WPF 绑定中进行字符串格式化的问题:https://dev59.com/J3RB5IYBdhLWcg3w-8E9。你可能会找到它有用。
编辑3 我很困惑为什么这对你仍然不起作用。这是我正在使用的窗口的完整源代码。
后台代码:
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:
<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>
ObservableCollection
。尝试在上面的代码中将其替换为List<T>
,它仍将正常工作。实际上,ObservableCollection
甚至不知道其项目属性的更改。当您更改FooBar
的属性时,ObservableCollection
既不触发CollectionChanged
也不触发PropertyChanged
(无法放置测试代码,因为没有足够的空间)。但是,如果您要添加/删除列表中的项目,则使用ObservableCollection
是最正确的方法。 - Ilya Serbis在这里正确的解决方案是使用一个ObservableCollection<>作为您的ListBox IetmsSource属性。 WPF将自动检测此集合内容中的任何更改,并强制相应的ListBox更新以反映更改。
您可能需要阅读此MSDN文章以获取更多信息。 它是专门编写的,以解释如何处理此情况
http://msdn.microsoft.com/en-us/magazine/dd252944.aspx?pr=blog
DisplayMemberPath="PropertyName"
属性添加到ListBox中。 - Ilya Serbisusing 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"));
}
}
}
这是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>
如果您用于存储项目的集合对象是ObservableCollection<>,那么这将为您处理。
也就是说,如果更改了集合,则绑定到它的任何控件都将被更新,反之亦然。