How do I bind to an Event that has a delegate using Caliburn.Micro? - caliburn.micro

Usually I can bind to an event like so:
cal:Message.Attach="[Event DragStarting] = [Action OnDragStarting($source,$eventArgs)]"
That is when an event has a signature like so:
public event EventHandler<DragDropCancelEventArgs> DragStarting;
How do I Attach to an event that has a delegate like the following:
public event CanDropEventHandler IDropTargetCanDropElement;
public delegate bool CanDropEventHandler(object sender, DropEventArgs e);
Any help figuring this out would be greatly appreciated!.
Thanks,
S

Why this isn't possible right away ?
Because Caliburn.Micro uses the Blend SDK's interactivity features to convert the syntax ([Event DragStarting] = [Action OnDragStarting($source,$eventArgs)]) to an EventTrigger and the EventTrigger class can't work with events that return a value, and if you think about it is logical, what to do with that return value. In general it is a bad practice for events to return values and you rarely come across such events.
How to fix this ?
The easiest solution would be to change the delegate signature if you could to look like this:
public delegate void CanDropEventHandler(object sender, DropEventArgs eventArgs);
Then you convert that bool value returned to a public property on the DropEventArgs class, after that Caliburn.Micro syntax will work correctly.
What if you can't change the delegate's signature ?
Then you will have to create an Adapter by deriving from the type that owns that event or by wrapping that type if it is sealed and then declare a new event with the signature i showed you above, and then listen to the new event.
Is there any other way ?
Maybe you can create a custom event trigger that derives from TriggerBase<T> which can work with events that have return values but this is an advanced technique that is a bit hard to implement, and anyway you won't to able to use CM's syntax anymore.
Edit: What i mean by that make the DropEventArgs class have that property instead of returning it from the delegate, so the DropEventArgs becomes something like this:
class DropEventArgs : EventArgs {
public bool CanDrop {get; set;} // OR CHOOSE WHATEVER NAME YOU WANT
}
and the delegate becomes like i showed you above.

Related

Notifying that all properties have changed on a ViewModel

I am working on a Silverlight application using V3 SP1 of MVVM Light Toolkit.
My application is fully French/English. All UI elements (buttons, labels, etc.) and all the data (models). I need dynamic language switching and this is fully implemented and works with anything coming from a resource file. What I am struggling with is the ViewModels.
The Models have language specific prperties (DescriptionEn, DescriptionFr) and an additional property call LocalizedDescription which uses the current culture to return call the language specific property.
When the language changes (via a button click) I raise and broadcast (via the Messenger) a property changed event.
In each of my ViewModels, I register to receive the property changed message for the language swap.
I want to notify all the properties of the ViewModel that something has changed.
From: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
However, since the toolkit abstracts the raising of the changed event with RaisePropertyChanged(...) I cannot get this to work. I have also examined the source of the tookit and discovered that RaisePropertyChanged calls VerifyPropertyName(..) which in turn returns an error is the property does not belong to the ViewModel. I also noticed that the VerifyPropertyName method is attributed with Conditional("DEBUG"), but even if I choose the Release configuration, the ArgumentException("Property not found") is still raised.
Does anyone know of a way to get this to work using the toolkit aside from manually calling RaisePropertyChanged for every property of the ViewModel?
Follow-up:
Based on the comment from Simon, I attempted to create my own class that extends ViewModelBase. I looked at the source on CodePlex and decided to create a single method called RaiseAllPropertyChanged(). It would simply be a copy of the RaisePropertyChanged(string propertyName) but without the parameter and without the call to VerifyPropertyName(...). I cannot get it to work. Here is what I have.
public class ViewModelBaseExtended : ViewModelBase
{
protected void RaiseAllPropertyChanged()
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(String.Empty));
}
}
}
But I get a compiler error: The event 'GalaSoft.MvvmLight.ViewModelBase.PropertyChanged' can only appear on the left hand side of += or -=. This is a copy of the code that is used in the ViewModelBase.
Can someone offer some advice as to how to get this to work?
Solution:
I copied all the code from ViewModelBase into a new class. I then added the method RaisePropertyChanged() mentioned above which instantiates the PropertyChangedEventArgs class with String.Empty. This is now the new subclass for my ViewModels.
Thanks again to Simon for leading the way!
In case you're reading this in 2016, you can use ObservableObject and notify that all of the properties have changed by doing:
RaisePropertyChanged(string.Empty);
Unfortunately this is not possible with the current code-base of MVVMLight
In the short term your have 2 options:
User your own custom base class. And by custom base class I mean "Do not inherit from the MVVMLight class".
Download and compile MVVMLight in Release mode. This will force the "VerifyPropertyName" method to be excluded. Of course then you don't get the value of property name checks.
I am sure Laurent Bugnion will have this fixed soon.
A lighter solution to this problem would have been to override RaisePropertyChanged(string propertyName) in your class :
protected override void RaisePropertyChanged(string propertyName)
{
if (propertyName != null)
{
base.RaisePropertyChanged(propertyName);
}
else
{
var handler = PropertyChangedHandler;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(null));
}
}
}

Silverlight Custom Control and Databinding doesn't work properly in WP7

I'm trying to create my calendar control with databinding.
public partial class Calendar : UserControl
{
public static readonly DependencyProperty DateProperty =
DependencyProperty.Register("Date", typeof(DateTime),
typeof(Calendar), null);
public object Date
{
get { return GetValue(DateProperty); }
set
{
SetValue(DateProperty, value);
OnPropertyChanged("Date");
}
}
public Calendar()
{
// Required to initialize variables
InitializeComponent();
DayText.Text = ((DateTime)Date).ToString("dd");
MonthText.Text = ((DateTime)Date).ToString("MMM");
this.Loaded += new RoutedEventHandler(Calendar_Loaded);
this.GotFocus += new RoutedEventHandler(Calendar_Loaded);
}
void Calendar_Loaded(object sender, RoutedEventArgs e)
{
DayText.Text = ((DateTime)Date).ToString("dd");
MonthText.Text = ((DateTime)Date).ToString("MMM");
}
}
But When I create the listbox with this control, same calndar have the wrong date. I'm sure that the Date passed thorough databinding is correct but I don't understand why same calender show a different day (I'm noticed that is the day of a previous calendar control intance)
Thank you for supporting!
Hmm ... where do we start? Here's a few things I've noticed:
If you're using a dependency property, there's no need to call OnPropertyChanged from the Date property setter.
The dependency property declares the type as DateTime, but your public exposed property is of type object, which then requires you to cast it elsewhere.
If Calendar_Loaded is to be called in more situations than in response to the Loaded event (such as the GotFocus event, then I'd recommend that you call it something else, or create a method with a relevant name (e.g. UpdateDateParts) and call it from properly named separate event handlers.
Using fixed format specifiers when processing date strings does not localize well.
In addition to that, I'd suggest that you could implement the user interface in a manner that supports databinding (and re-templating) by using bindings and exposing the date parts of the Date dependency property instead of manually updating the Text property of some text blocks/boxes in event handlers. In fact, if you derive from Control instead of UserControl then you can create and actuall lookless control that has it's user interface defined by a style in themes\generic.xaml that can be re-defined by users of your control.
As for why the date is incorrect in different instances of your calendar control, we'd need to see some of your XAML/code to see how the control is being used and initialized to be able to provide a better answer. However, I thought the above was worth putting in an Answer, instead of trying to say it in a Comment.

Can somebody please explain this common binding pitfall to me? (using the wrong bindable event name)

I refer to this site link text
Using the wrong event name in the
[Bindable] tag can cause your
application to not bind your property,
and you will not even know why. When
you use the [Bindable] tag with a
custom name, the example below looks
like a good idea:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event=EVENT_CHANGED_CONST)]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
The code above assigns a static
property to the event name, and then
uses the same assignment to dispatch
the event. However, when the value
changes, the binding does not appear
to work. The reason is that the event
name will be EVENT_CHANGED_CONST and
not the value of the variable.
The code should have been written as
follows:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event="eventChangedConst")]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
I agree, the wrong example does look like a good idea and I would do it that way because I think it's the right way and avoids the possibility of a typing error. Why is the name of the constant used instead of it's value? Surely this can't be right?
I appreciate your insights
Because the standard Flex compiler isn't that clever at times... and I feel your pain! I've complained about this exact problem more than a few times.
If I remember correctly, it's because the compiler does multiple passes. One of the early passes changes the Metadata into AS code. At this point in the compiler it hasn't parsed the rest of the AS code, so its not capable of parsing Constants or references to static variables in other files.
The only thing I can suggest is sign up to the Adobe JIRA, vote for the bug, and hope that the compiler fixes in 4.5 bring some relief.

Questions about bindable in Flex

Since I found the webpages explaning the bindable propety quite confusing,so I would like to post my question here,which is quite simple,if I declare a variable to be bindable,does that mean whenever I changed the value of this variable in another class,all appearence of this variable will be synchronized to be the same value at the same time?
Say,if boolean variable "select" is declared to be bindable in Class A and default to be false,and we have an if statement in class A like if(select).
Then in another class,we changed the value of "select" to be true,will that if(select) statement pass the test ?
Also,how about the following setter method that is defined to be bindable:
[Bindable]
public function set isShowingAvg(b:Boolean):void
{
_isShowingAvg = b;
hasChanged();
}
Does this code imply that changing the value of _isShowingAvg is also going to be broadcasted?
Thanks in advance.
Thanks for your idea.
Declaring a property as Bindable means that when you change the value, an event will get broadcasted. This event enables data binding, but it's not necessarily automatic.
If the consuming class is MXML and you use brackets, like this:
<mx:Button enabled="{selected}" />
Then the MXML compiler will generate the appropriate binding code and anytime selected changes, enabled will also get changed.
If you're using it outside MXML then you'll either subscribe to the event to detect changes or use BindingUtils.
In your example I think you need to mark the getter [Bindable] and not the setter.
example:
public static const SHOWING_AVG_CHANGED:String = "showingAvgChangedEvent";
[Bindable(event="showingAvgChangedEvent")]
public function get isShowingAvg():Boolean
{
return _isShowingAvg;
}
public function set isShowingAvg(isShowing:Boolean):void
{
_isShowingAvg = isShowing;
dispatchEvent(new Event(SHOWING_AVG_CHANGED));
}

How can I emulate ErrorProvider in .NET Compact Framework?

Since there is no ErrorProvider class in .NETCF, how can I implement similar functionality (not necessarily exactly like ErrorProvider)?
I am using all the regular databinding constructs to bind controls to a datatable, using the DataRow.RowError property and DataRow.SetColumnError method, but I can't find events on any of DataTable, BindingManagerBase, etc. that I can hook into to receive any sort of notification.
Am I stuck calling a method to manually iterate through all the controls on my form and change some look/feel of the bound control?
Thanks,
MrB
The ErrorProvider class seems pretty basic - actually, a little too basic. If you have Red Gate Reflector, I would recommend disassembling the class and looking at it. Otherwise, create a Dictionary<Control, String>.
Here is a quick idea on creating your own provider:
Dictionary<Control, String> ErrorSet = new Dictionary<Control, String>();
public void SetError(Control control, String message)
{
// code for adding error information
ErrorSet.Add(control, message);
}
public String GetError(Control control)
{
// code for retrieving error information
return ErrorSet[control];
}
public String Clear()
{
// code for clearing all errors
}
I don't have R-G reflector here or I would provide more sample methods. But this ought to provide some sort of sample to work from.

Resources