WPF双向绑定无法在CheckBox和列表上工作

WPF双向绑定无法在CheckBox和列表上工作<string>

问题描述:

即使在审查了大量解决方案建议后,我仍无法在xaml中获得简单的双向绑定工作。我有一个窗口,一个数据上下文和一个应用程序。问题是:WPF双向绑定无法在CheckBox和列表上工作<string>

a)虽然App构造函数运行时,窗口(在同一构造函数中初始化和.Show -ed)显示出来,但根本没有更新,即使我切换了C#代码中的复选框值几次; b)当App构造函数完成时,窗口只更新一次;我已经设置好了,如果我点击窗口中的复选框,App中的事件处理程序(绑定到DataContext属性更改通知)应增加字符串列表的大小,这也会显示出来。代码中增加的列表正确发生,但不反映在窗口中。

摘要:在Window

  • 用户输入到应用程序的C#代码罚款:我可以在复选框的变化等行动
  • 相反的方向不起作用:每当项目在改变通过代码的dataContext,即使iNotifyProperty被实现并执行,Window也不会自动更新。

我希望那是什么

一),而应用程序的构造和运行的切换复选框值,窗口应该反映通过设置/清除蜱包装盒上的变化;

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

观察

  • 我尽量确保在窗口的DataContext是在窗口调用InitializeComponent之前设置。没有真正不幸的是区别...
  • 我得到的MainWindow.xaml文件在VS一个线索:复选框路径以及列表框绑定NameListCannot resolve symbol due to unknown DataContext 然而,当应用程序注释构造函数终止窗口被更新,当我点击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

一旦应用程序构造器h作为成品,窗口更新一次,但从来没有一次后,不管有多少字符串被添加到NameList

enter image description here

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

+1

我建议你删除所有奇怪的'MyDataContext'东西在'App'和刚刚创建的MainWindow的构造函数什么的视图模型。此外,对于将绑定到WPF中的控件的集合,始终使用'ObservableCollection '。不要使用列表。它的内容改变时不会引发事件。 –

+2

'ItemsSource =“{Binding NameList,Mode = TwoWay}”'没有意义。一个ItemsControl从不改变它的'ItemsSource'属性,所以设置'Mode = TwoWay'永远不会有任何效果。除此之外,'MyDataContext.OnPropertyChanged(“NameList”);'因为NameList实例没有改变而被忽略。所以使用ObservableCollection。 – Clemens

+0

先生们 - 谢谢!事实上,我的主要错误是不使用ObservableCollection。我还删除了NameList绑定中的'Mode = TwoWay',正如你所提到的那样,Clemens - 它确实没有任何作用(并且没有显式设置TwoWay模式而完美工作)。感谢您指出了这一点。 Ed,我将在MainWindow中尝试创建视图模型。我现在的代码更多的是对自己的可行性检查。 (和我有限的WPF知识)导致了不寻常的结构。我会纠正这一点。 – Woelund

如果绑定集合未实现INotifyCollectionChanged(例如ObservableCollection<T>),则在尝试更新视图时会出现不一致或不存在的行为。我注意到,在将检查状态切换为真后,当我们滑动鼠标的滚轮时,列表确实会更新。另外,正如@Clemens所说,你的ItemsSource绑定应该是Mode=TwoWay,因为这是唯一合理的模式。

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

至于睡觉之间的来回切换IsChecked,我的猜测是,Thread.Sleep是在UI线程上发生的(因而绑起来),因此你的死区时间,其中PropertyChanged是无用的6秒。我可以用下面来解决这个(假设正确的集合类型正在使用):

private async void Toggle() 
{ 
    MyDataContext.IsChecked = true; 
    await Task.Delay(3000); 
    MyDataContext.IsChecked = false; 
    await Task.Delay(3000); 
    MyDataContext.IsChecked = true; 
} 

,并在App构造年底Toggle()通话。这不幸的是,应用程序试图从不工作的不同线程修改集合。然后,您可以解决有喜欢的事,可笑:

 ... 
     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; }); 
    } 

但是这只是强制执行程序的贫困人口结构。 编辑:我忘了提及使用异步/等待有释放UI线程的额外好处;它不再在检查状态之间锁定整个窗口。

我建议你将你的代码分离到适当的文件中,然后将逻辑分离到适当的位置。您的HandleDataContextPropertyChange可能发生在设置者IsChecked的减去通知呼叫。

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

+0

感谢布兰登 - 像魅力一样工作!非常感谢你的努力。我现在也看到,移动鼠标滚轮确实更新了列表...有趣的。当然,我的主要错误是不使用ObservableCollection。我也曾尝试在App构造函数的最后创建一个新线程来切换IsChecked标志,但遇到了您提到的同一问题(集合不能从不同线程修改)。通过调度员切换(我每天都在学习:-))很好地工作。 我的代码是可行性检查 - 将按照您的建议改进结构。 – Woelund