与 ItemControl 的双向绑定

发布于 2024-09-01 03:24:32 字数 3509 浏览 0 评论 0原文

我正在尝试编写一个具有 ItemsControl 的用户控件,其中的 ItemsTemplate 包含一个允许 TwoWay 绑定的 TextBox。但是,我一定在代码中的某个地方犯了错误,因为绑定似乎仅在 Mode=OneWay 时才起作用。这是我的项目的相当简化的摘录,但它仍然包含问题:

<UserControl x:Class="ItemsControlTest.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid>
        <StackPanel>
            <ItemsControl ItemsSource="{Binding Path=.}"
                          x:Name="myItemsControl">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBox Text="{Binding Mode=TwoWay,
                                                UpdateSourceTrigger=LostFocus,
                                                Path=.}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
            <Button Click="Button_Click"
                    Content="Click Here To Change Focus From ItemsControl" />
        </StackPanel>
    </Grid>
</UserControl>

这是上述控件背后的代码:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;

namespace ItemsControlTest
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        public ObservableCollection<string> MyCollection
        {
            get { return (ObservableCollection<string>)GetValue(MyCollectionProperty); }
            set { SetValue(MyCollectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyCollection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyCollectionProperty =
            DependencyProperty.Register("MyCollection",
                                        typeof(ObservableCollection<string>),
                                        typeof(UserControl1),
                                        new UIPropertyMetadata(new ObservableCollection<string>()));

        public UserControl1()
        {
            for (int i = 0; i < 6; i++)
                MyCollection.Add("String " + i.ToString());

            InitializeComponent();

            myItemsControl.DataContext = this.MyCollection;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // Insert a string after the third element of MyCollection
            MyCollection.Insert(3, "Inserted Item");

            // Display contents of MyCollection in a MessageBox
            string str = "";
            foreach (string s in MyCollection)
                str += s + Environment.NewLine;
            MessageBox.Show(str);
        }
    }
}

最后,这是主窗口的 xaml:

<Window x:Class="ItemsControlTest.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:ItemsControlTest"
        Title="Window1" Height="300" Width="300">
    <Grid>
        <src:UserControl1 />
    </Grid>
</Window>

嗯,这就是一切。我不确定为什么在窗口中编辑 TextBox.Text 属性似乎没有更新后面代码中绑定的源属性,即 MyCollection。单击按钮几乎会导致问题出现在我面前;)请帮助我理解我哪里出了问题。

谢谢!

安德鲁

I'm trying to write a user control that has an ItemsControl, the ItemsTemplate of which contains a TextBox that will allow for TwoWay binding. However, I must be making a mistake somewhere in my code, because the binding only appears to work as if Mode=OneWay. This is a pretty simplified excerpt from my project, but it still contains the problem:

<UserControl x:Class="ItemsControlTest.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid>
        <StackPanel>
            <ItemsControl ItemsSource="{Binding Path=.}"
                          x:Name="myItemsControl">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBox Text="{Binding Mode=TwoWay,
                                                UpdateSourceTrigger=LostFocus,
                                                Path=.}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
            <Button Click="Button_Click"
                    Content="Click Here To Change Focus From ItemsControl" />
        </StackPanel>
    </Grid>
</UserControl>

Here's the code behind for the above control:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;

namespace ItemsControlTest
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        public ObservableCollection<string> MyCollection
        {
            get { return (ObservableCollection<string>)GetValue(MyCollectionProperty); }
            set { SetValue(MyCollectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyCollection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyCollectionProperty =
            DependencyProperty.Register("MyCollection",
                                        typeof(ObservableCollection<string>),
                                        typeof(UserControl1),
                                        new UIPropertyMetadata(new ObservableCollection<string>()));

        public UserControl1()
        {
            for (int i = 0; i < 6; i++)
                MyCollection.Add("String " + i.ToString());

            InitializeComponent();

            myItemsControl.DataContext = this.MyCollection;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // Insert a string after the third element of MyCollection
            MyCollection.Insert(3, "Inserted Item");

            // Display contents of MyCollection in a MessageBox
            string str = "";
            foreach (string s in MyCollection)
                str += s + Environment.NewLine;
            MessageBox.Show(str);
        }
    }
}

And finally, here's the xaml for the main window:

<Window x:Class="ItemsControlTest.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:ItemsControlTest"
        Title="Window1" Height="300" Width="300">
    <Grid>
        <src:UserControl1 />
    </Grid>
</Window>

Well, that's everything. I'm not sure why editing the TextBox.Text properties in the window does not seem to update the source property for the binding in the code behind, namely MyCollection. Clicking on the button pretty much causes the problem to stare me in the face;) Please help me understand where I'm going wrong.

Thanx!

Andrew

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

懷念過去 2024-09-08 03:24:32

好吧,我相信导致此问题的原因是您直接绑定到 String 。 C# 中的字符串是不可变的,因此当您更改文本时,它无法更改 ObservableCollection 中的基础字符串。要解决此问题,您可以做的就是简单地创建一个模型类来保存字符串数据,然后将 TextBox.Text 绑定到该类内的属性。这是一个示例:

public partial class BindingToString : Window
{
    public BindingToString()
    {
        MyCollection = new ObservableCollection<TestItem>();

        for (int i = 0; i < 6; i++)
            MyCollection.Add(new TestItem("String " + i.ToString()));

        InitializeComponent();

        myItemsControl.DataContext = this.MyCollection;
    }

    public ObservableCollection<TestItem> MyCollection
    {
        get;
        set;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // Display contents of MyCollection in a MessageBox
        string str = "";
        foreach (TestItem s in MyCollection)
            str += s.Name + Environment.NewLine;
        MessageBox.Show(str);
    }
}

public class TestItem
{
    public string Name
    {
        get;
        set;
    }

    public TestItem(string name)
    {
        Name = name;
    }
}

请注意,我将您的依赖属性更改为标准属性 - 没有理由使集合成为依赖属性。除此之外,唯一的区别是包含包装类 TestItem 来保存字符串数据。

<Window x:Class="TestWpfApplication.BindingToString"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BindingToString " Height="300" Width="300">
<Grid>
    <StackPanel>
        <ItemsControl ItemsSource="{Binding}"
                      x:Name="myItemsControl">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button Click="Button_Click"
                Content="Click Here To Change Focus From ItemsControl" />
    </StackPanel>
</Grid>

现在,TextBox 已绑定到 TestItem 上的 Name 路径,并且此绑定可以正常工作并按预期修改集合。

Ok I believe what is causing this problem is that you are binding directly to a String . Strings are immutable in C# and thus when you change the text, it cannot change the underlying string in the ObservableCollection. What you can do to get around this problem is simply create a model class to hold the string data, and then bind the TextBox.Text to a property inside that class. Here is an example:

public partial class BindingToString : Window
{
    public BindingToString()
    {
        MyCollection = new ObservableCollection<TestItem>();

        for (int i = 0; i < 6; i++)
            MyCollection.Add(new TestItem("String " + i.ToString()));

        InitializeComponent();

        myItemsControl.DataContext = this.MyCollection;
    }

    public ObservableCollection<TestItem> MyCollection
    {
        get;
        set;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // Display contents of MyCollection in a MessageBox
        string str = "";
        foreach (TestItem s in MyCollection)
            str += s.Name + Environment.NewLine;
        MessageBox.Show(str);
    }
}

public class TestItem
{
    public string Name
    {
        get;
        set;
    }

    public TestItem(string name)
    {
        Name = name;
    }
}

Notice that I changed your dependency property to a standard property- there is no reason to make the collection a dependency property. Besides that the only difference is the inclusion of the wrapper class TestItem to hold the string data.

<Window x:Class="TestWpfApplication.BindingToString"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BindingToString " Height="300" Width="300">
<Grid>
    <StackPanel>
        <ItemsControl ItemsSource="{Binding}"
                      x:Name="myItemsControl">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button Click="Button_Click"
                Content="Click Here To Change Focus From ItemsControl" />
    </StackPanel>
</Grid>

Now the TextBox is bound to the Name path on TestItem, and this binding works and modifies the collection as expected.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文