WPF双向绑定不能在CheckBox和List <string>上工作

时间:2016-09-23 15:34:25

标签: wpf xaml binding two-way

即使在审查了大量解决方案提案后,我也无法在xaml中获得简单的双向绑定。我有一个Window,一个dataContext和一个App。问题是:

a)当App构造函数运行时,Window(在同一个构造函数中初始化和.Show - )显示,但根本没有更新,即使我在C#代码中切换复选框值a几次;

b)当App构造函数完成时,Window只更新一次;我设置它,以便如果我单击窗口中的复选框,App中的事件处理程序(绑定到DataContext属性更改通知)应该增加字符串列表的大小,这也会显示。列表的增加在代码中正确发生,但不会反映在窗口中。

摘要

  • 窗口中的用户输入到达应用程序的C#代码正常:我可以对复选框更改等进行操作。
  • 相反方向不起作用:每当通过代码在dataContext中更改项目时,即使iNotifyProperty已实现并执行,Window也不会自动更新。

我期望的是

a)当App构造函数运行并切换CheckBox值时,Window应该通过设置/清除框上的勾号来反映更改;

b)在App构造函数完成后,每当我将CheckBox从FALSE切换为TRUE时,NameList都会附加一个新字符串。我希望窗口中的列表相应增加并自动显示完整的附加NameList内容。

观察

  • 我尝试确保在Window上调用InitializeComponent之前设置了Window上的DataContext。不幸的是,真的没有什么区别......
  • 我在MainWindow.xaml文件中的VS中获得了一条线索:CheckBox路径以及ListBox绑定NameList都注明了 Cannot resolve symbol due to unknown DataContext 但是,当App构造函数终止时,Window会更新,当我单击CheckBox时,会触发正确的NotifyProperty事件。这告诉我运行时绑定实际上应该工作......显然只是单向,而不是双向。

MainWindow.xaml:

<Window x:Class="StatisticsEvaluation.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <StackPanel Orientation="Vertical">

        <CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content="CheckBox" />

        <ListBox ItemsSource="{Binding NameList, Mode=TwoWay}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

        <TextBlock FontSize="18" FontFamily="Arial" Foreground="Black" Text="TextBlock" Visibility="Visible" />

    </StackPanel>
</Grid>

MainWindow.xaml.cs:

namespace StatisticsEvaluation
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {            
    }
}

}

App和DataContext:

namespace StatisticsEvaluation
{
    public class DataContextClass : INotifyPropertyChanged
    {
        private bool isChecked;

        public bool IsChecked
        {
            get
            {
                return isChecked;
            }

            set
            {
                isChecked = value;
                OnPropertyChanged("IsChecked");
            }
        }

        private List<string> nameList;

        public List<string> NameList
        {
            get
            {
                return nameList;
            }

            set
            {
                nameList = value;
                OnPropertyChanged("NameList");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

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


    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>

    public partial class App : Application
    {
        private MainWindow MyWindow { get; set; }

        private DataContextClass MyDataContext{ get; set; }

        private void HandleDataContextPropertyChange(object sender, PropertyChangedEventArgs e)
        {
            // If the CheckBox was just toggled to TRUE, increase the NameList
            // with an additional name and call OnPropertyChanged on it ... 
            // hoping that this would trigger a Window UI update - but no luck !

            if ((e.PropertyName == "IsChecked") && MyDataContext.IsChecked)
            {
                var randomProvider = new Random();
                MyDataContext.NameList.Add(randomProvider.Next().ToString());
                MyDataContext.OnPropertyChanged("NameList");
            }
        }

        public App()
        {
            MyDataContext = new DataContextClass();
            MyDataContext.PropertyChanged += HandleDataContextPropertyChange;

            MyWindow = new MainWindow {DataContext = MyDataContext};
            MyWindow.InitializeComponent();
            MyWindow.Show();

            MyDataContext.NameList = new List<string>();
            MyDataContext.NameList.Add("FirstName");
            MyDataContext.NameList.Add("SecondName");
            MyDataContext.NameList.Add("ThirdName");

            MyDataContext.IsChecked = true;
            Thread.Sleep(3000);
            MyDataContext.IsChecked = false;
            Thread.Sleep(3000);
            MyDataContext.IsChecked = true;
        }       
    }
}

当我启动应用程序时,一旦App构造函数点击.Show,就会出现以下窗口:

enter image description here

App contructor完成后,Window会更新一次,但不会再次更新,无论有多少字符串添加到NameList

enter image description here

任何想法,为什么我的双向绑定只能在一个方向上工作?

1 个答案:

答案 0 :(得分:0)

如果绑定集合未实现select t.* from (select t.*, (@pn := (case when (@pcy := @pn) = NULL then -1 -- never gets here when (@pn := category) = NULL then -1 -- never gets here else @pcy end) ) as next_category from (select t.*, (@pc := (case when (@pcx := @pc) = NULL then -1 -- never gets here when (@pc := category) = NULL then -1 -- never gets here else @pcx end) ) as prev_category from t cross join (select @pc := '') params order by t.begintime ) t cross join (select @pn := '') params order by t.begintime desc ) t where prev_category = @cat1 and next_category = @cat2; (例如INotifyCollectionChanged),则在尝试更新视图时,您将获得不一致或不存在的行为。我注意到,在将检查状态切换为true后,在弹出鼠标滚轮时,列表确实会更新。另外,正如@Clemens所说,ObservableCollection<T>绑定应该是ItemsSource,因为这是唯一有意义的模式。

顺便说一句,你应该使用符合Mode=TwoWay标准的集合,因为如果你在完成后没有清除绑定,你可能会遇到泄漏[1]。这在单窗口应用程序中不是问题,但现在值得一提。

至于INotifyCollectionChanged在睡眠之间切换,我的有根据的猜测是IsChecked正在UI线程上发生(从而将其绑定),所以你已经死了6秒Thread.Sleep没用的时间。我能够用以下方法解决这个问题(假设正在使用正确的集合类型):

PropertyChanged

并在private async void Toggle() { MyDataContext.IsChecked = true; await Task.Delay(3000); MyDataContext.IsChecked = false; await Task.Delay(3000); MyDataContext.IsChecked = true; } 构造函数的末尾调用Toggle()。不幸的是,这导致应用程序尝试从不起作用的不同线程修改集合。然后你可以用一些荒谬的东西来解决那个

App

但这只是强制你的程序结构不佳。 编辑:我忘了提到使用async / await还有释放UI线程的额外好处;它不再在检查状态之间锁定整个窗口。

我建议您将代码分成适当的文件,然后将逻辑分成适当的位置。您的 ... Toggle(Application.Current.Dispatcher); } private async void Toggle(System.Windows.Threading.Dispatcher d) { d.Invoke(() => { MyDataContext.IsChecked = true; }); await Task.Delay(3000); d.Invoke(() => { MyDataContext.IsChecked = false; }); await Task.Delay(3000); d.Invoke(() => { MyDataContext.IsChecked = true; }); } 可以在HandleDataContextPropertyChange的设置者中进行,减去通知呼叫。

[1] https://blog.jetbrains.com/dotnet/2014/09/04/fighting-common-wpf-memory-leaks-with-dotmemory/