XAML code:
<DatePicker Date="{Binding DateTimeOffsetTest}"></DatePicker>
DataContext is set to App.ViewModel (static instance of ViewModel - just for a test!)
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private DateTimeOffset _dateTimeOffsetTest = new DateTimeOffset(new DateTime(1980, 10, 10));
public DateTimeOffset DateTimeOffsetTest
{
get { return _dateTimeOffsetTest; }
set
{
_dateTimeOffsetTest = value;
//Was OnPropertyChanged(""); (Thanks to Will for fix)
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
For a test, i simply initialized ViewModel as static instance in App.
I added this code to the button's click event handler:
App.ViewModel.DateTimeOffsetTest = new DateTimeOffset(new DateTime(1988, 09, 11));;
Problem is that DatePicker behaves like it's OneTime binding. If I update the property in ViewModel, DatePicker won't update. I've tried setting the mode to OneWay and change the UpdateSourceTrigger but it doesn't work.
Does anyone have a solution?
(note, this answer applies to the original code of the question)
OnPropertyChanged("");
Yeah, nope. You're providing a name (a meaningless one--an empty string) to a property that doesn't exist.
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
In this case, propertyName will be supplied only if you OMIT the argument in the method call.
According to the reference docs at MSDN:
The Caller Info attributes don't make a parameter optional. Instead, they affect the default value that's passed in when the argument is omitted.
If you pass any value into the method, it will override the CallerMemberNameAttribute-assigned value.
In your property setters, do the following:
public DateTimeOffset DateTimeOffsetTest
{
get { return _dateTimeOffsetTest; }
set
{
_dateTimeOffsetTest = value;
OnPropertyChanged(); // LOOK HERE
}
}
The framework will provide the name of the caller to the method, as you have not provided one. Your bindings will now work.
Apparently, DatePicker's OneWay binding mode is bugged and changes are not reflected. You can "solve" the issue by setting the binding mode to TwoWay.
<DatePicker Date="{Binding DateTimeOffsetTest, Mode=TwoWay}">
Related
I'm using Mahapp TimePicker control, I define it in the following way:
<Controls:TimePicker Culture="it-IT" Width="200" Controls:TextBoxHelper.Watermark="Start pause" SelectedTime="{Binding Stop, IsAsync=True, UpdateSourceTrigger=PropertyChanged}"/>
and this is the property for bind the value:
private TimeSpan? _stop;
public TimeSpan? Stop
{
get { return _stop; }
set
{
_stop = value;
OnPropertyChanged();
}
}
this is my OnPropertyChanged(); implementation:
public new event PropertyChangedEventHandler PropertyChanged;
protected new virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
This is the situation: when I change the value on the control this doesn't execute the binding, this only happen in the fist time, the second time that I execute the binding the value is taken correctly. If I put a breakpoint both on get and set instead, seems that the binding is done correctly. I don't what'happean.
Remove the IsAsync = true. IsAsync should only be used, when your getter is expected to return the result with noticeable delay. Or as Microsoft puts it:
Use the IsAsync property when the get accessor of your binding source property might take a long time. One example is an image property with a get accessor that downloads from the Web. Setting IsAsync to true avoids blocking the UI while the download occurs.
So basically the getter is executed in another thread and returns the value to the binding once it's done. No idea how WPF actually handles setters with IsAsync, maybe someone else can add that part.
Can anyone tell me why PropertyChanged value comes null in INotifyPropertyChanged. I have a property and I change its value, but when it enters in NotifyPropertyChanged method the value of PropertyChanged is null. Following is my code :
public class WorkOrder : INotifyPropertyChanged
{
public string _orderDays="0 Days";
public string OrderDays
{
get { return _orderDays; }
set
{
if (_orderDays == value)
return;
_orderDays = value;
NotifyPropertyChanged("OrderDays");
}
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
public void NotifyPropertyChanged([CallerMemberName]String propertyName =null )
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
this is the xaml where I bind the value
<Label x:Name="lblTicketDate" Text="{Binding OrderDays}"
Grid.Row="1" HorizontalOptions="CenterAndExpand"
VerticalOptions="EndAndExpand" />
but when it enters in NotifyPropertyChangd method the value of PropertyChanged is null
That just means that nothing has subscribed to the event yet. Beyond events, a delegate instances refers to one or more actions to execute when the delegate is invoked - a null reference is used to represent "0 actions".
If you use:
var order = new WorkOrder();
// Or whatever's useful instead of Console.WriteLine
order.PropertyChanged += (sender, args) => Console.WriteLine(e.PropertyName);
order.OrderDays = "Different";
then you'll see the event being raised.
I was experimenting with Data Binding in Windows Forms and found a glitch that I can't explain. I post the question here in hopes that someone in the community can come up with an answer that makes sense.
I tried to come up with a clever way of binding read-only values that depend on operations on other values, and update it automatically when the dependent values change.
I created a form with 3 textboxes, where I want the sum of the first 2 to appear in the 3rd textbox.
The following code should work, but doesn't, at least not properly:
public class Model : INotifyPropertyChanged
{
private int m_valueA;
private int m_valueB;
public int ValueA
{
get { return m_valueA; }
set { m_valueA = value; RaisePropertyChanged("ValueA"); }
}
public int ValueB
{
get { return m_valueB; }
set { m_valueB = value; RaisePropertyChanged("ValueB"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class DynamicBindingProperty<T> : INotifyPropertyChanged
{
private Func<T> m_function;
private HashSet<string> m_properties;
public DynamicBindingProperty(Func<T> function, INotifyPropertyChanged container, IEnumerable<string> properties)
{
m_function = function;
m_properties = new HashSet<string>(properties);
container.PropertyChanged += DynamicBindingProperty_PropertyChanged;
}
public T Property { get { return m_function(); } }
void DynamicBindingProperty_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!m_properties.Contains(e.PropertyName)) return;
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs("Property"));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
InitializeDataBinding();
}
private void InitializeDataBinding()
{
Model model = new Model();
DynamicBindingProperty<int> tmp = new DynamicBindingProperty<int>(() => model.ValueA + model.ValueB, model, new[] {"ValueA", "ValueB"});
textBox1.DataBindings.Add("Text", model, "ValueA");
textBox2.DataBindings.Add("Text", model, "ValueB");
textBox3.DataBindings.Add("Text", tmp, "Property");
tmp.PropertyChanged += (sender, args) => Console.WriteLine(args.PropertyName);
}
}
After experimenting for a while, I tried renaming DynamicBindingProperty<T>.Property to something else (e.g. DynamicProperty), and everything worked as expected!. Now, I was expecting something to break by renaming Model.ValueA to Property, but it didn't, and still worked flawlessly.
What is going on here?
I did some debugging and it looks like a bug (or requirement "the property must not be named Property" I am not aware of). If you replace
PropertyChanged(this, new PropertyChangedEventArgs("Property"));
with
PropertyChanged(this, new PropertyChangedEventArgs(null));
it still does not work - null or an empty string means any property may have changed. This indicates that problem is not in the handling of the change notification but that the binding has not been correctly established.
If you add a second property Property2 to DynamicBindingProperty<T> that does the same as Property and bind it to a fourth text box, then both text boxes will get update correctly if you perform a change notification with an empty string, null or "Property2". If you perform the change notification with "Property" both text boxes will not get update correctly. This indicates that the binding to Property is not completely broken and also that the change notification is somewhat broken.
Sadly I was unable to pin down the exact location where things go wrong, but if you invest enough time stepping through optimized framework source code you can probably figure it out. The earliest difference between the case with property name Property and the case with property name Property2 I could identify when processing a change notification was in OnValueChanged() in the internal class System.ComponentModel.ReflectPropertyDescriptor. In one case the base implementation gets called while it gets skipped in the other case - at least if the debugger didn't trick me, but this is hard to tell in optimized code.
I have a Custom Usercontrol that has a textblock whose text will change on occasion.
the TextBlocks Code is
XAML:
<TextBlock Text="{Binding ElementName=dashboardcounter, Path=Counter}" FontFamily="{Binding ElementName=dashboardcounter, Path=FontFamily}" HorizontalAlignment="Left" Margin="17,5,0,0" VerticalAlignment="Top" FontSize="32" Foreground="#FF5C636C"/>
.cs:
private static readonly DependencyProperty CounterProperty = DependencyProperty.Register("Counter", typeof(string), typeof(DashboardCounter));
public string Counter
{
get { return (string)GetValue(CounterProperty); }
set { SetValue(CounterProperty, value); }
}
My Class:
private string _errorsCount;
public string ErrorsCount
{
get { return _errorsCount; }
set { _errorsCount = value; NotifyPropertyChanged("ErrorsCount"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Binding of said usercontrol:
dashboardCounter.Counter = view.ErrorsCount;
The TextBlock shows - absolutly NOTHING.
what am I doing wrong?
the string is dynamic and changes on occasion.
It was originally an Int but I chose it to be string instead and convert my "Count" toString() instead of creating a IValueConverter
By using dashboardCounter.Counter = view.ErrorsCount; you are just calling the setter of your dependency property, which in turn calls the DependencyProperty.SetValue method.
Here's the official description of it (from msdn) :
Sets the local value of a dependency property, specified by its
dependency property identifier.
It sets the local value and that's all (of course following this assignment, your binding and your textblock will be updated of course).
But there's no binding creation between you Counter property and your ErrorsCount property.
So updating ErrorsCount won't update Counter and as a result your TextBlock won't be updated as well.
In your example, when dashboardCounter.Counter = view.ErrorsCount; is called probably during an initialization phase, Counter is set to string.Empty or null (assuming this a the value of ErrorsCount at that point) and will remain unchanged. No binding being created, updating ErrorsCount won't affect Counter or your view.
You have at least 3 solutions to solve your problem :
1. Directly bind your Text property to the DependencyProperty or the "INotifyPropertyChanged powered property" that is actually changing (most common case)
2. Create the needed binding programmatically by yourself instead of using dashboardCounter.Counter = view.ErrorsCount;. You'll find a short official tutorial in here and the code could look as the following one :
Binding yourbinding = new Binding("ErrorsCount");
myBinding.Source = view;
BindingOperations.SetBinding(dashboardCounter.nameofyourTextBlock, TextBlock.TextProperty, yourbinding);
3. And of course, bind your ErrorsCount property to your Counter property in XAML but i don't know if it would fit your needs :
<YourDashboardCounterControl Counter="{Binding Path=ErrorsCount Source=IfYouNeedIt}"
Well the title may not help to much, but here is my problem. I have problems binding properties between controls in SILVERLIGHT. Here is the structure:
There is a control called "ProjectItemList", this control will recieve a string and display it in a very simple way.
There is a control called "ProjectList", this control will recive an array of string and use the control "ProjectItemList" to represent each of the strings in the array.
There is a window called "ProjectWindow", this window contains a "ProjectList" control, and a dependency property called "Nombres".
The context: The "ProjectWindow" window include a "ProjectList" control, there is a binding between ProjectWindow's dependency property "Nombres" and a dependency property "ListOfNames", here is the code of the "ListOfNames" dependency property:
public static DependencyProperty ListOfNamesProperty =
DependencyProperty.Register("ListOfNames", typeof(string[]), typeof(ProjectList), new PropertyMetadata(null));
public string[] ListOfNames
{
get
{
return (string[])GetValue(ListOfNamesProperty);
}
set
{
SetValue(ListOfNamesProperty, value);
List<ProjectItemList> auxList = new List<ProjectItemList>();
foreach (string s in value)
{
ProjectItemList il = new ProjectItemList();
il.Nombre = s;
this.lb_projects.Items.Add(il);
}
}
}
The problem is that, although the list control in "ProjectList" represent the "Nombres" property, the "Set" of "ListOfNames" is never called, so I can't create each of the "ProjectItemList" objects and represent the data.
I'm fairly new into XAML and Silverlight, so i may not considering something simple.
Thanks!
When you use dependency properties, the set and get are not called through ui binding, this is your problem.
WPF ui controls tald directrly with the dependeny property without going through your getter and setter.
You need to specify a callback to monitor property changes:
public static DependencyProperty ListOfNamesProperty =
DependencyProperty.Register("ListOfNames", typeof(string[]), typeof(ProjectList),
new PropertyMetadata(ListOfNamesChaned));
private static void ListOfNamesChaned(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
List<ProjectItemList> auxList = new List<ProjectItemList>();
foreach (string s in value)
{
ProjectItemList il = new ProjectItemList();
il.Nombre = s;
this.lb_projects.Items.Add(il);
}
}
public string[] ListOfNames
{
get
{
return (string[])GetValue(ListOfNamesProperty);
}
set
{
SetValue(ListOfNamesProperty, value);
}
}
See Chen Kinnrot's answer to know why.
For this to achieve, Raise Property Change event can be used i.e with the propertychanged event handler,Code snippet is like:
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
use OnPropertyChanged to bind the property and if any change in that property.it will notify and your binding properties automatically will be changed and ui will be updated which are bound to these properties.