Wpf usercontrol with embedded button : change button's content - c#

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.

Related

Binding to property of UserControl doesn't work

I got a custom TextBox which I plan to include in another UserControl, however when setting up the Binding for it, it simply just doesn't bind.
I simplified the code for clarity.
My custom TextBox:
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
partial class CustomTextBox : UserControl
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomTextBox),
new PropertyMetadata(String.Empty));
}
This binding works as expected. When using CustomTextBox in another UserControl or Window, I can access the property just as expected.
The following code blocks describe the UserControl that uses CustomTextBox and the corresponding ViewModel with the property I want to bind Text to.
<UserControl>
<UserControl.DataContext>
<vm:MyViewModel />
</UserControl.DataContext>
<local:CustomTextBox Text="{Binding FooBar, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
public class MyViewModel : INotifyPropertyChanged
{
private string _fooBar;
public string FooBar
{
get { return _fooBar = (_fooBar ?? ""); }
set
{
_fooBar = value; OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
My problem occurs exactly when I want to bind the Text property to a ViewModel in another UserControl, it just doesn't work. In this case I tried to bind the Text property to the FooBar property on the MyViewModel class, however changes to the Text property do not get reflected on the FooBar property and vice-versa. However when I hover over the binding in the XAML view, it shows the type of the property, so I don't exactly see what's wrong here.
My best guess is that it has to do with two bindings accessing the same property.
modify DP registration to include FrameworkPropertyMetadataOptions.BindsTwoWayByDefault option
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomTextBox),
new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

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.

TemplateBinding not working for textbox text

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)));

sync templatedParent property with control template item property

Using WPF I'm trying to bind Text property of TextBox to Text property of custom control. Could anyone tell me how to achieve that?
Firstly I have template for control which contains TextBox:
<TextBox x:Name="PART_InputTextBox"
Text="[???]">
</TextBox>
My custom control which uses this template contains DependencyProperty "Text"
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(AutocompleteSelector), new UIPropertyMetadata(null));
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
My question is: What binding I should use (instead of [???]) in Text property of TextBox, to get two way sync of these properties? I mean that, when CustomControl.Text would be changed I want to change TextBox.Text too and vice-versa. I've tried already
{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}
but it doesn't work.

Categories

Resources