How do I create a Routed Event in Silverlight 2? - silverlight

Anyone know how to create a routed event in Silverlight 2? In WPF, the code would be like below. However, there’s no EventManager in Silverlight.
public static readonly RoutedEvent ShowVideoEvent =
EventManager.RegisterRoutedEvent("ShowVideo", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(NavBar));
public event RoutedEventHandler ShowVideo
{
add { AddHandler(ShowVideoEvent, value); }
remove { RemoveHandler(ShowVideoEvent, value); }
}

At least for the time being, there doesn't seem to be a way to create your own. That post was however for Beta2, looking at the document for Beta2->RC0 breaking changes, there doesn't seem to be any mention of anything. But then I guess it could be no breaking change, we can always hope eh ;)
There are a number of events which are routed but again I'm not sure this documentation has been updated for RC0.

Related

What would be the best way to raise a MVVM property from a different ViewModel?

On my MainViewModel, I have a property that looks like this:
private bool _IsSkinNight = true;
public bool IsSkinNight
{
get { return _IsSkinNight; }
set
{
_IsSkinNight = value;
RaisePropertyChanged("IsSkinNight");
RaisePropertyChanged("WindowBackColor");
RaisePropertyChanged("StyleImage");
}
}
As you can see, I use this one property to raise other properties. It makes changing the UI much easier. Especially as I am going to add more items to it.
However, I have a property on a WPF page that I need to update as well. But since it's on a different page, its ViewModel is separate as well. So this property can't call RaisePropertyChanged on it and the property in the page can't check the state of IsSkinNight.
So what would be the best way to cross between the different ViewModels? I'll be adding more pages. So is there a way to make like a universal property that all ViewModels can access?
Thanks
Even if I don't like using it, you probably need to look up for EventAgregators.
Basically you will be able to fire an event in your MainViewModel, and in your other view models you will be able to register to one or more event of this agregator.
However, I strongly recommend that you use it very lightly, because it can become extremely difficult to Debug, since there is no call stack when you fire an event like that.
Thanks for the help everyone. I did a bit more research, based on the suggestions here, and came across MVVM Messages.
Using this blog post, I was able to use MVVM messages to achieve my goal. Thanks again.
In such a case, I sometimes create a kind of Context class, to keep track of "global" properties. This class has to be registered as a Singleton.
public class SkinContext : ISkinContext {
private bool _IsSkinNight = true;
public bool IsSkinNight
{
get { return _IsSkinNight; }
set
{
_IsSkinNight = value;
RaisePropertyChanged("IsSkinNight");
RaisePropertyChanged("WindowBackColor");
RaisePropertyChanged("StyleImage");
}
}
then I inject this class in each ViewModel that needs to be aware of the context and subscribe to the NotifyPropertyChanged event (Or a custom event that you created):
public class SomeViewModel {
public SomeViewModel(ISkinContext context){
context.OnPropertyChanged += (s,e) => { /*Raise other notification and whatnot*/ }
}
}

Why is there no IsReadOnlyChanged event on TextBox controls?

I was adding some workaround code to fix the bug outlined in Is this a bug in DotNet 4 WPF Spell Checking?, (When a WPF TextBox changes Enabled, Visible or ReadOnly states, any SpellCheck custom dictionaries get dropped off until you disable and re-enable SpellCheck) and the simplest fix seemed to be to handle the IsVisibleChanged, IsEnabledChanged, and IsReadOnlyChanged events.
Simple, right? Except there is no IsReadOnlyChanged event. Anybody know why and what the best way to trap a change to IsReadOnly in a WPF TextBox would be?
You can always follow dependency property change with DependencyPropertyDescriptor.AddValueChanged
DependencyPropertyDescriptor.FromProperty(TextBoxBase.IsReadOnlyProperty)
.AddValueChanged(ctrl, OnReadOnlyChanged)
Create a custom class and handle OnPropertyChanged event. Sth like this:
public class MyTextBox: TextBox
{
public MyTextBox() { }
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property.ToString() == "IsReadOnly")
{
// here you are sure that ContentPropertyhas changed
}
}
}

Difference between normal customized event the static RoutedEvent

Please check the code below which defines the same scroll event in two different ways:
---------------------------------normal way--------------------------------------
public event RoutedEventHandler CloseTab;
------------------------------static RoutedEvent---------------------------------
public static readonly RoutedEvent CloseTabEvent =
EventManager.RegisterRoutedEvent("CloseTab", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(CloseableTabItem));
public event RoutedEventHandler CloseTab
{
add { AddHandler(CloseTabEvent, value); }
remove { RemoveHandler(CloseTabEvent, value); }
}
1.What's the difference between the normal customized event and the static RoutedEvent?
2.Why the event in the first way can be raised by a method like "if(CloseTab!=null){CloseTab(o,e);}",but in the second way ,it only can be raised by the UIElement.RaiseEvent() method?
3.I know this question must be very very silly,but it dose make me confused.
It is that Why the event in the first way can be raised by the "if(CloseTab!=null){CloseTab(o,e);}" method only in the class which the event is defined?And when it is outside the owner class,the event can only add or remove handlers with "+=/-=" ?
Thanks for your time!!
You implementation basics i.e. the way we raise each one differently, are correct.
But apart from that, as far as my knowldge on routed events goes...
The first is NOT a routed event. It is a simple CLR event declared with its type as the RoutedEventHandler delegate. When this event is raised it will not bubble \ tunnel up to the ancestor/child UI elements respectively.
At practical level, I guess if you try using the first one in EventTrigger it will not work.

What is the best way to handel click-events in MVVM?

What is the best way to handel click-events in MVVM? Are there a best way?
I have found two solutions:
with a relaycommand:
RelayCommand buttonAddCategory_Click;
public ICommand ButtonAddCategory_Click
{
get
{
return buttonAddCategory_Click ?? (buttonAddCategory_Click = new RelayCommand(param => this.AddCategory(),
param => true));
}
}
pro: ?; contra: need workaround with events if i would change ui elements like focus
with attached behaviour:
public static bool GetIsResetMouseLeftButtonDown(TreeView treeView)
{
return (bool)treeView.GetValue(IsResetMouseLeftButtonDownProperty);
}
public static void SetIsResetMouseLeftButtonDown(TreeView treeViewItem, bool value)
{
treeViewItem.SetValue(IsResetMouseLeftButtonDownProperty, value);
}
public static readonly DependencyProperty IsResetMouseLeftButtonDownProperty =
DependencyProperty.RegisterAttached("PreviewMouseLeftButtonDown", typeof(bool), typeof(TreeViewBehavior),
new UIPropertyMetadata(false, OnIsMouseLeftButtonDownChanged));
pro: you have RoutedEventArgs for changes on the ui; contra: access to other controls?
Right now i use both solutions. The RellayCommand in Buttons (with events for ui updates) and the attached behaviour for a treeview to deselect the treeviewitem if a user clicks.
To me there is no simple answer to this question.
That's the way I see it:
if you have a defined state-change on the VM, expose a RelayCommand which then can be bound to something the triggers it. In 99,9% percent of the cases this is a button/menu-entry. Something where it can be easily used. The cases that are left -> well some workaround might be needed, like calling a method from the view.
So a RelayCommand should imho be used if you are really targeting the VM.
Focus-changes on the other hand are view-related functionality. Imho this has nothing todo with the WM. That means for me it should be implemented in the view. So to me I'd even go for a straight-forward eventhandler that does the job.
hth,
Martin
I like this idea:
UI logic, such as opening new windows, showing/hiding elements, etc. You keep that on the code-behind.
When this 'click' should do something with the model, invoke the action.
So, a button that closes the window and saves something would be defined like this:
<Button Name="SaveBtnr" VerticalAlignment="Bottom"
Command="{Binding Save}" Click="OnSaveClick"
CommandParameter="{Binding}">Save</Button>
And the handler would be:
private void OnSaveClick(object sender, RoutedEventArgs e)
{
//Do UI Stuff
}
And then your command:
public void SaveCommand(object parameter)
{
//SaveStuff
}

WPF XAML Intellisense doesn't work correctly for custom routed event handlers

I defined a custom routed event with the following constructs (names changed):
public class MyRoutedEventArgs : RoutedEventArgs
{...}
public delegate void MyRoutedEventHandler(Object sender, MyRoutedEventArgs args);
public static readonly RoutedEvent MyEvent;
MyClass.MyEvent =
EventManager.RegisterRoutedEvent("MyEvent", RoutingStrategy.Tunnel, typeof(MyRoutedEventHandler), typeof(MyClass));
Next, I'm creating a CLR event wrapper:
public event MyRoutedEventHandler MyEvent {
add { AddHandler(MyEvent, value); }
remove { RemoveHandler(MyEvent, value); }
}
The problem is when I define it as shown above, XAML intellisense doesn't work for autogenerating the handler method body. What I noticed is that if you change your CLR event wrapper to use generic RoutedEventHandler type, everything works! However in this case, the auto-generated method gets a generic RoutedEventArgs (well, correctly corresponding to RoutedEventHandler), which forces me to manually rewrite it as MyRoutedEventArgs.
I think this is currently a limitation you have to live with. But all you have to do is, once the eventhandler for the generic type was autogenerated, change the signature of the generated method to use MyRoutedEventArgs instead of RoutedEventArgs. Allthough still ugly (we shouldn't be forced to do this) I would think it is not such a big problem.
Anyone tried it with VS 2010 ?

Resources