I am trying to make a sample WPF user control (maybe it would be better to say “developer control”) with bindable properties. My code consists of these files:
----- MainWindow.xaml -----
<Window x:Class="Test_Binding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:testBinding="clr-namespace:Test_Binding"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<testBinding:MyLabelledTextBox x:Name="MLTB" LabelText="My custom control: MyLabelledTextBox" Text="{Binding StringData, Mode=OneWay}" />
</StackPanel>
</Window>
----- MainWindow.xaml.cs -----
using System.Windows;
namespace Test_Binding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = new MyDataObject();
this.InitializeComponent();
}
}
}
----- MyDataObject.cs -----
using System.Runtime.CompilerServices; // CallerMemberName
using System.ComponentModel; // INotifyPropertyChanged
namespace Test_Binding
{
public class MyDataObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string stringData;
public string StringData
{
get { return this.stringData; }
set
{
if (value != this.stringData)
{
this.stringData = value;
this.OnPropertyChanged();
}
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public MyDataObject()
{
System.Timers.Timer t = new System.Timers.Timer();
t.Interval = 10000;
t.Elapsed += t_Elapsed;
t.Start();
}
private void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.StringData = ((this.StringData ?? string.Empty).Length >= 4 ? string.Empty : this.StringData + "*");
}
}
}
----- MyLabelledTextBox.xaml -----
<UserControl x:Class="Test_Binding.MyLabelledTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Background="Yellow">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Label x:Name="MLTBLabel" Grid.Row="0" Grid.Column="0" />
<TextBox x:Name="MLTBTextBox" Grid.Row="0" Grid.Column="1" Background="Yellow" Text="{Binding Text, Mode=TwoWay}" />
</Grid>
</StackPanel>
</UserControl>
----- MyLabelledTextBox.xaml.cs -----
using System.Windows;
using System.Windows.Controls;
namespace Test_Binding
{
/// <summary>
/// Interaction logic for MyLabelledTextBox.xaml
/// </summary>
public partial class MyLabelledTextBox : UserControl
{
public static readonly DependencyProperty LabelTextProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(MyLabelledTextBox),
new PropertyMetadata(string.Empty, MyLabelledTextBox.LabelTextPropertyChanged));
public string LabelText
{
get { return (string)this.GetValue(MyLabelledTextBox.LabelTextProperty); }
set { this.SetValue(MyLabelledTextBox.LabelTextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyLabelledTextBox),
new PropertyMetadata(string.Empty, MyLabelledTextBox.TextPropertyChanged));
public string Text
{
get { return (string)this.GetValue(MyLabelledTextBox.TextProperty); }
set { this.SetValue(MyLabelledTextBox.TextProperty, value); }
}
public MyLabelledTextBox()
{
this.InitializeComponent();
this.MLTBLabel.DataContext = this;
this.MLTBTextBox.DataContext = this;
this.MLTBTextBox.TextChanged += new TextChangedEventHandler(this.MLTBTextBox_TextChanged);
}
private void MLTBTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
this.Text = this.MLTBTextBox.Text; // transfer changes from TextBox to bindable property (bindable property change notification will be fired)
}
private static void LabelTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyLabelledTextBox)d).MLTBLabel.Content = (string)e.NewValue; // transfer changes from bindable property to Label
}
private static void TextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyLabelledTextBox)d).MLTBTextBox.Text = (string)e.NewValue; // transfer changes from bindable property to TextBox
}
}
}
There is an instance of the “MyDataObject” class with the property “StringData”, which is modified periodically using a timer. My user control is bound to its property “StringData”. If the binding in the “MainWindow.xaml” file is set as “TwoWay”, the user control keeps being updated, but if I use the “OneWay” binding, then the user control is updated a single time and then the “PropertyChanged” event of the instance of the “MyDataObject” class does not fire again, because suddenly it has no subscriber.
Why does the “OneWay” binding stop working after being invoked once ? What code change would allow both the “TwoWay” and “OneWay” bindings to keep working ?
Bindingproblems, you should see what errors are displayed in the Output Window in Visual Studio... they'll tell you what the problem is. - Sheridan