silverlight 3 collection binding - silverlight

Someone please help me understand why this binding does not work...
I have a class called SelectionManager with a property called 'dates' which is populated by a WCF service. The property is an array of structs which bundles a DateTime and an integer count of business objects.
public class SelectionManager : INotifyPropertyChanged {
... other properties ...
public DQMServiceDateCountPair[] dates { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName) {
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
}
I have another class called DateSelector which has a DependencyProperty called 'pairs' setup to be the binding target of 'dates'.
public partial class DateSelector : UserControl {
... other stuff ...
public static readonly DependencyProperty pairsProperty = DependencyProperty.Register(
"pairs",
typeof(DQMServiceDateCountPair[]),
typeof(DateSelector),
new PropertyMetadata(new DQMServiceDateCountPair[0])
);
public DQMServiceDateCountPair[] pairs {
get { return (DQMServiceDateCountPair[])GetValue(pairsProperty); }
set {
Debug.WriteLine("adding dates");
SetValue(pairsProperty, value);
dateMode = DateMode.Years;
}
}
}
In my MainPage.xaml, I have a line like this:
<date:DateSelector x:Name="dateSelector" pairs="{Binding dates}" />
It's weird, because all my other bindings in MainPage.xaml update correctly, including a ComboBox bound to 'dates'. My UserControl however, will not update. The Debug.Writeline doesn't get called in the set statement of the 'pairs' property.
In playing around with it, I've tried making the DQMServiceDateCountPair[] property into an ObservableCollection and implementing INotifyCollectionChanged, but that doesn't help.
If I leave either the source property or the target property as an array, and make the other an ObservableCollection, then I get a binding error that says it can't automatically convert one to the other, so Silverlight seems aware of the binding, it just doesn't update it.
Can anyone help?
P.S. I'm using Silverlight 3.

Try changing your code as follows:
1. Add DataMember/DataContract attributes
2. Make "set" public
[DataContract]
public class SelectionManager : INotifyPropertyChanged {
[DataMember]
public DQMServiceDateCountPair[] dates { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName) {
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
}

Whats actually wierd is that the other stuff is working when you've coded your class the way you have. My guess is that the dates array gets set by some code that runs internally in your selection manager on completion of a WCF request.
Howerver whilst you have implemented INotifyPropertyChanged you aren't actually raising the event that it defines. You can't really combine INotifyPropertyChanged with the Auto-property C# syntax. You need this:-
public SelectionManager : INotifyPropertyChanged
{
private DQMServiceDateCountPair[] myDates;
public DQMServiceDateCountPair[] dates
{
get { return myDates; }
set
{
myDates = value;
NotifyPropertyChanged("dates");
}
// rest of your code
}

So, here's what what going on. The binding has been working perfectly well this whole time. For the past week I've been struggling with this, it's been happily updating along--but because of a faulty assumption on my part, I could never see it.
In case anyone else harbors this faulty assumption, let me spell it out:
The GetValue and SetValue calls are not made automatically by virtue of the fact that you are declaring a Dependency Property. The "new PropertyMetadata()" part of the declaration has an overload that takes a callback method. In this callback method, you have to set the property value yourself. For instance, in my code, I made this the PropertyMetadata call:
new PropertyMetadata(new PropertyChangedCallback(OnPairsPropertyChanged))
and the callback method reads like this:
private static void OnPairsPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
((DateSelector)d).pairs = (DQMServiceDateCountPair[])e.NewValue;
}
Thanks to everyone who tried to help!

Related

Drawing two polylines from one PointCollection throws exception (Silverlight 5)

I'm using Silverlight 5 with MVVM.
I have a ViewModel that is a Singleton. The ViewModel exposes a PointCollection that I am using to draw a Polyline in one of my views.
If I try to draw the same Polyline in a second view, by databinding to the PointCollection again, I get a "value does not fall within the expected range" exception.
As far as I could find out (with my limited Silverlight knowledge), this is caused by the fact that PointCollections are not shareable.
Is there a workaround? How can I get a second Polyline drawn that is identical to the first? I want to databind two Polylines to one PointCollection at the same time.
Edit: I haven't found a solution, but someone with the same problem here. According to Microsoft:
This MSDN page mentions that some objects are not shareable and will genereate a "value out of range" exception.
http://msdn.microsoft.com/en-us/library/system.windows.resourcedictionary(VS.95).aspx
The PointCollection page also mentions that it is not shareable.
http://msdn.microsoft.com/en-us/library/system.windows.media.pointcollection(VS.95).aspx
Currently, this is by design behavior. However, we are evaluating this to see whether we can either change the behavior or at least the exception text.
I found a solution here: duplicate the PointCollection in the getter.
private PointCollection sourcePoints;
public PointCollection SourcePoints
{
get
{
// create a new instance of PointCollection for binding
PointCollection newPoints = new PointCollection();
foreach (Point p in sourcePoints)
{
newPoints.Add(p);
}
return newPoints;
}
Have a look at this question: Why doesn't this data binding work?
And at this one too: 2nd time binding to PointCollection not being rendered
As you gave little details I am not quite sure what is going on but these posts might help out. If not, please post your code.
I did some testing and the best solution I can think of is this:
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;
namespace SilverlightApplication6
{
public class DemoVM : INotifyPropertyChanged
{
#region PointsClone Property
private PointCollection _pointsClone;
public PointCollection PointsClone
{
get
{
return _pointsClone;
}
set
{
if (_pointsClone != value)
{
_pointsClone = value;
OnPropertyChanged("PointsClone");
}
}
}
#endregion
#region Points Property
private PointCollection _points;
public PointCollection Points
{
get
{
return _points;
}
set
{
if (_points != value)
{
_points = value;
PointsClone.Clear();
foreach (var point in _points)
{
PointsClone.Add(point);
}
OnPropertyChanged("Points");
}
}
}
#endregion
public DemoVM()
{
PointsClone = new PointCollection();
Points = new PointCollection();
}
public void AddPoint(Point point)
{
Points.Add(point);
PointsClone.Add(point);
}
public void ClearPoints()
{
Points.Clear();
PointsClone.Clear();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Bind one PolyLine.Points to Points and the other PolyLine.Points to PointsClone.
It is a bit ugly because it will break when you use vm.Points.Add(point) instead of vm.AddPoint(point). By applying proper encapsulation you might be able to solve that.
Maybe your PointCollection is Freezed and that's making the trouble.
MSDN:
Freezable Features: Because it inherits from the Freezable class, the PointCollection class provides several special features: PointCollection objects can be declared as resources, shared among multiple objects, made read-only to improve performance, cloned, and made thread-safe. For more information about the different features provided by Freezable objects, see the Freezable Objects Overview.

How can I get bindings to update when the value is changed?

I'm trying to understand WPF binding. As simple as it gets:
I have a ClassWithProperty that has a public uint Prop1.
The main window has a public ClassWithProp object and uses it for data context. This is set in the main Windows's constructor:
this.ClassWithProp = new ClassWithProp();
this.DataContext = this.ClassWithProp;
ClassWithProp's default constructor sets Porp1 value to 1.
The main windows contains a label:
<Label Content="{Binding Prop1}" ... />
It also contains a button that, when click, sets the ClassWithProp.Prop1 to 2.
When the window first appears, the label correctly shows 1. When the button is clicked the property's value is changed to 2, but the lable does not refresh.
Sorry - probably obvious but I'm a novice in WPF:
Why doesn't the bound label update when the undelying property changes?
Your ClassWithProperty needs to implement the INotifyPropertyChanged interface (which has just the one event on it, PropertyChanged), this way the WPF binding subsystem can listen for property changes and update the value. When you have changed the value of a property, you raise the event.
Here is an example:
pulic class ClassWithProperty : INotifyPropertyChanged
{
public uint Prop1
{
get { return _prop1; }
set
{
_prop1 = value;
OnPropertyChanged("Prop1");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private uint _prop1;
}
Implement INPC.
Also read the overview, it probably answers more than 90% of questions people have about data binding.

PropertyChangedEventHandler always null in silverlight binding

I've been trying to resolve this issue for some time.
I'm trying to bind a TextBlock's text to a string property using the INotifyPropertyChanged interface. For some reason the PropertyChangedEventHandler is always null when the value of the property is changed thus the target never gets updated...
Any suggestions?
Code below:
XAML code:
<UserControl x:Class="MoleDashboard.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:basics="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:datacontrols="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
xmlns:prop="clr-namespace:MoleDashboard"
<UserControl.Resources>
<prop:YearScreen x:Key="YearScreenProps"/>
</UserControl.Resource>
<TextBlock Margin="10 5" x:Name="DataGridLabel" Visibility="Visible" Text="{Binding YearProperty, Source={StaticResource YearScreenProps}, Mode=OneWay}"/>
Bound property code:
public class YearScreen : INotifyPropertyChanged
{
private string metricProperty;
private string yearProperty;
public event PropertyChangedEventHandler PropertyChanged;
public YearScreen()
{
}
public string YearProperty
{
get { return yearProperty; }
set { yearProperty = value; this.OnPropertyChanged("YearProperty"); }
}
public string MetricProperty
{
get { return metricProperty; }
set { metricProperty = value; this.OnPropertyChanged("MetricProperty"); }
}
public void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
This is based on the comments provided and not just the question above.
Basically the problem is that you are creating updating a second instance of your ViewModel (called a YearScreen in your code) and updating that.
A YearScreen is already being created and bound to your Xaml, by the inclusion of:
<UserControl.Resources>
<prop:YearScreen x:Key="YearScreenProps"/>
</UserControl.Resource>
You are then creating a second ViewScreen elsewhere in code (via new ViewScreen()) and updating that, however there is no connection between the 2 ViewScreen instances, so nothing will update in the Xaml page.
One Possible (quick) solution:
Create your YearScreen as a singleton. That is add a static accessor of type YearScreen in the class and set it from the constructor.
public class YearScreen : INotifyPropertyChanged
{
private static YearScreen _This;
public static YearScreen This { get { return _This; } }
[snip]
public YearScreen()
{
_This = this;
}
The you can access the "single" instance of your YearScreen from elsewhere using the static singleton accessor e.g.:
YearScreen.This.YearProperty = DateTime.Now.ToString():
There are better patterns for sharing ViewModels than singletons, but that will get you going.
The pattern you started with is ViewFirst creation (the view creates the ViewModel). ModelFirst does the opposite, but is bad as the model knows how it is displayed. Using a controller object to create the View and ViewModel and connect them is a better alternative, but that is then getting quite complicated. Using injection of single instance objects is a better option, but involves a whole load of new concepts. Lookup Silverlight Prism after you solve your current problems.
Instead of creating the ViewModel in the resources you should set it into the DataContext of the view from external code.
If you really want to put it in the Resources like that you can get it out of the resources in the code behind Loaded method or in the constructor after the initializecomponent call. Like so:
private YearScreen model;
public MainPage()
{
this.Loaded += MainPage_Loaded;
this.InitializeComponent();
}
void MainPage_Loaded(object sender, EventArgs e)
{
this.model = (YearScreen)this.Resources["YearScreenProps"];
}
Maybe expose it as a property so you can then access it externally. But personally I'd rather create the model externally than pass it into the View instead. Put it into the DataContext.

how do i get a wpf window to refresh?

I am building a simple roulette app. The player(UI) puts together a list of bets and submits them to the table object to be evaluated and paid out. I've got the code to work and the game process goes smoothly. The problem is that after a turn I can't get the player balance(textblock) or the betlist(listview) to update. Is there some sort of global window refresh command I am missing, or do I have to manually set each of these to update somehow?
WPF can take care of updating these values for you automatically, but you have to let it know when things have changed. Typically, this is done by using DependencyProperties on your model objects, but it can also be done by implementing INotifyPropertyChanged. In either case, when you update a property's value, the PropertyChanged event gets called; WPF automatically subscribes to this event when it binds to a value, and will update the UI when a change occurs. Without this notification, WPF won't check to see if the values in your object have changed, and you won't see the change reflected on the screen.
What about implementing INotifyPropertyChanged, and bind the balance and the betlist to the controls you are using?
Something like:
public class Player : INotifyPropertyChanged
{
private int _balance;
#region Properties
public int Balance
{
get { return this._balance; }
set
{
if (this._balance != value)
{
this._balance = value;
NotifyPropertyChanged("Balance");
}
}
}
public BindingList<Bet> BetList { get; set; }
#endregion // Properties
private void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public class Bet
{
// some code
}
For the binding list you wouldn't need to implement anything since it implements an interface that notifies changes to whatever is bound to (IRaiseItemChangedEvents). But then again you could be using a different approach.

What's the point in setting up dependency properties, when PropertyChangedEventHandler does the job?

Currently I have use the following approach to setup change notification on any of my properties that I bind to in xaml:
class MyClass : INotifyPropertyChanged
{
string name;
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
However, I've seen that to implement a dependency property I need to do stuff like registering it and setting callbacks etc, which in turn will just end up calling the above code.
So what's the point of setting all the extra boiler plate stuff for dependency properties when I can just use the above approach?
Thanks.
Dependency properties can be the target of a binding, whereas regular CLR properties can't. That's why the properties of a control (binding target) are usually dependency properties, whereas the properties of a model or ViewModel class (binding source) are not.
What you are doing is correct (assuming I understand it correctly) dependency properties are not for the things you bind to in the model they are for the properties in controls that the model will be bound to - for example the Text property in a text box.
There are a number of reasons to use them in your custom controls, not least of which is the automatic plumbing that comes with them so that they will correctly bind to a property declared as in your example.

Resources