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

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.

Related

IMarkupExtension with DependencyProperties

I'm trying to create a custom markup extension using IMarkupExtension<T> that has some DependencyProperties for binding. However, I am struggling to resolve the problem of the markup extension being resolved at XAML parse time, and the bindings only later. I don't seem to ever get something through the bindings: they're always null and never call their change callback.
The docs mention something about returning the instance of the markup extension (under "Returning the Current Markup Extensions Instance"), but that seems to make stuff explode because it's the wrong type for the target. This SL5 MultiBinding seems to return a proxy binding to an internal source object, but I can't manage to get that working: my bindings still don't ever set.
I can't seem to find any solid information how how to actually implement markup extensions with DependencyProperties (even though it seemed like something a lot of people were excited about with SL5...). Can anyone offer any guidance or tutorials?
Specifically, what I'm trying to do is create a markup extension that can dynamically construct a path to do a binding to a list, like so:
{my:ListLookup ListPath='List' Index={Binding Index}}
I'm wanting it to basically output a Binding that would look like {Binding List[Index]}, where Index is dynamic. The purpose of doing this over, say, a MultiBinding on the list and index, is so that we are binding directly to the object and get change notifications. (If there's a better way of doing this...)
I've fiddled with this a lot more and I've found the solution. It's based on the implementation of the SL5 MultiBinding that I linked to in the question.
The trick is that a Binding on a MarkupExtension will never be evaluated because it doesn't have a DataContext or something, but if you take the BindingExpression from it and throw it into a proxy Attached Property (attached to the target object) then you can get the Binding to resolve.
Below is a simple MarkupExtension that demonstrates this. All it's doing is taking a single Binding and outputting its value (obeying changes appropriately), but it shows how it holds together. This can be extended to solve the dictionary issue I was talking about, along with this problem in general.
public class SimpleBindingMarkupExtension : DependencyObject, IMarkupExtension<object>, INotifyPropertyChanged
{
public object Binding
{
get { return (object)GetValue(BindingProperty); }
set { SetValue(BindingProperty, value); }
}
public static readonly DependencyProperty BindingProperty =
DependencyProperty.Register(
"Binding",
typeof(object),
typeof(SimpleBindingMarkupExtension),
new PropertyMetadata(null));
public static readonly DependencyProperty ProxyAttachedBindingProperty =
DependencyProperty.RegisterAttached(
"ProxyAttachedBinding",
typeof(object),
typeof(SimpleBindingMarkupExtension),
new PropertyMetadata(null, OnProxyAttachedBindingChanged));
public static readonly DependencyProperty AttachedMarkupExtensionProperty =
DependencyProperty.RegisterAttached(
"AttachedMarkupExtension",
typeof(SimpleBindingMarkupExtension),
typeof(SimpleBindingMarkupExtension),
new PropertyMetadata(null));
private object _bindingSource;
public object BindingSource
{
get { return _bindingSource; }
set
{
_bindingSource = value;
OnPropertyChanged("BindingSource");
}
}
private static void OnProxyAttachedBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Pull the MarkupExtension from the attached property
var markupExtension = (SimpleBindingMarkupExtension) d.GetValue(AttachedMarkupExtensionProperty);
markupExtension.ProxyAttachedBindingChanged(e.NewValue);
}
private void ProxyAttachedBindingChanged(object value)
{
BindingSource = value;
}
public object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = (IProvideValueTarget) serviceProvider.GetService(typeof (IProvideValueTarget));
DependencyObject targetObject = target.TargetObject as DependencyObject;
if (targetObject == null)
return null;
// Attach this MarkupExtension to the object so we can find it again from attached property change callbacks
targetObject.SetValue(AttachedMarkupExtensionProperty, this);
// Put binding onto proxy attached property, so it actually evaluates
var localValue = ReadLocalValue(BindingProperty);
var bindingExpression = localValue as BindingExpression;
if (bindingExpression == null)
{
return localValue;
}
Binding originalBinding = bindingExpression.ParentBinding;
BindingOperations.SetBinding(targetObject, ProxyAttachedBindingProperty, originalBinding);
// Give the target a proxy Binding that binds to a property on the MarkupExtension
Binding binding = new Binding
{
Path = new PropertyPath("BindingSource"),
Source = this
};
return binding.ProvideValue(serviceProvider);
}
#region INotifyPropertyChanged
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
Usage:
<TextBlock Text="{local:SimpleBindingMarkupExtension Binding={Binding Text}}"/>
As mentioned, this example will produce the same result as just saying Text="{Binding Text}", but shows the solution.

MVP in Winforms

I'm primarily from an ASP.Net background with some MVC. I've also done a little Silverlight and MVVM, however I'm now about to move into Winforms which I have very little experience of, so I'm wondering how to tackle MVP.
Typical MVP samples show the presenter setting a view property (via some kind of IView interface), with the concrete view putting that property value into a textbox for example. Instead of this archaic approach, can one utilise INotifyPropertyChanged in MVP, and if so how? A very quick example would be really useful!
If I was to create a model that implemented INotifyPropertyChanged then isn't this more like MVVM? (i.e. the presenter updates the model, and via the magic of INotifyPropertyChanged the view gets updated). Yet everywhere I've read about MVVM and Winforms, people say it isn't suitable. Why? My understanding is that you can databind just about any control's property, so what's Winforms missing? I'm trying to understand the shortcomings of databinding in Winforms compared to WPF, and why MVVM can't be used, as it seems simpler to implement than MVP.
Thanks in advance
Andy.
I have just checked up how data binding in WinForms uses INotifyPropertyChanged.
The data binding through the BindingSource does really support INotifyPropertyChanged if the DataSource object of the BindingSource or model property corresponding to DataMember implements this. You can use M. Fowlers supervising presenter / controller to full extent here:
You don't even need a hand-written code, the BindingSource synchronizes the view with the model properties in both directions (model -> view and view -> model), and if the model supports INotifyPropertyChanged then the view will be updated automatically.
The code constructs I have used so far:
During view initialization:
this.bindingSource.DataSource = this.presenter;
Designer-generated code:
this.textBoxPhone.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.bindingSource, "Model.Phone", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
The model class:
public class Customer : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName == value)
return;
_firstName = value;
NotifyPropertyChanged("FirstName");
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (_lastName == value)
return;
_lastName = value;
NotifyPropertyChanged("LastName");
}
}
private string _company;
public string Company
{
get { return _company; }
set
{
if (_company == value)
return;
_company = value;
NotifyPropertyChanged("Company");
}
}
private string _phone;
public string Phone
{
get { return _phone; }
set
{
if (_phone == value)
return;
_phone = value;
NotifyPropertyChanged("Phone");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The presenter class:
public class CustomerPresenter
{
public CustomerPresenter(Customer model)
{
if (model == null)
throw new ArgumentNullException("model");
this.Model = model;
}
public Customer Model { get; set; }
public ICustomerView View { private get; set; }
}
Try to find examples of Supervising Controller MVP flavor, I use that with WinForms, very successfully I would say. The entities support INotifyPropertyChanged, presenter binds them to the view, and presenter subscribes to the PropertyChanged event so that it knows when view changed something (dirty checking). View is responsible only for binding data, all other functionality is moved to the presenter.
You don't miss anything. MVVM is very suitable with WinForms. Microsoft only encoureges the use of WPF and MVVM pattern with it.

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.

silverlight 3 collection binding

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!

Resources