WPF Binding to a property of a custom control - wpf

I've created a custom control, means a class deriving from control associating a default lookless theme defined via Themes/Generic.xaml. So far so good.
Now, I want to use the control like any other of the main WPF-controls (textbox, listbox, label, textblock, ...) and bind to the defined properties.
The custom control defines a property called Value, that I like to set a Binding to. But nothing ever is written to the bound property in the DataContext.
Well, here's what I've got so far:
In Custom Control class, there is as follows:
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(string), typeof(MyClass),
new FrameworkPropertyMetadata("", new PropertyChangedCallback(onValuePropertyChangedCallback)));
private static void onValuePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
MyClass myClass = (MyClass)sender;
myClass.Value = (string)args.NewValue;
}
public string Value
{
get { return (string) GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
When I use the control, it's like that
<local:MyClass MyValue="{Binding CurrentValue}" ... />
The CurrentValue-property of the DataContext is never affected, never changes it's value.
What am I doing wrong?

The Binding to should be two-way in order to update the source property:
<local:MyClass Value="{Binding CurrentValue, Mode=TwoWay}" ... />
If you want this to be the default binding mode, you could set an appropriate flag when you register your property:
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(string), typeof(MyClass),
new FrameworkPropertyMetadata(
"", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
Note that your onValuePropertyChangedCallback was entirely redundant. You don't need to set the property again, when its value has just changed.

Related

Two-Way Dependency Property Binding with User Control

I have a simple User Control:
public partial class FreqSpreadUC : UserControl
{
public FreqSpreadUC()
{
InitializeComponent();
FreqOne = "trying to pass this on to the parent";
}
public String FreqOne
{
get { return (String)GetValue(FreqOneProperty); }
set { SetValue(FreqOneProperty, value); }
}
// Using a DependencyProperty as the backing store for FreqOne. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FreqOneProperty =
DependencyProperty.Register("FreqOne", typeof(String), typeof(FreqSpreadUC));
}
Then I am consuming it as follows:
<spread:FreqSpreadUC FreqOne="{Binding TestFreqOne}" />
When the underlying user control changes its FreqOne dependency property, I want TestFreqOne to change as well. I am trying to create a user control that will "output" a result to the parent control. How would I go about doing this? Thank you.
Your question title already suggests it. You need a two-way binding:
<spread:FreqSpreadUC FreqOne="{Binding TestFreqOne, Mode=TwoWay}" />
Alternatively you might register property metadata with your dependency property that makes it bind two-way by default:
public static readonly DependencyProperty FreqOneProperty =
DependencyProperty.Register(
"FreqOne", typeof(String), typeof(FreqSpreadUC)
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Custom Dependency Property only fire once

I created a dependency property on a custom user control and problem is that the "OnPeriodTypeChangedHandler" Property Change Call Back only fire once when control is created for the first time, subsequently if I try to call the property via Binding it doesn't fire at all. Any ideas?
#region PeriodType
public static readonly DependencyProperty PeriodTypeProperty =
DependencyProperty.Register(
"PeriodType",
typeof(PeriodTypeEnum),
typeof(Period),
new FrameworkPropertyMetadata(
PeriodTypeEnum.None,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnPeriodTypeChangedHandler)
)
);
public static void OnPeriodTypeChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Get instance of current control from sender
// and property value from e.NewValue
// Set public property on TaregtCatalogControl, e.g.
((Period)sender).PeriodType = (PeriodTypeEnum)e.NewValue;
if ((PeriodTypeEnum)e.NewValue == PeriodTypeEnum.Month)
((Period)sender).Periods = ((Period)sender).GetMonthlyPeriods();
if ((PeriodTypeEnum)e.NewValue == PeriodTypeEnum.Quarter)
((Period)sender).Periods = ((Period)sender).GetQuarterlyPeriods();
}
public PeriodTypeEnum PeriodType
{
get
{
return (PeriodTypeEnum)GetValue(PeriodTypeProperty);
}
set
{
SetValue(PeriodTypeProperty, value);
}
}
#endregion
In case you want your DP to bind TwoWay by default, you can specify it at time of DP registration using FrameworkPropertyMetadataOptions.BindsTwoWayByDefault. This way you don't have to set mode to TwoWay at time of binding.
public static readonly DependencyProperty PeriodTypeProperty =
DependencyProperty.Register(
"PeriodType",
typeof(string),
typeof(MyTextBox),
new FrameworkPropertyMetadata(
PeriodTypeEnum.None,
FrameworkPropertyMetadataOptions.AffectsRender
| FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, <- HERE
new PropertyChangedCallback(OnPeriodTypeChangedHandler)
)
);
I think I found the solution in this particular instance by changing the Mode property to "TwoWay" in ViewModel for its data binding property.

WPF Binding to a custom property in a custom control

I have a custom text box defined as follows:
public class CustomTextBox : TextBox
{
public static DependencyProperty CustomTextProperty =
DependencyProperty.Register("CustomText", typeof(string),
typeof(CustomTextBox));
static CustomTextBox()
{
TextProperty.OverrideMetadata(typeof(SMSTextBox),
new FrameworkPropertyMetadata(string.Empty,
FrameworkPropertyMetadataOptions.Journal |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(CustomTextBox_OnTextPropertyChanged));
}
public string CustomText
{
get { return (string)GetValue(CustomTextProperty); }
set { SetValue(CustomTextProperty, value); }
}
private static void CustomTextBox_OnTextPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
CustomTextBox customTextBox = d as CustomTextBox;
customTextBox.SetValue(CustomTextProperty, e.NewValue);
}
}
I'm binding the Custom Text property in the XAML -
<local:CustomTextBox CustomText="{Binding ViewModelProperty}" />
The problem I'm facing is that when I enter anything in the CustomTextBox, the changes are not reflected in the ViewModelProperty i.e. the ViewModelProperty is not getting updated. The CustomTextProperty is getting updated but I suppose I need to do something extra to make the binding work as well.
What am I not doing? I would appreciate any help regarding this.
Thank you
I guess the binding needs to be two-way.
<local:CustomTextBox
CustomText="{Binding ViewModelProperty, Mode=TwoWay}" />
You wouldn't need to specify the Mode if you made the CustomText property bind two-way by default:
public static readonly DependencyProperty CustomTextProperty =
DependencyProperty.Register(
"CustomText", typeof(string), typeof(CustomTextBox),
new FrameworkPropertyMetadata(
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
You may also have to define a PropertyChangedCallback for the CustomText property that updates the Text property (i.e. the other direction of what you have implemented now). Otherwise the TextBox won't display anything that is initially contained in the ViewModel property and of course woudln't be updated when the ViewModel property changes.

DependencyProperty not triggered

I defined property in my usercontrol like this:
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
SetValue(IsSelectedProperty, value);
StackPanelDetails.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
}
}
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof (bool), typeof (ucMyControl));
But when I set its property in xaml, it want trigger it (set is not called).
<DataTemplate><local:ucTopicItem IsSelected="False" /></DataTemplate>
What could be the problem?
The setter of your dependency property will not be called when the property is set in XAML. WPF will instead call the SetValue method directly.
See MSDN XAML Loading and Dependency Properties for an explanation why the setter is not called.
You would have to register a PropertyChangedCallback with property metadata.
You should use a property changed handler in your dependency property directly. This way you ensure that it gets called when set in XAML:
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(ucMyControl), new PropertyMetadata(false, OnIsSelectedChanged));
private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Implement change logic
}

How do I pass changes from source in a TwoWay binding?

I have created a custom TextBox control (but not derived from TextBox) that contains a Dependency Property "Text".
I have added an instance of this and bound it to a property on my view model using a TwoWay binding.
From within my custom TextBox control, how do I update the Text property in such a way that the change is propagated to the property on the view model?
If I set the "Text" property on my custom control, that replaces the binding leaving the property on the view model as null.
I would have thought this would be simple but I can't see how to do it (the standard TextBox control must do it!)
Cheers
Edit:
Custom Control:
public class SampleCustomControl : CustomControl
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(SampleCustomControl), new PropertyMetadata(null));
public void Update()
{
// This replaces my binding, I want it to pass the new value
// through to the "SomeProperty" two way binding.
Text = "some value";
}
}
Usage:
<Controls:SampleCustomControl Text="{Binding SomeProperty, Mode=TwoWay}" />
You need to add a Property Changed callback in the metadata of your dependency property.
This callback will be fired when the Text property changes (from either side). You can use the value passed in from this to update your custom UI that you've built to display the text.
Update:
Responding to your comment about what this is about. Since your example code is too vague to test, here is what I used to test your problem.
public class TestControl : ContentControl
{
private TextBlock _tb;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_tb = new TextBlock();
_tb.Text = Text;
this.Content = _tb;
_tb.MouseLeftButtonDown += new MouseButtonEventHandler(_tb_MouseLeftButtonDown);
}
void _tb_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Update();
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TestControl), new PropertyMetadata(string.Empty, OnTextChanged));
public void Update()
{
// This replaces my binding, I want it to pass the new value
// through to the "SomeProperty" two way binding.
Text = "some value";
}
public static void OnTextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
((TestControl)sender).UpdateText((string)e.NewValue);
}
protected void UpdateText(string text)
{
if (_tb != null) _tb.Text = text;
}
}
I then bound the Text property on my control to the view model using a two way binding. When I click the text in the view both the view and the viewmodel get updated with the new text "some value". If I update the value in the viewmodel (and raise the property changed event) the value gets updated in the view and the control so the binding is still valid.
There must be some other missing pieces in your example.
As long as your binding property is set to TwoWay and you have exposed the getter and the setter, than the text you enter in the TextBox is sent to the ViewModel. I believe the actual send occurs when you lose focus of that control however, i believe.

Resources