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}"
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'm having a custom Control that has a dependency property
public static readonly DependencyProperty SelectedUserCodeProperty = DependencyProperty.Register(
"SelectedUserCode",
typeof(decimal),
typeof(SystemUsersControl),
new PropertyMetadata(SelectedUserCodeChanged));
public decimal SelectedUserCode
{
get
{
return (decimal)this.GetValue(SelectedUserCodeProperty);
}
set
{
this.SetValue(SelectedUserCodeProperty, value);
RaisePropertyChanged("SelectedUserCode");
}
}
This control is inside another usercontrol that I'm attempting to get the dependency property above in its viewmodel
this xaml is inside the parent control
<SystemUsers:SystemUsersControl Name="ctrlSystemUsersControl" SelectedUserCode="{Binding SelectedSystemUserCode, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,2,0,0"/>
but nothing is bound to the parent control viewmodel
I don't know what's the problem, it's my first time dealing with dependency properties, I'm considering making the two controls in one :( unless I got any help :)
Don't worry,
SelectedSystemUserCode must be a property . If its a property you will see initial value ,but what will fully support binding for your class is ,implementation of INotifyPropertyChanged. This basic interface will be a messenger for us.
1)When you implement INotifyPropertyChanged,the below event will be added to your class.
public event PropertyChangedEventHandler PropertyChanged;
2)Then create a firing method
public void FirePropertyChanged(string prop)
{
if(PropertyChanged!=null)
{
PropertyChanged(prop);
}
}
3) Register this event for not getting null reference.
in constructor this.PropertyChanged(s,a)=>{ //may do nothing };
4) //You may use Lazy < T > instead of this.
public decimal SelectedSystemUserCode
{
get{
if(_selectedSystemUserCode==null)
{
_selectedSystemUserCode=default(decimal);
}
return _selectedSystemUserCode;
}
set
{
_selectedSystemUserCode=value;
FirePropertyChanged("SelectedSystemUserCode");
//This will be messanger for our binding
}
}
In addition,
As I remember is the default value so you may give a decimal value for that,SelectedUserCodeChanged is callback method its ok also.
//new PropertyMetadata(SelectedUserCodeChanged)
new PropertyMetadata(0) or null
Hope helps.
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.
I'm trying to follow the MVVM design paradigm with C# and XAML. I'm running into trouble with a nested user control. I'm trying to bind an element on the nested user control to one of the values in the ViewModel (which is bound to the View via the DataContext property). The same ViewModel is used for both the outer and nested user controls.
It partially works as is, but changes only go one-way from the ViewModel to the nested user control. I need the changes made in the nested user control to propagate back to the ViewModel.
Starting with the XAML for the main View, I have:
<UserControl>
<!-- ... -->
<UserControl.DataContext>
<local:MyViewModel x:Name="myViewModel" />
</UserControl.DataContext>
<!-- ... -->
<local:NestedUserControl
x:Name="nestedUserControl"
CustomNestedValue="{Binding Path=CustomValue, ElementName=myViewModel, Mode=TwoWay}" />
</UserControl>
In the C# code for the ViewModel:
// Constructor
public MyViewModel()
{
CustomValue = true;
}
private bool _customValue;
public bool CustomValue
{
get { return _customValue; }
set
{
if (_customValue != value)
{
_customValue = value;
RaisePropertyChanged ("CustomValue");
}
}
}
And in the code behind of the NestedUserControl, I have:
public static readonly DependencyProperty CustomNestedValueProperty =
DependencyProperty.Register (
"CustomNestedValue",
typeof (bool),
typeof (NestedUserControl),
new FrameworkPropertyMetatdata
{
BindsTwoWayByDefault = true,
PropertyChangedCallback =
new PropertyChangedCallback (CustomNestedValueChangedCallback)
});
public bool CustomNestedValue
{
get { return (bool) GetValue (CustomNestedValueProperty); }
set { SetValue (CustomNestedValueProperty, value); }
}
protected static void CustomNestedValueChangedCallback (
DependencyObject Source,
DependencyPropertyChangedEventArgs e)
{
bool value = (bool) e.NewValue;
NestedUserControl control = source as NestedUserControl;
control.OnCustomValueChange (value);
}
public void OnCustomValueChange (bool value)
{
RaisePropertyChanged ("CustomNestedValue");
// Do other stuff ...
}
// This function is where the nested user control gets direct
// interactions from the user which cause the dependency
// property to change. When this event occurs, the change needs
// to be communicated back up to the view model.
private void _onPreviewMouseDown (object sender, MouseButtonEventArgs e)
{
CustomNestedValue = !CustomNestedValue;
}
[Note: Not only do I set the binding mode to TwoWay when setting the binding in XAML, but I attempted to make this the default behavior of the DependencyProperty in the code above. No luck.]
Both the code behind for the nested user control and the ViewModel code contain the below PropertyChangedEventHandler event/response, which is necessary for the INotifyPropertyChanged interface. From what I understand, this is how bindings between XAML elements and the ViewModel are kept in sync.
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
try
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
catch (Exception e)
{
// ...
}
}
When I run the code, whenever the RaisePropertyChanged function is called for the NestedUserControl, the PropertyChanged event is always null. This is only a problem for the nested usercontrol, and not the outer one. Shouldn't this event be automatically set via the binding mechanism?
I've been struggling with this for several days now to no avail. Any help would be much appreciated. Thanks!
Binding to a DependencyObject operates without using the INotifyPropertyChanged interface. In fact, if you set a breakpoint in the getter or setter of the CustomNestedValue property of the NestedUserControl, you'll find it will never hit when binding in XAML. In essence, the INotifyPropertyChanged is a way of achieving binding without descending from DependencyObject.
When the MyViewModel.CustomValue is bound to the NestedUserControl, the binding code calls (in pseudo code):
NestedUserControl.SetBinding(binding, NestedUserControl.CustomNestedValueProperty)
The INotifyPropertyChanged.PropertyChanged event is never registered and will remain null. However, this doesn't necessarily answer why the value isn't going back to the ViewModel.
Regardless, you could remove a few moving pieces and go with
public static readonly DependencyProperty CustomNestedValueProperty =
DependencyProperty.Register("CustomNestedValue",
typeof (bool),
typeof (NestedUserControl),
null);
public bool CustomNestedValue
{
get { return (bool) GetValue (CustomNestedValueProperty); }
set { SetValue (CustomNestedValueProperty, value); }
}
That's how most of my DependencyProperties are written and they do support TwoWay binding.