在WPF中将控件绑定到集合/数组中的单个值

4
在WPF中,我有一个bool?值的集合,我想以编程方式将每个值绑定到单独的复选框。我希望这些绑定是双向的,这样在代码中更改集合中的各个项的值会更新复选框,反之亦然。
我已经花了很长时间来尝试解决这个问题,但我完全被卡住了。使用以下代码时,复选框仅在窗口加载时获得正确的值,之后就不再更新了。 (更新:这似乎是.NET4中的一个错误,因为在相同的.NET3.5项目中会更新集合。更新:Microsoft已确认该错误,并将在.NET4版本中修复。)
非常感谢您的帮助!
C#:
namespace MyNamespace
{
    public partial class MyWindow : Window, INotifyPropertyChanged
    {
        public MyWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

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

        public List<bool?> myCollection = new List<bool?>
            { true, false, true, false, true, false };

        public List<bool?> MyCollection
        {
            get { return myCollection; }
            set { myCollection = value; }
        }
    }
}

XAML:

<CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}">

我仍在研究前三个答案,但我对于为什么我的代码似乎对前两位回答者有效(至少在 CheckBox -> MyCollection 方向上)感到困惑。对我来说,这根本没有发生。我正在使用 VS2010 Beta 2 和 .NET4,如果相关的话。 - Matt Jenkins
我肯定没想到这会是相关的:我把所有东西都复制到了一个.NET 3.5项目中,嘿,就这样——CheckBox更新了MyCollection中的值。我想知道为什么在.NET 4中不起作用? - Matt Jenkins
嗯,如果你有像这样的代码,在.NET 3.5中可以工作,但是相同的代码在.NET 4中不起作用,那就说明.NET 4中存在一个错误。也许值得发布到Connect上。 - itowlson
3个回答

5

这里需要做一些更改才能使它正常工作。首先,您需要将布尔值包装在实现INotifyPropertyChanged接口的对象中,以获取所需的更改通知。目前,您正在绑定到集合中的布尔值,而这些值不实现该接口。要实现这一点,您可以创建一个包装类,如下所示:

  public class Wrapper: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private bool val = false;

        public bool Val
        {
            get { return val; }
            set
            {
                val = value;
                this.OnPropertyChanged("Val");
            }
        }

        public Wrapper(bool val)
        {
            this.val = val;
        }

    }

接下来,您需要在表单中创建这些对象,而不是布尔值列表。您可能还想使用可观察集合而不是列表,以便发送添加和删除项的通知。如下所示:

public Window1()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    private ObservableCollection<Wrapper> myCollection = new ObservableCollection<Wrapper>()
        {new Wrapper(true), new Wrapper(false), new Wrapper(true)};


    public ObservableCollection<Wrapper> MyCollection
    {
        get { return myCollection; }
    }

接下来要做的是在您的ui中显示一个复选框列表。为此,WPF提供了itemscontrols。ListBox是一个itemscontrol,因此我们可以将其用作起点。将listbox的itemssource设置为MyCollection。然后,我们需要定义如何在列表框中显示每个Wrapper对象,这可以通过在窗口资源中创建的datatemplate来完成。如下所示:

<Window.Resources>
    <DataTemplate x:Key="myCollectionItems">
        <CheckBox IsChecked="{Binding Path=Val, Mode=TwoWay}"></CheckBox>
    </DataTemplate>
</Window.Resources>
<Grid>
    <ListBox ItemsSource="{Binding Path=MyCollection}" ItemTemplate="{StaticResource myCollectionItems}"></ListBox>
</Grid>

这将为您提供一个简单的复选框演示,其中复选框的值绑定到布尔值列表。


虽然你的建议可能是正确的,但原帖的代码只需要将集合类型从“List”更改为“ObservableCollection”,就可以解决问题了。你的回答也许是正确的,但却不是当前问题所在。 - Aviad P.
问题说明需要绑定到复选框列表,这显然最好使用ItemsControl而不是绑定到每个项目。问题还说明代码中的更改应导致复选框更改,这通过在包装器对象中实现更改通知来演示。据我所知,如果不这样做,更改将不会传递到UI。 - mattythomas2000
非常感谢你的回复,Matt。你提供的方法对我来说非常有用,因为我打算以编程方式创建复选框,而你的答案为此提供了一个优雅的解决方案。 - Matt Jenkins

0

你为什么认为它不起作用?对我来说它是有效的 :)

这是我的测试XAML:

<UniformGrid>
    <CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}"/>
    <ListBox ItemsSource="{Binding MyCollection}"/>
    <Button Content="Test" Click="Button_Click"/>
</UniformGrid>

这是我的代码后台:
private void Button_Click(object sender, RoutedEventArgs e)
{

}

我在 Button_Click 上放置了一个断点,并检查了 MyCollection[0],它根据 CheckBoxIsChecked 值进行了更新。

尝试将你的集合类型从 List<bool?> 更改为 ObservableCollection<bool?>,也许这就是你认为它对你不起作用的原因(即集合的更改没有在视图中反映出来)。


感谢您的回复,Aviad。我再次在新项目中尝试了我的代码,但没有成功。不确定您是如何做到的!然而,当我将List<>更改为ObservableCollection<>时,代码中的值更改会立即反映在复选框上,但反之则不行。但至少这是一个开始。 - Matt Jenkins
请参考我的评论,CheckBox->MyCollection在.NET3.5中可用,但在.NET4中不可用。 - Matt Jenkins

0
将您的 List<bool?> 更改为 ObservableCollection<bool?>。List 不会引发 WPF 需要更新 UI 的更改通知,而 ObservableCollection 会处理此情况,其中列表条目已更改且需要相应更新复选框。
在另一个方向上,即使使用 List<bool?>,对我来说也是可行的 -- 即切换复选框会修改集合中的值。您的绑定语法肯定是正确的。

感谢您的回复。更改为ObservableCollection确实会导致UI在代码中的值发生更改时进行更新。但是奇怪的是,如果我保留其他代码不变,则另一个方向无法与我的代码一起正常工作。 - Matt Jenkins
请看我的评论,CheckBox->MyCollection在.NET 3.5中可以工作,但在.NET 4中不能。 - Matt Jenkins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接