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.
Related
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}">
I want to change a value (textBlock) according to an event. Then, I want to refresh my window, but I couldn't. I used invalidateVisual as well as solutions of other posts, but nothing worked.
Thank you in advance
Several solutions (the first and second one does not make use of databinding).
txtMyControl.text = "New value";
If not on the main thread, you could use the dispatcher to update the value.
Application.Current.Dispatcher.BeginInvoke(() => txtMyControl.text == "New Value")
However, the most WPF friendly way to do it is to use the databinding.
Any change made to the value in code will be instantly reflected in the UI.
XAML
<TextBox x:Name="txtExample" Text="{Binding MyTextProperty,Mode=TwoWay}" Height="24" Width="120"/>
In your code, you have to declare a variable that will be persistent.
private ExampleModel _ExampleModel = new ExmampleModel();
When you load your code, you associate that variable to your textbox data context.
txtExample.DataContext = _ExampleModel
Then, you have the class that will contains all the editable properties on screen (textboxes, radio boxes, etc...)
public class ExampleModel : INotifyPropertyChanged
{
private string _MyTextProperty = "test";
public string MyTextProperty {
get { return _MyTextProperty; }
set {
if (string.Compare(_MyTextProperty, value) != 0) {
_MyTextProperty = value;
RaisePropertyChanged("MyTextProperty");
}
}
}
public void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
}
Whenever you handle your event, you just have to change the value of the property containing the information and the UI will refresh accordingly. Also, since we use a two-way binding, the value from your textbox will always be the same than the one contained by MyTextProperty property in ExampleModel class, which make value retrieval very easy.
ex:
_ExampleModel.MyTextProperty = "New value";
If you were already using databinding, make sure the class used implements INotifyPropertyChanged and that the propertyChanged event is called when the property value change or otherwise it won't update the UI.
The best approach to what you're trying to do would be to use Data Binding.
You need to have a string object that will always hold the value of your textblock. Next you need to bind that object to your textblock and then use the event provided by the INotifyPropertyChanged interface and each time the value changes its representation (the textblock) will change to, no need to refresh the window.
More information here
If your event updates the textblock and the textblock you are using is bound to a string property and that property issues a NotifyPropertyChanged() in it's set method, that will cause the display to refresh as you desire.
There are other ways, but this is the easiest given my understanding of your question.
(this is similar to the other answer, but I tried to word so it is easier to understand/implement.)
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}"
I was looking at this question, but I don't understand how to actually USE the created AttachedProperty. The problem is trying to have a binding on the source of the WebBrowser control.
The code there looks like:
public static class WebBrowserUtility
{
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(WebBrowserUtility), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string) obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
WebBrowser browser = o as WebBrowser;
if (browser != null)
{
string uri = e.NewValue as string;
browser.Source = uri != null ? new Uri(uri) : null;
}
}
}
and
<WebBrowser ns:WebBrowserUtility.BindableSource="{Binding WebAddress}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Width="300"
Height="200" />
The WebAddress, what is that exactly? This is my understanding (which is probably wrong):
There's an AttachedProperty that can be attached to any object, and in this particular case, it is basically just attaching a property called BindableSource which is of type String.
When we have the "{Binding WebAddress}" it means that in some c# code somewhere that handles this .xaml file there's something that looks like:
public String WebAddress
{
// get and set here? not sure
}
And to take advantage of the property changed, I can called RaisedPropertyChanged and it will fire that static method up there?
Even when I look at it, it doesn't seem right, but I can't find anything online to help me.
There's an AttachedProperty that can be attached to any object, and in this particular case, it is basically just attaching a property called BindableSource which is of type String.
You might want to read the MSDN article on attached properties.
It is rather simple: Dependency properties work with dictionaries in which controls are associated with their values for a property, this makes it quite easy to add something like attached properties which can extend a control.
In the RegisterAttached method of the attached property a PropertyChangedCallback is hooked up which will be executed if the value changes. Using a dependency property enables binding which is the point of doing this in the first place. All the property really does is call the relevant code to navigate the browser if the value changes.
When we have the "{Binding WebAddress}" it means that in some c# code somewhere that handles this .xaml file there's something that looks like [...]
The binding references some public property or depedency property (not a field) called WebAddress inside the DataContext of the WebBrowser. For general information on data-binding see the Data Binding Overview.
So if you want to create a property which should be a binding source you either implement INotifyPropertyChanged or you create a DependencyProperty (they fire change notifications on their own and you normally do only create those on controls and UI-related classes)
Your property could look like this:
public class MyModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private string _webAddress;
public string WebAddress
{
get { return _webAddress; }
set
{
if (value != _webAddress)
{
_webAddress = value;
NotifyPropertyChanged("WebAddress");
}
}
}
}
Here you have to raise the PropertyChanged event in the setter as you suspected. How to actually declare working bindings in XAML is a rather broad topic sp i would like to direct you to the aforementioned Data Binding Overview again which should explain that.
And to take advantage of the property changed, I can called RaisedPropertyChanged and it will fire that static method up there?
The event is fired to trigger the binding to update, this in turn changes the value of the attached property which in turn causes the PropertyChangedCallback to be executed which eventually navigates the browser.
What I need is to be able to execute code in a code-behind for my view class when a property on my view-model is updated. My understanding is that I need to use a dependency-property.
My view-model does implement INotifyPropertyChanged.
Here is the property in my view-model:
private DisplayPosition statusPosition;
public DisplayPosition StatusPosition
{
get { return this.statusPosition; }
set
{
this.statusPosition = value;
this.OnPropertyChanged("StatusPosition");
}
}
Here is my dependency property in my view:
public DisplayPosition StatusPosition
{
get { return (DisplayPosition)GetValue(StatusPositionProperty); }
set { SetValue(StatusPositionProperty, value); }
}
public static readonly DependencyProperty StatusPositionProperty =
DependencyProperty.Register(
"StatusPosition",
typeof(DisplayPosition),
typeof(TranscriptView),
new PropertyMetadata(DisplayPosition.BottomLeft));
Here is where I set up my binding in my view class (handler for this.DataContextChanged):
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Binding myBinding = new Binding("StatusPosition");
myBinding.Source = this.DataContext;
myBinding.NotifyOnTargetUpdated = true;
this.SetBinding(TranscriptView.StatusPositionProperty, myBinding);
}
When I put a break-point on the setter for the property in my view, it never gets hit even after I watch the value change in the view-model, and the PropertyChanged event raised. Ultimately, my goal is to be able to put more code in the setter.
The hairy detail, if you're curious, is that I need to move a TextBlock around between multiple StackPanels based on this value. I can't seem to find a XAML-only way of doing that.
More often than not, these problems are simple little obvious things that I've missed. Nothing I'm trying is helping me sort this one out, though.
When I put a break-point on the setter for the property in my view, it never gets hit even after I watch the value change in the view-model, and the PropertyChanged event raised. Ultimately, my goal is to be able to put more code in the setter.
You can't do this. When you're using DependencyProperties, the setter is never called when the bound property changes. It's only purpose is to allow you to set the DP from code.
You need to, instead, add a PropertyChangedCallback to the metadata on your DP, and add the extra code there. This will get called when the DP value updates, whether via binding, code, etc.