TemplateBinding not working for textbox text - c#

I have a custom control called EnhancedTextBox which is a UserControl that has a TextBox and a Button. To the consumer I want it to mostly look like a TextBox, so I did the following:
<UserControl.Template>
<ControlTemplate TargetType="textBoxes:EnhancedTextBox">
...
<TextBox Text="{TemplateBinding Text}"...
And in EnhancedTextBox I have
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof (String), typeof (EnhancedTextBox));
public String Text
{
get { return (String) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
Yet, when I use it as the following:
<EnhancedTextBox Text="{Binding MyText, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}}" />
Then, MyText is never updated, as well as I inspect EnhancedTextBox.Text and it is null. What am I missing? I have been staring at this for a bit and can't figure out what is wrong. I even thought it might be the fact that I was using the same name, so create a property called Text1 which did not work....
Also of note, if I use a regular TextBox, then this all works. So, I am fairly certain the problem is with the EnhancedTextBox itself

I figured it out after reading this MSDN about TemplateBinding. Specifically,
A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}.
So, I decided to do this explicitly...which would allow me to set the UpdateSourceTrigger (still not sure why it doesn't default to PropertyChanged)
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, UpdateSourceTrigger=PropertyChanged}"....
And, now it is working. TemplateBinding does not even expose these properties....again, not sure why

You are missing the CallBack when you register the property.
Here's a sample code.
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
public void IsSelectedChangedCallback()
{
//actions when property changed
}
private static void OnSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
userControl.IsSelectedChangedCallback();
}
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register("IsSelected", typeof(bool), typeof(MyUserControl), new PropertyMetadata(new PropertyChangedCallback(OnSelectedChanged)));

Related

WPF custom ContentPresenter does not bind properties

I have a custom class derived from ContentPresenter which has a DepenedencyProperty. However, when I attempt to bind it, nothing happens (Source's getter is not called, content is not updated, no errors are logged in Trace):
public class IsReadOnlyCellPresenter : ContentPresenter
{
[Bindable(true)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(IsReadOnlyCellPresenter), new FrameworkPropertyMetadata(null));
}
xaml:
<local:IsReadOnlyCellPresenter Text="{Binding Path=MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<local:IsReadOnlyCellPresenter.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Text, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</DataTemplate>
</local:IsReadOnlyCellPresenter.ContentTemplate>
</local:IsReadOnlyCellPresenter>
If I use static value (e.g. Text="Foo") it works as expected. If I derive from ContentControl (class IsReadOnlyCellPresenter : ContentControl), the Text binding works but not Mode=TemplatedParent which points to nowhere.
What is the issue with ContentPresenter here? Can ContentPresenter be derived from?

Wpf usercontrol with embedded button : change button's content

I have a simple usercontrol with a button in it which i modified.
When I add this usercontrol to my mainwindow, I can only access the usercontrol's properties. How can I access the button content ? Ideally I'd like to have a custom property let's say "TheText" and I changed it like that
<local:MyButtonControl TheText="My text here will be the button content">
This is what I have in the usercontrol "MyButtonControl"
public object TheText
{
get => (object)GetValue(_text);
set => SetValue(_text, value);
}
public static readonly DependencyProperty _text =
DependencyProperty.Register("Text", typeof(object), typeof(MyButton), new UIPropertyMetadata(null));
But what Am I supposed to put for binding ? Can't figure it out. Here's the concerned button.
<Button x:Name="button" Content="{Binding ??? }" Style="{StaticResource RoundedButton}"/>
The Binding should look like this:
<Button Content="{Binding Text,
RelativeSource={RelativeSource AncestorType=UserControl}}" .../>
Note that a correct dependency property declaration would have to use the same name for both the dependency property and the CLR wrapper. There is also a convention to name the identifier field as <PropertyName>Property.
public object Text
{
get => (object)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(object), typeof(MyButton));
You should certainly also use string as type of a property that is called Text. Or you call the property ButtonContent or something like that.

WPF Dependency Control Updates

I have a user control called TitleBar which contains this DependencyProperty:
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(TitleBar), new PropertyMetadata(null));
public string Title
{
get
{
return (string)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
}
}
The xaml for the user control looks like:
<Label Content="{Binding Title, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
And my xaml of the View looks like:
<Components:TitleBar x:Name="customTitleBar" Title="Test" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3"/>
When I set Title="Test" on the View that holds the TitleBar usercontrol, the changes do not show up in the designer, nor at runtime. How can I fix this?
Figured it out -- I was incorrectly handling the OnPropertyChanged event due to being confused about the parameters.
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(TitleBar), new PropertyMetadata(null
() => { (sender, e) ... }))
Worked after casting the parameters and calling view updates directly on them.
I don't have full code but my guess is that the problem is in binding context for Label, which by default will be DataContext and not UserControl.
<Label Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=Title}"
Changing binding source to be your UserControl should help and you shouldn't have to handle property updates from callback

Binding from DependencyProperty of a UserControl not working

I have a UserControl with one DependencyProperty which sets in codebehind (I guess this may be a source of my problem, but still don't know what to do):
UserControl
public partial class MyControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyControl),
new FrameworkPropertyMetadata("",FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); InvokePropertyChanged(new PropertyChangedEventArgs("Text"));}
}
public static string GetText(DependencyObject obj)
{
return (string)obj.GetValue(TextProperty);
}
public static void SetText(DependencyObject obj, string value)
{
obj.SetValue(TextProperty, value);
}
private void ChangeText()
{
Text="some value";
}
}
In my View.xaml I use this control like this:
<MyControl Text="{Binding Text, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
And the Text property in my ViewModel:
private string _text;
public string Text
{
get { return _text; }
set { _text= value; InvokePropertyChanged(new PropertyChangedEventArgs("Text"));}
}
The problem:
Text property in the ViewModel never gets updated; when use binding with a regular control like TextBox, all works perfect; if I set Text in XAML, Text propery of UserControl updates.
What I did wrong?
UPDATE
My issue was that I have set DataContext explicitly on MyControl.
Issue is in your Binding:
Text="{Binding Text, RelativeSource={RelativeSource Self},
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Text property is in your ViewModel but you are referring to itself by using RealtiveSource to point back to self. So, it's binding Text DP with itself.
If you have set DataContext of your control, it will automatically inherit DataContext from parent. So, you don't need RelativeSource at all.
It simply should be:
Text="{Binding Text}"
Few points more (but not related to your issue):
Since you target to use this property from within control, so go for normal DP instead of attached property.
Since at time of registration, you have set it to bind TwoWay by default. No need to explicitly do that at time of binding.
Remove InvokePropertyChanged call from your DP wrapper setter. Setter won't be called from XAML and also DP is already PropertyChanged aware.
UPDATE
In case DataContext of MyControl is set to instance of another class, above approach will search for Text property in MyControl DataContext.
You can pass DataContext of parent control (StackPanel in your case) like this:
Text="{Binding DataContext.Text, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=StackPanel}}"
You have registered your property as attached, yet you are also using it as a regular DependencyProperty. I think that the xaml parser gets confused. Decide which one you want to use.

Issue with binding custom DependencyProperty to ListView.SelectedItem

I'm having an issue with a binding that I'm trying to implement. It will update the DP once, but after that, it's never updated again.
In XAML I have two controls binding to a listview.selected item.
<controls:MapControl DataContext="{Binding ElementName=availableMapsListView, Path=SelectedItem}" MapData="{Binding .}">
and
<TextBlock DataContext="{Binding ElementName=availableMapsListView, Path=SelectedItem}" Text="{Binding Name}" />
The textblock update as expected with each change of the listview's selected item.
My custom control creates the dependency property like so:
public class MapControl : UserControl
{
public MapData MapData
{
get { return (MapData)GetValue(MapDataProperty); }
set { SetValue(MapDataProperty, value); }
}
public static readonly DependencyProperty MapDataProperty =
DependencyProperty.Register("MapData", typeof(MapData), typeof(MapControl),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnMapDataPropertyChanged),
new CoerceValueCallback(OnMapCoerceValue)
)
);
private static void OnMapDataPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null)
{
((MapControl)source).MapData = (MapData)e.NewValue;
}
}
private static object OnMapCoerceValue(DependencyObject dpo, Object obj)
{
return obj;
}
...
}
I'm pretty much at my wits end and not sure what I should do from here. Any help is greatly appreciated.
Not sure exactly what you're trying to achieve or why your code appears so convoluted. If you explain more someone may be able to provide you with a much simpler solution.
That said, by the sounds of it the problem is simply that you're overwriting the binding with a local value. This looks like the culprit:
((MapControl)source).MapData = (MapData)e.NewValue;
When you do this, the MapControl.MapData property will no longer be bound to '.' Instead, it will take on whatever value you've assigned. So your MapControl.DataContext property is likely perfectly correct, but it's not being transferred to the MapData property because you've destroyed the binding.
I had the same error last week. My solution was simple : When you explicitly define a DependencyProperty you must also explicitly define the mode to TwoWay.
<TextBlock DataContext="{Binding ElementName=availableMapsListView,
Path=SelectedItem}"
Text="{Binding Name, Mode=TwoWay}" />

Categories

Resources