WPF双向绑定无法在CheckBox和列表上工作
即使在审查了大量解决方案建议后,我仍无法在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一个线索:复选框路径以及列表框绑定
NameList
与Cannot 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
:
一旦应用程序构造器h作为成品,窗口更新一次,但从来没有一次后,不管有多少字符串被添加到NameList
:
任何想法,为什么我的双向绑定只在一个方向的工作?
如果绑定集合未实现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/
感谢布兰登 - 像魅力一样工作!非常感谢你的努力。我现在也看到,移动鼠标滚轮确实更新了列表...有趣的。当然,我的主要错误是不使用ObservableCollection。我也曾尝试在App构造函数的最后创建一个新线程来切换IsChecked标志,但遇到了您提到的同一问题(集合不能从不同线程修改)。通过调度员切换(我每天都在学习:-))很好地工作。 我的代码是可行性检查 - 将按照您的建议改进结构。 – Woelund
我建议你删除所有奇怪的'MyDataContext'东西在'App'和刚刚创建的MainWindow的构造函数什么的视图模型。此外,对于将绑定到WPF中的控件的集合,始终使用'ObservableCollection'。不要使用列表。它的内容改变时不会引发事件。 –
'ItemsSource =“{Binding NameList,Mode = TwoWay}”'没有意义。一个ItemsControl从不改变它的'ItemsSource'属性,所以设置'Mode = TwoWay'永远不会有任何效果。除此之外,'MyDataContext.OnPropertyChanged(“NameList”);'因为NameList实例没有改变而被忽略。所以使用ObservableCollection。 – Clemens
先生们 - 谢谢!事实上,我的主要错误是不使用ObservableCollection。我还删除了NameList绑定中的'Mode = TwoWay',正如你所提到的那样,Clemens - 它确实没有任何作用(并且没有显式设置TwoWay模式而完美工作)。感谢您指出了这一点。 Ed,我将在MainWindow中尝试创建视图模型。我现在的代码更多的是对自己的可行性检查。 (和我有限的WPF知识)导致了不寻常的结构。我会纠正这一点。 – Woelund