Edit: a sample project can be found here.
I am using a ListBox
inside my main window, which I later bind to an ObservableCollection
. I use both a TextBlock
and a custom control which I bind to the same property of the collection. My problem is that the TextBlock
gets properly updated, whereas the custom control doesn’t (it gets default constructed but its Text
property is never updated by the binding).
<ListBox Name="MyCustomItemList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ItemText}"/>
<local:MyCustomBlock Text="{Binding ItemText}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I implemented MyCustomBlock
as a child of System.Windows.Controls.Canvas
with a Text
dependency property:
public class MyCustomBlock : Canvas
{
public MyCustomBlock() => Text = "<default>";
public MyCustomBlock(string text) => Text = text;
private static void TextChangedCallback(DependencyObject o,
DependencyPropertyChangedEventArgs e)
{
...
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text), typeof(string), typeof(MyCustomBlock),
new FrameworkPropertyMetadata("", TextChangedCallback));
}
Finally, this is the data I bind to the ListBox
in the MainWindow
constructor:
public class MyCustomItem
{
public MyCustomItem(string text) => ItemText = text;
public string ItemText { get; set; }
}
public MainWindow()
{
InitializeComponent();
var list = new ObservableCollection<MyCustomItem>();
list.Add(new MyCustomItem("Hello"));
list.Add(new MyCustomItem("World"));
MyCustomItemList.ItemsSource = list;
}
Did I forget something in my setup? How come TextBlock.Text
is seemingly properly updated but not MyCustomBlock.Text
?
Dependency properties can get their value from several sources and so WPF employs a precedence system to determine which value applies. "Local" values (provided using SetValue or SetBinding) will override anything provided by the creating template.
In your case, your setting a "local" value in the constructor (presumably intending it to behave as a default value). A better way to set a default value is by providing it in the PropertyMetadata.
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text), typeof(string), typeof(MyCustomBlock),
new FrameworkPropertyMetadata("<default>", TextChangedCallback));