Reputation: 651
I am working on a custom control. I want it to support data-binding, so it is as easy to maintain as the built-in controls are.
Here is what I got so far:
the window Window.xaml code that contains my control:
<custom:Matrix x:Name="customMatrix" DockPanel.Dock="Top" Title="{Binding Title}"/>
... and its Window.xaml.cs code-behind (just a fragment of a viewmodel)
public ViewModel VM { get; private set; }
private ProblemManager()
{
VM = new ViewModel();
InitializeComponent();
DataContext = VM;
VM.Title = "a title";
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
ItemsList = new ObservableCollection<List<int>>();
}
public ObservableCollection<List<int>> ItemsList { get; set; }
protected string title;
public string Title
{
get { return title; }
set
{
if (title != value)
{
title = value;
NotifyPropertyChanged("Title");
}
}
}
protected void NotifyPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
my control Matrix.xaml
// ...
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Title}"/>
// ...
and the control's Matrix.xaml.cs code-behind
public partial class Matrix : UserControl
{
public Matrix()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(Matrix), new PropertyMetadata("", TitleChanged));
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
private void TitleChanged(string title)
{
Title = title;
}
private static void TitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Matrix)d).TitleChanged((string)e.NewValue);
}
}
And the thing is that if not binding in my window xaml to the Matrix.Title
property i.e. giving that value statically in xaml like Title="some title"
the binding inside my control code works (I can even do customMatrix.Title += "1";
in my window and it works). However, when binding to the Matrix.Title
property (like I did in the code provided), it does not work and I got a blank title in my view. Just to state it clearly, binding to default controls in the window works.
How to make the Matrix.Title
property bindable?
Upvotes: 1
Views: 1417
Reputation: 16148
The TextBlock binding on your Matrix control is wrong.
You've set up a dependency property and then the code in your Window.xaml is binding that property to Title in your Window class. That part is all fine, although you don't need the TitleChanged function (the binding gets done automatically so you don't need to do it manually yourself). The problem is that the TextBlock.Text property in your UserControl is declared like this:
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Title}"/>
So it's trying to bind to the UserControl's current DataContext, and if you haven't set that (which you apparently haven't) then it inherits it from MainWindow which may or may not be what you want it to be. To fix this you need to bind the TextBlock to the UserControl's property, not the DataContext property, and you do that with FindAncester:
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type custom:Matrix}}, Path=Title}" />
UPDATED: Another way to do this is to give the UserControl in your Matrix.xaml file a name and bind to it with ElementName:
<UserControl x:Class="YourProjectNameHere.Matrix"
xmlns:custom="clr-namespace:Wpftest2"
... more namespaces here
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
Name="thisControl">
<Grid>
<TextBlock Name="theTextBlock" Grid.Row="0" Grid.Column="1" Text="{Binding ElementName=thisControl, Path=Title}"/>
</Grid>
Upvotes: 2