Silverlight CustomControl dependency property can't be bound to parent viewmodel - c#

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.

Related

Custom Usercontrol TextBlock.text binding

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}"

Binding properties not working

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.

DependencyProperties: PropertyChangedCallBack only called once

I created a control, derived from Canvas, that should plot a live diagram, given values that are passed via a binding to a DependencyProperty. The simplified version is this:
public class Plotter : Canvas
{
public float Value { get { return (float)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(float), typeof(Plotter),
new PropertyMetadata(0f, new PropertyChangedCallback(ValueChangedCallBack)));
public static void ValueChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
Plotter plotter = (Plotter)property;
plotter.Value = (float)args.NewValue; //<-- Removed this line to get it to work
// Actually draw the value into the canvas geometry
plotter.PlotValue(plotter.Value);
}
}
I bound the control like this:
<mystuff:Plotter Value="{Binding MyViewModelProperty}" Height="50" Width="200" />
My ViewModel implements INotifyPropertyChanged and calls PropertyChanged correctly. If I bind MyViewModelProperty to a textbox, it correctly updates every time. Only if I bind it to my own control, my ValueChangedCallBack is only called once as the page is loaded, and then never again.
What am I not seeing here? Thanks for any help!
Solved: I dont have to set the Value explicitly in the callback.
You set the property Value on the callback for the property Value changing. That doesn't make much sense in any case. But is that locally set value overriding the binding value, causing your binding to no longer be set on the dependency property?
Do you need to set the Mode of your binding to TwoWay?
Should you not be using DependencyProperty.Register instead of DependencyProperty.RegisterAttached?

Using a custom attached property with a binding

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.

DependencyProperty binding mode twoway but propertychangedeventhandler is null

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.

Categories