Why does my 2 way binding not work - wpf

I'm trying to bind 2 ways
In my ViewModel, I have
private Temporary _selectedCompany;
public Temporary SelectedCompany
{
get
{
return this._selectedCompany;
}
set
{
if (this._selectedCompany == value || value == null)
return;
this._selectedCompany = value;
this.SelectedCompany.CompanyName = "TestName";
base.OnPropertyChanged("SelectedCompany");
}
}
Temporary is actually a class similar to a class you'd do for a CompanyAddress (name, country, phone etc) and was created by EntityFramework.
In the corresponding View, the XAML is
<local:CompanyDetail CompanyName="{Binding SelectedCompany.CompanyName}"/>
In code behind of the custom control
// Dependency Property
public static readonly DependencyProperty CompanyNameProperty =
DependencyProperty.Register("CompanyName", typeof(string),
typeof(CompanyDetail), null);
// .NET Property wrapper
public string CompanyName
{
get { return (string)GetValue(CompanyNameProperty); }
set { SetValue(CompanyNameProperty, value); }
}
There is nothing in the ViewModel. There is the following XAML
<TextBox Text="{Binding CompanyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=UserControl, AncestorLevel=1, Mode=FindAncestor}}" />
So, when the controls are loaded and shown on screen, I see the value "TestName" in the TextBox but, if I change the value by typing and then click an OK button I can see the value has not been updated.
I'm assuming it has nothing to do with INotifyPropertyChanged because I think it's all a reference type any way?
What am I doing wrong?

The CompanyName binding must be made two-way, either by setting it explicitly
<local:CompanyDetail
CompanyName="{Binding SelectedCompany.CompanyName, Mode=TwoWay}"/>
or by declaring the dependency property to bind two-way by default
public static readonly DependencyProperty CompanyNameProperty =
DependencyProperty.Register(
"CompanyName", typeof(string), typeof(CompanyDetail),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Related

Binding to dependency property in usercontrol

I have a UserControl that contains a ListBox and I want to track the SelectedItems of that listbox.
The UserControl has a DP "SelectedItemsList" that is defined like this
public static DependencyProperty SelectedItemsListProperty = DependencyProperty.Register(
"SelectedItemsList",
typeof (IList),
typeof (MyListControl),
new FrameworkPropertyMetadata(null,
OnSelectedItemsChanged));
In the listbox' Item "SelectionChanged" event, I want to save the selected items to the DP. This is triggered whenever I change the selection in the listbox.
private void OnItemSelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedItemsList = this.myListBox.SelectedItems;
}
In my view that contains the "MyListControl" I create a binding to my viewmodel that want to use the selected items.
<controls:MyListControl
Source="{Binding SomeItemsList, UpdateSourceTrigger=PropertyChanged}"
SelectedItemsList="{Binding SelectedItems, UpdateSourceTrigger=PropertyChanged}"/>
My problem is, that the DP SelectedItemsList never gets updated. The PropertyChangeCallback "OnSelectedItemsChanged" of the DP is only triggered when I initially load the lists content. The value of the SelectedItemsList is always null.
I am aware that this question is similar to Dependency property callback does not work, but the answers posted there do not solve my problem.
What am I missing here?
Thanks,
Edit (2015-09-10):
Thank you all for your comments. I found a solution that fits my needs:
First of all I created a custom listbox control that provided the list of selected items in a dependency property (very similar to Select multiple items from a DataGrid in an MVVM WPF project).
public class CustomListBox : ListBox
{
public static readonly DependencyProperty SelectedItemsListProperty =
DependencyProperty.Register("SelectedItemsList",
typeof (IList),
typeof (CustomListBox),
new PropertyMetadata(null));
public CustomListBox()
{
SelectionChanged += OnSelectionChanged;
}
public IList SelectedItemsList
{
get { return (IList)GetValue(SelectedItemsListProperty); }
set { SetValue(SelectedItemsListProperty, value); }
}
void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedItemsList= new ArrayList(this.SelectedItems);
}
}
I am not happy yet with the "new ArrayList"-part, but if in my viewmodel's property setter I want to check for equality, SelectedItemsList can not be a reference of SelectedItems. The previous and the new value would always be the same.
Then I reduced the item selection parts of my UserControl "MyListControl" simply to the dependency property itself:
public static DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
"SelectedItems",
typeof (IList),
typeof (MyListControl),
new FrameworkPropertyMetadata(null));
public IList SelectedItems
{
get
{
return (IList)GetValue(SelectedItemsProperty);
}
set
{
SetValue(SelectedItemsProperty, value);
}
}
and modified the xaml of the MyListControl:
<controls:CustomListBox
SelectionMode="Extended"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:MyListControl}},
Path=Source, UpdateSourceTrigger=PropertyChanged}"
SelectedItemsList="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:MyListControl}},
Path=SelectedItems, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
>
The property in my ViewModel looks like
public IList SelectedObjects
{
get { return _selectedObjects; }
set { if (this._selectedObjects != value)
{
this._selectedObjects = value;
OnPropertyChanged(SelectedObjectsProperty);
}
}
}
It was important that the type of this property is IList, otherwise the value in the setter would always be null.
And in the view's xaml
<controls:MyListControl
Source="{Binding CurrentImageList, UpdateSourceTrigger=PropertyChanged}"
SelectedItems="{Binding SelectedObjects, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
I just had the same problem today, unfortunately, when you are assigning to SelectedItemsList a value, WPF seems to unbind it. To fix it, I update the value in the binded item. I know that it is not the best solution in the world but for me it works.
In this case the code would looked like this:
private void OnItemSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SetPropertyValue(
this.GetBindingExpression(SelectedItemsListProperty),
this.myListBox.SelectedItems);
}
private void SetPropertyValue(BindingExpression bindingExpression, object value)
{
string path = bindingExpression.ParentBinding.Path.Path;
var properties = new Queue<string>(
path.Split(
new[]
{
'.'
}).ToList());
this.SetPropertyValue(bindingExpression.DataItem, bindingExpression.DataItem.GetType(), properties, value);
}
private void SetPropertyValue(object destination, Type type, Queue<string> properties, object value)
{
PropertyInfo property = type.GetProperty(properties.Dequeue());
if (property != null && destination != null)
{
if (properties.Count > 0)
{
this.SetPropertyValue(property.GetValue(destination), property.PropertyType, properties, value);
}
else
{
property.SetValue(destination, value);
}
}
}
You need to bind your Listbox' SelectedItems to the DP SelectedItemsList to propagate the user selection to the DP. The binding you already have will then pass the changes on to the viewmodel, but I think you will need a binding mode 'twoway' instead of UpdateSourceTrigger.
And don't use the PropertyChangeCallback in your DP: Changing the SelectedItemsList if the SelectedItemsListProperty has changed makes no sense. (Usually the former is a wrapper property of the latter.)

Pass textbox from a page to Custom usercontrol using dependencyProperty

I have a Custom user control in a silver light Project.
I use it in other page and want to Pass textbox to Custom User control.
For this I create dependcy as below :
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("TextBoxControl", typeof(TextBox), typeof(SpellCheck), new PropertyMetadata(false));
public TextBox TextBoxControl
{
get { return (TextBox)GetValue(MyPropertyProperty); }
set
{
SetValue(MyPropertyProperty, value);
TextSpell = value;
}
}
Here TextSpell is a textbox.
And I use this property in a silver light page as below:
<TextBox x:Name="txtNote" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="400"/>
<myButton:SpellCheck x:Name="btnSpell" Grid.Row="3" TextBoxControl="txtNote" Grid.Column="1" Width="20" Height="20" Margin="403,0,0,0" HorizontalAlignment="Left"/>
But I give s me a error : "The Typeconvertor for Texbox dose not support converting from a string"
So How can I pass a text box in custom usercontrol.
Thanks,
Hitesh
You can not simply use the field name (x:Name) string of the TextBox as a value for your TextBoxControl property. Instead you may use an ElementName binding like this:
<myButton:SpellCheck TextBoxControl="{Binding ElementName=txtNote}" ... />
And there are more things wrong:
In the CLR wrappers of a dependency property, you should never call anything else than GetValue and SetValue. The explanation is given in the XAML Loading and Dependency Properties article on MSDN. Instead, you have to have a PropertyChangedCallback registered with the property metadata.
There is a naming convention for the static dependency property fields. They should be named like the property, with a trailing Property.
The default value has to match the property type. Your false value is not valid, and might be null instead. But as that is the default anyway, you should leave it out completely.
The declaration would now look like this:
public static readonly DependencyProperty TextBoxControlProperty =
DependencyProperty.Register(
"TextBoxControl", typeof(TextBox), typeof(SpellCheck),
new PropertyMetadata(TextBoxControlPropertyChanged));
public TextBox TextBoxControl
{
get { return (TextBox)GetValue(TextBoxControlProperty); }
set { SetValue(TextBoxControlProperty, value); }
}
private static void TextBoxControlPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var spellCheck = (SpellCheck)obj;
spellCheck.TextSpell = (TextBox)e.NewValue;
}

How to define a dependency property in a user control that just bubbles up a value from one of its children?

I am creating a ToggleSwitchItem user control, which contains a ToggleSwitch and a TextBlock. I have defined a dependency property called IsChecked which I just want to use to expose the IsChecked property of the private ToggleSwitch child.
But the data binding doesn't work... It just stays at the default value when loaded.
What am I missing?
Code:
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register(
"IsChecked",
typeof(bool),
typeof(ToggleSwitchItem),
new PropertyMetadata(new PropertyChangedCallback
(OnIsCheckedChanged)));
public bool IsChecked
{
get
{
return (bool)GetValue(IsCheckedProperty);
}
set
{
SetValue(IsCheckedProperty, value);
}
}
private static void OnIsCheckedChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
ToggleSwitchItem item = (ToggleSwitchItem)d;
bool newValue = (bool)e.NewValue;
item.m_switch.IsChecked = newValue;
}
for the data binding, I'm using to following:
<phone:PhoneApplicationPage.Resources>
<myApp:SharedPreferences x:Key="appSettings"/>
</phone:PhoneApplicationPage.Resources>
IsChecked="{Binding Source={StaticResource appSettings},
Path=SomeProperty, Mode=TwoWay}"
The SharedPreferences class is working fine, as it works without issue when bound to a plain vanilla ToggleSwitch's IsChecked property exactly as per above.
Thanks!
SOLUTION (with help from Anthony):
I bind my child toggle switch to my user control in the user control's constructor like so:
Binding binding = new Binding();
binding.Source = this;
binding.Path = new PropertyPath("IsChecked");
binding.Mode = BindingMode.TwoWay;
m_switch.SetBinding(ToggleSwitch.IsCheckedProperty, binding);
And I remove the callback as I no longer need it:
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register(
"IsChecked",
typeof(bool),
typeof(ToggleSwitchItem),
null);
public bool IsChecked
{
get
{
return (bool)GetValue(IsCheckedProperty);
}
set
{
SetValue(IsCheckedProperty, value);
}
}
I can't quite see what is actually wrong with the code you've show so far, except that you haven't show how the user toggling the switch would actually cause the IsChecked property to change.
Have you try using binding inside the UserControl:
<ToggleButton IsChecked="{Binding Parent.IsChecked, ElementName=LayoutRoot, Mode=TwoWay}" />
You do not need the OnPropertyChanged callback with this approach.
Check the DataContext of your control.Which means 2 things : All instances of your control must have right DataContext to work -ok-, and also you should not 'break' this DataContext when you define the control (at the Class level). If, when you define your control, you set the DataContext to 'this' / Me in code or to 'Self' in xaml, it nows refer only to itself and forget about the DataContext in which it is when you instanciate it in your application -- Binding fails.
If you have to refer to your control's properties within your control Xaml, use a binding with findAncestor / AncestorType = ToggleSwitchItem Or name your control in Xaml and bind with its ElementName.
Maybe this could help
public bool IsChecked
{
get { return GetValue(IsCheckedProperty) is bool ? (bool) GetValue(IsCheckedProperty) : false; }
set
{
SetValue(IsCheckedProperty, value);
}
}

Binding to a User Control's property, containing logic, with a property in View Model

I'm building an application using WPF and MVVM. I've come across a situation where I have a view containing a usercontrol (representing a Timer). This usercontrol has a property in it's code behind which performs some calculations before getting and setting data.
TimerControl.xaml.cs:
public DateTime? DateTimeValue
{
get
{
string hours = this.txtHours.Text;
string minutes = this.txtMinutes.Text;
string amPm = this.txtAmPm.Text;
if (!string.IsNullOrWhiteSpace(hours) && !string.IsNullOrWhiteSpace(minutes) && !string.IsNullOrWhiteSpace(amPm))
{
string value = string.Format("{0}:{1} {2}", this.txtHours.Text, this.txtMinutes.Text, this.txtAmPm.Text);
DateTime time = DateTime.Parse(value);
return time;
}
else
{
return null;
}
}
set
{
DateTime? time = value;
if (time.HasValue)
{
string timeString = time.Value.ToShortTimeString();
//9:54 AM
string[] values = timeString.Split(':', ' ');
if (values.Length == 3)
{
this.txtHours.Text = values[0];
this.txtMinutes.Text = values[1];
this.txtAmPm.Text = values[2];
}
}
}
}
Now I wanted to bind this property to a property present in view model of the view. Following is property in the VM:
public DateTime? StartTime
{
get
{
return _StartTime;
}
set
{
_StartTime = value;
RaisePropertyChanged("StartTime");
}
}
This is how I am performing binding in the xaml of View.
MyView.xaml:
<my:TimeControl Background="White" Grid.Column="1" Grid.Row="2" Margin="3" x:Name="StartTimeControl" DateTimeValue="{Binding StartTime}" Width="150" Height="26" HorizontalAlignment="Left">
But it is giving me an error that:
A 'Binding' cannot be set on the 'DateTimeValue' property of type 'TimeControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
I've been struggling for hours trying to figure out a way to make this binding work. I have even tried to make a dependency property in the TimeControl's code behind for the DateTimeValue property, which has resolved the above exception, but the binding still doesn't work. Whenever I access StartTime property in the VM's code behind, it is showing null. Although it should show a valid value by getting the DateTimeValue property.
Kindly suggest me a way to make this work. Thanks.
Your implementation of DateTimeValue property shown in this question is certainly wrong and leads to exception, because DateTimeValue should be dependency property.
But you mentioned that you have tried to use dependency property with no success. I suppose the reason is in collision of DataContexts and your XAML looks like this:
<UserControl x:Class="Test.SomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:Test"
Name="Root">
<WrapPanel>
<self:TimerControl Time="{Binding StartTime}"/>
</WrapPanel>
</UserControl>
This code doesn't work. Why? DataContext of TimerControl is inherited (or maybe you replace it at all), meanwhile when you address to StartTime you have in mind ViewModel as DataContext. So you should clearly point to correct DataContext:
<self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/>
===UPDATE===
The whole code of my Timer control (as you can see my Timer has textbox, when you input some text, textbox raises appropriate event, which we handle and set Time property):
public partial class Timer : UserControl
{
public Timer()
{
InitializeComponent();
}
public DateTime? Time
{
get
{
return (DateTime?)this.GetValue(Timer.TimeProperty);
}
set
{
this.SetValue(Timer.TimeProperty, value);
}
}
public static readonly DependencyProperty TimeProperty =
DependencyProperty.Register("Time",
typeof(DateTime?),
typeof(Timer),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (d, e) => { }));
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (DateTime.Now.Ticks % 2 == 0)
{
this.Time = DateTime.Now;
}
else
{
this.Time = null;
}
}
}
And XAML:
<UserControl x:Class="Test.Timer">
<Grid>
<TextBox TextChanged="TextBox_TextChanged"/>
</Grid>
</UserControl>
Usage of Time control in XAML:
<UserControl x:Class="Test.StartupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:Test"
Name="Root">
<WrapPanel>
<self:Timer Time="{Binding DataContext.StartTime, ElementName=Root}"/>
</WrapPanel>
</UserControl>
Code behind of StartupView:
public StartupView()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
Property in ViewModel remains the same. During debugging setter of StartTime property fires every time when I change text in Timer.
What excatly do you want to do?
You can't bind to a standard property. If you want to bind you should use a dependency property.
public DateTime? DateTimeValue
{
get { return (DateTime?)GetValue(DateTimeValueProperty); }
set { SetValue(DateTimeValueProperty, value); }
}
// Using a DependencyProperty as the backing store for DateTimeValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DateTimeValueProperty =
DependencyProperty.Register("DateTimeValue", typeof(DateTime?), typeof(TimeControl), new UIPropertyMetadata(null));
Inside the UserControl:
<TextBox Text="{Binding DateTimeValue,RelativeSource={RelativeSource AncestorLevel=1, Mode=FindAncestor,AncestorType=UserControl}, Converter=...}" />
To bind directly to a DateTimeValue is not possible because there is no converter available for string->DateTime so you have to write an IValueConverter and specify this in your binding.
From outside of course you should be able to bind the value directly.

WPF: DependencyProperty refusing to work

I'm trying to register 3 dependency properties on a Window to control it's formatting. I've looked over and over the code but I must be missing something.
public static readonly DependencyProperty TextColorProperty = DependencyProperty.Register("TextColor", typeof(Color), typeof(WinStickyFingers), new PropertyMetadata(Colors.White));
public Color TextColor {
get { return (Color)base.GetValue(TextColorProperty); }
set { base.SetValue(TextColorProperty, value); }
}
public static readonly DependencyProperty BackgroundColorProperty = DependencyProperty.Register("BackgroundColor", typeof(Color), typeof(WinStickyFingers), new PropertyMetadata(Colors.Black));
public Color BackgroundColor {
get { return (Color)base.GetValue(BackgroundColorProperty); }
set {
base.SetValue(BackgroundColorProperty, value);
}
}
<TextBlock DockPanel.Dock="Top" Text="{Binding Name}" Foreground="{Binding TextColor,Converter={StaticResource DebugConverter}}" Background="{Binding Path=BackgroundColor}" />
I'm using Bea Stollnitz's debugging method but my breakpoint isn't even triggered.
What is the DataContext of the TextBlock? How does it know that it is supposed to bind to the properties on your Window?
You need to either set DataContext to the Window instance, or set the Source (or RelativeSource, or ElementName) properties on your bindings. All of these properties exist as a means of resolving the bound object for your Binding. DataContext is a fallback if none of the others is set, but I'm guessing that you haven't set that either.

Resources