我有一个WPF ListBox,我添加了一些'FooBar'对象作为项目(通过代码)。 FooBars不是WPF对象,只是带有覆盖ToString()函数的哑类。
现在,当我更改影响ToString的属性时,我想让ListBox更新。
- 我该怎么做“快速又脏”(如重画)。
- 依赖属性是否可以继续?
- 是否值得/总是可取的,为我的FooBars创建一个wpf包装类?
谢谢...
我有一个WPF ListBox,我添加了一些'FooBar'对象作为项目(通过代码)。 FooBars不是WPF对象,只是带有覆盖ToString()函数的哑类。
现在,当我更改影响ToString的属性时,我想让ListBox更新。
谢谢...
你的类型应该实现 INotifyPropertyChanged
这样集合就可以检测到变化。正如萨姆所说,过去了 string.Empty
作为论点。
您 也 需要有 ListBox
的数据源是一个提供更改通知的集合。这是通过 INotifyCollectionChanged
接口(或不是WPF IBindingList
接口)。
当然,你需要的 INotifyCollectionChanged
触发任何一个成员的接口 INotifyPropertyChanged
物品发射它的事件。值得庆幸的是,框架中有一些类型可以为您提供此逻辑。可能是最合适的一个 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绑定中的字符串格式。您可能会发现它很有帮助。
编辑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 <> 为您的ListBox IetmsSource属性。 WPF将自动检测此集合内容中的任何更改,并强制更新相应的ListBox以反映更改。
您可能需要阅读此MSDN文章以获取更多信息。编写它是为了具体解释如何处理这种情况
http://msdn.microsoft.com/en-us/magazine/dd252944.aspx?pr=blog
尝试在FooBar对象上实现INotifyPropertyChanged接口。当它们更改时,引发PropertyChanged事件,将string.Empty作为属性名称传递。这应该够了吧。
如果用于存储项目的集合对象是observablecollection <>,则会为您处理。
即如果集合被更改,任何控件数据绑定都将被更新,反之亦然。
这是我为此工作的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"));
}
}
}
这是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>
按下按钮会更新第一个FooBar的第一个属性,并使其显示在ListBox中。