WPF Databinding Question
I've written a very short app in which I'm trying to achieve the following : have the CheckBox
change its state from code. I've wired up the INotifyPropertyChanged
interface and was expecting to see some results but apparently the app does nothing. Is there something wrong with the databinding?
Window1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
namespace WpfTEST
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window, INotifyPropertyChanged
{
public Window1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
this.PropertyChanged += new PropertyChangedEventHandler(Window1_PropertyChanged);
}
public bool Flag
{
get { return m_flag; }
set
{
m_flag = value;
OnPropertyChanged("Flag");
}
}
private bool m_flag = false;
void Window1_Loaded(object sender, RoutedEventArgs e)
{
this.m_cbox.DataContext = this;
for (int i = 0; i < 1000; i++)
{
开发者_如何学Go Flag = (i % 2 == 0);
Thread.Sleep(200);
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
void Window1_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
#endregion
}
}
Window1.xaml
<Window x:Class="WpfTEST.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" x:Name="window">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="0.5*" />
</Grid.RowDefinitions>
<CheckBox x:Name="m_cbox" Content="Let's see what happens" Grid.Row="2" Grid.Column="2" Grid.RowSpan="1" Grid.ColumnSpan="1" IsChecked="{Binding Path=Flag, ElementName=window, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Window>
The only real problem I see with your code is the sleep loop in Loaded. That causes the UI thread to go unresponsive for the duration of the loop, since you're putting the UI thread into a blocked state.
There's a better way to test if you can change the flag from code and have your binding fire. Add a button to the window, hook up a click handler to the button, and in that click handler, toggle Flag
-- I made those modifications to your original code (and removed the sleep loop), clicking on the button toggles the checkbox's state in the fashion you seem to desire.
<Window x:Class="WpfTEST.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" x:Name="window">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="0.5*" />
</Grid.RowDefinitions>
<!-- I added this button: -->
<Button Click="Button_Click" Grid.Row="0" Grid.Column="0"/>
<CheckBox x:Name="m_cbox" Content="Let's see what happens" Grid.Row="2" Grid.Column="2" Grid.RowSpan="1" Grid.ColumnSpan="1" IsChecked="{Binding Path=Flag, ElementName=window, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Window>
and in code behind:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
namespace WpfTEST {
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window, INotifyPropertyChanged {
public Window1() {
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
this.PropertyChanged += new PropertyChangedEventHandler(Window1_PropertyChanged);
}
public bool Flag {
get { return m_flag; }
set {
m_flag = value;
OnPropertyChanged("Flag");
}
}
private bool m_flag = false;
void Window1_Loaded( object sender, RoutedEventArgs e ) {
this.m_cbox.DataContext = this;
Flag = false;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged( string name ) {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
void Window1_PropertyChanged( object sender, PropertyChangedEventArgs e ) {
}
#endregion
private void Button_Click( object sender, RoutedEventArgs e ) {
Flag = !Flag;
}
}
}
Other than the above modifications to add the button and its click handler, and remove that sleep loop, I made no modifications to your original code (although you could streamline it a bit).
That will only work if your Flag property is a dependency property. To get this working in the simplest way i can think of i would do the following : change your binding to be :
IsChecked="{Binding Path=Flag,Mode=TwoWay}"
and in your constructor in your code behind do this :
DataContext = this;
or in XAML
DataContext="{Binding RelativeSource={RelativeSource self}}"
Note : Flag does not need to be a dependency property if you are setting the datacontext to be the window.
精彩评论