Routed Events in WPF - using an Action delegate - wpf

I'm developing a user control, and wish to use a routed event. I notice that there are two delegates provided - RoutedEventHandler, and RoutedPropertyChangedEventHandler. The first that doesn't pass along any information, and the second that takes the old and new values of a property change. However, I need to pass just a single piece of information, so I want the equivalent of an Action delegate. Is there anything provided? Can I use an Action delegate?

Create a subclass of RoutedEventArgs to hold your additional data, and use EventHandler<T> with your args class. This will be convertible to RoutedEventHandler and the additional data will be available in your handlers.
You could create a generic RoutedEventArgs class that holds a single parameter of any type, but creating a new class usually makes the code easier to read and easier to modify to include more parameters in the future.
public class FooEventArgs
: RoutedEventArgs
{
// Declare additional data to pass here
public string Data { get; set; }
}
public class FooControl
: UserControl
{
public static readonly RoutedEvent FooEvent =
EventManager.RegisterRoutedEvent("Foo", RoutingStrategy.Bubble,
typeof(EventHandler<FooEventArgs>), typeof(FooControl));
public event EventHandler<FooEventArgs> Foo
{
add { AddHandler(FooEvent, value); }
remove { RemoveHandler(FooEvent, value); }
}
protected void OnFoo()
{
base.RaiseEvent(new FooEventArgs()
{
RoutedEvent = FooEvent,
// Supply the data here
Data = "data",
});
}
}

Related

Using a generic ViewModel

I have a WPF MVVM App, but I want my ViewModel to be generic. What the app is suppose to do is take some Data and do CRUD operations on it without knowing the Type of the data it's getting at the compile-time. So I declared my ViewModel like this:
public class GenericViewModel<T> where T : class
{
private void ConstructorBase()
{
Type theType = typeof(T);
Properties = theType.GetProperties().ToList();
}
public GenericViewModel(DbContext _dbContextInsert) //pravi novi repository na osnovu DbContexta
{
ConstructorBase();
_R = new RepositoryGlobal<T>(_dbContextInsert);
}
public T newT { get; set; }
public T selectedT { get; set; }
public List<PropertyInfo> Properties { get; set; }
private RepositoryGlobal<T> _R;
}
Now, disregard almost everything you see inside it, the only important thing is that the Constructor is never reached. I set this ViewModel as the DataContext for the main window like this:
InitializeComponent();
this.DataContext = new GenericViewModel<Person>(new PersonDbContext());
But when I put a breakpoint inside the ViewModel's constructor, the program never gets stopped.
Any ideas?
Dependencies should be abstractions, not implementations.
Your generic view model should not create it's own repository, instead you should pass in an instance of this dependency via the constructor.
public class GenericViewModel<T> where T : class
{
protected readonly IRepository<T> _Repository;
public GenericViewModel(IRepository<T> repository)
{
_Repository = repository;
}
...
}
You would then create an instance of your repository like so:
DbContext context = new PersonDbContext();
IRepository<Person> personRepo = new PersonRepository(context);
GenericViewModel<Person> personViewModel = new GenericViewModel<Person>(personRepo);
There, your View Model's dependencies are no longer tied to a specific implementation, your code is now far more adaptable to changes. Not to mention massively easier to test.

wpf - best practice of registering a DelegateCommand to a CompositeCommand

iv'e got a CompositeCommand exposed globally in my startup project
public static class Commands
{
public static readonly CompositeCommand DiceRolledCommand = new CompositeCommand();
}
in a ControlLibrary referenced by my startup project iv'e got a Control which has a DelegateCommand ,
each instance of this Control has to register it's Command with the globally exposed DiceRolledCommand.
what wold be the best practice of doing so :
here are 3 idea's of which the first 2 i don't like because they are a kinda of hack , where you take some programming component (dp) and alter it's use for your benefit , resulting in poor code and design .
1)
a regular decadency property of type CompositeCommand which will be set with DiceRolledCommand
and on it's CallBack register MyControl's DelegateCommand (OnDiceRolledCommand) .
public class MyControl : Control
{
public DelegateCommand<Tuple<int, int>> OnDiceRolledCommand { get; private set; }
public CompositeCommand GlobalDiceRolledCommand
{
get { return (CompositeCommand)GetValue(GlobalDiceRolledCommandProperty); }
set { SetValue(GlobalDiceRolledCommandProperty, value); }
}
public static readonly DependencyProperty GlobalDiceRolledCommandProperty =
DependencyProperty.Register("GlobalDiceRolledCommand", typeof(CompositeCommand), typeof(MyControl), new UIPropertyMetadata(null,GlobalDiceRolledCommandPropertyChanged));
private static void GlobalDiceRolledCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myControl= d as MyControl ;
var compoisteCommand = e.NewValue as CompositeCommand;
compoisteCommand.RegisterCommand(myControl.OnDiceRolledCommand);
}
}
<local:MyControl GlobalDiceRolledCommand="{x:Static local:Commands.DiceRolledCommand}"/>
i don't like this approach since it's a kind of manipulation where a Dependency Property is used has a Complex logical setter .
2) i could also do the same as in (1) using a third party class with an attached property which will register the OnDiceRolledCommand in an attached property's CallBack
public static class Commands
{
public static readonly CompositeCommand DiceRolledCommand = new CompositeCommand();
public static ICommand GetRegisterToDiceRolledCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(RegisterToDiceRolledCommandProperty);
}
public static void SetRegisterToDiceRolledCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(RegisterToDiceRolledCommandProperty, value);
}
public static readonly DependencyProperty RegisterToDiceRolledCommandProperty =
DependencyProperty.RegisterAttached("RegisterToDiceRolledCommand", typeof(ICommand), typeof(Commands), new UIPropertyMetadata(null,OnRegisterToDiceRolledCommandProperty);
private static void OnRegisterToDiceRolledCommandProperty(DependencyObject d , DependencyPropertyChangedEventArgs e)
{
var commandToRegister = e.newValue as DelegateCommand;
DiceRolledCommand.RegisterCommand(commandToRegister );
}
}
<local:MyContorl local:Commands.RegisterToDiceRolledCommand="{Binding OnDiceRolledCommand , RelativeSource={RelativeSource Self}}"/>
i also don't like this approach for the same reason as 1 ..
3) passing the composite command as a parameter to constructor , this approach is better since it keeps
the initializing logic in the constructor where it should be , i just can't figure out how to pass
an argument to a contractor through XAML , i'm not sure if it's even possible .
public class MyControl : Control
{
public MyControl(CompositeCommand globalDiceRolledCommand)
{
.........
globalDiceRolledCommand.Register(OnDiceRolledCommand);
}
}
<local:MyControl ..... >
Some how pass parameters to contractor in order to create the element in XAML
</local:MyControl>
to summarize :
A) any thoughts about (1) and (2) .
B) thoughts of how to accomplish 3 , and if it seems like good design .
C) Any good pattern of accomplishing this scenario.
thanks in advance .
Whenever I use Global Commands like that they are usually defined in either an Infrastructure class library which every library can reference. Or they are defined in a consuming core library that each module could reference directly.
I wrote a lot of this up in a Code Project article
Part 2 here

Does it make sense to directly use a business object's DependencyProperty's in the model?

With the following business object:
public class ItemsRow : BusinessObject<ItemsRow>
{
public static readonly DependencyProperty ItemIdProperty = DependencyProperty.Register("ItemId", typeof(int), typeof(ItemsRow));
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(ItemsRow));
public int ItemId
{
get { return (int)this.GetValue(ItemIdProperty); }
set { this.SetValue(ItemIdProperty, value); }
}
public string Description
{
get { return (string)this.GetValue(DescriptionProperty); }
set { this.SetValue(DescriptionProperty, value); }
}
}
How would you go about exposing the properties in a model, seeing as how the properties are already DependencyProperty's?
I was wondering if it would make any sense to do this:
public class ItemModel: DependencyObject
{
Item _item;
public ItemModel(Item item)
{
_item = item;
}
public static readonly DependencyProperty DescriptionProperty = Item.DescriptionProperty;
public string Description
{
get { return _item.Description; }
set { _item.Description = value; }
}
}
Would that work as intended or would the model necessarily have to have its own set of DependencyProperty's that are backed by the business object's DependencyProperty's? Or could this be modified slightly to work correctly?
That won't work because the dependency property registration needs to know on which type the property is being defined; that's why you pass the third argument to the register method. So far that reason alone, it won't work properly. But from a theoretical MVVM design stand point, having a separate object in your model that closely resembles your business object is a trade-off that you chose to have another layer of abstraction. You are essentially buying the redundancy to allow yourself to have another layer of abstraction allowing you to swap the business object without changing your model. However, if you make your model object dependent on specifics of your business object implementation, you are defeating that purpose. In that case, I would just directly use the "business object" as your model object.

Subscribe PropertyChanged events of window in C++/CLI

I just tried to subscribe to WPF property change events using C++/CLI. I didn't expect this to get difficult.
First I tried to subscribe to a specific property of some window (IsMouseDirectlyOver) and finally succeeded with following code:
void MyClass::DependencyPropertyChanged(Object^ sender, DependencyPropertyChangedEventArgs args)
{
Debug::WriteLine("DependencyPropertyChanged: "+sender->ToString()+", "+args.Property->Name);
}
window->IsMouseDirectlyOverChanged += gcnew DependencyPropertyChangedEventHandler(this, &MyClass::DependencyPropertyChanged);
Then I tried to subscribe to any property changes of an object (which is most important to me because my final code must be able to handle property changes by property names). I totally failed on this.
I tried various things but nothing worked. I could not find any C++/CLI examples but according to documentation and C# examples the following seemed to be the most sensible code to me:
window->PropertyChanged += gcnew PropertyChangedEventHandler(this, &MyClass::PropertyChanged);
void MyClass::PropertyChanged(Object^ sender, PropertyChangedEventArgs^ args)
{
...
}
But the compiler tells me by error C2039 that 'PropertyChangedEvent' is no element of 'System::Windows::Window'.
How can I achieve what I want?
Al mentioned in the comments, your code doesn't work, because there is no PropertyChanged event on Window, it's as simple as that.
What you can do instead is to override the OnPropertyChanged() method, which is present on a Window. In your override, you can do anything you want, including raising PropertyChanged (don't forget to create that event first).
I had a look on the snoop sources. I modified it and wrote a very, very basic example that works:
String^ ownerPropertyName = "IsActive";
DependencyObject^ propertyOwner = window;
DependencyPropertyDescriptor^ ownerPropertyDescriptor = DependencyPropertyDescriptor::FromName(ownerPropertyName, propertyOwner->GetType(), propertyOwner->GetType());
DependencyProperty^ ownerProperty = ownerPropertyDescriptor->DependencyProperty;
Type^ ownerPropertyType = ownerProperty->PropertyType;
DependencyProperty^ myProperty = DependencyProperty::Register(ownerPropertyName, ownerPropertyType, GetType(), gcnew PropertyMetadata(gcnew PropertyChangedCallback(&MyClass::BoundPropertyChangedCallback)));
Binding^ myPropertyToOwnerPropertyBinding = gcnew Binding(ownerPropertyName);
myPropertyToOwnerPropertyBinding->Mode = BindingMode::OneWay;
myPropertyToOwnerPropertyBinding->Source = propertyOwner;
BindingOperations::SetBinding(this, myProperty, myPropertyToOwnerPropertyBinding);
And:
static void BoundPropertyChangedCallback(DependencyObject^ me, DependencyPropertyChangedEventArgs args)
{
Debug::WriteLine("BoundPropertyChangedCallback: "+args.OldValue+", "+args.NewValue+", "+args.Property->Name);
}
Looks pretty complicated to me. I have no idea if that binding stuff is really necessary. In fact this can even subscribe to properties that do not have events (like IsMouseOver) and can operate on objects that do not implement INotifyPropertyChanged (like Window). And it does not need any switch/case for properties.
The class PropertyDescriptor (or the derived DependencyPropertyDescriptor) provides a mechanism to add a property change handler by their AddValueChanged method:
DependencyPropertyDescriptor^ propertyDescriptor = DependencyPropertyDescriptor::FromName(
"ActualWidth", component->GetType(), component->GetType());
propertyDescriptor->AddValueChanged(component, gcnew EventHandler(ActualWidthChanged));
...
static void ActualWidthChanged(Object^ component, EventArgs^ e)
{
...
}
Unfortunately the handler doesn't get passed the changed property, so i guess you would have to add different handlers for all properties you want to monitor.
EDIT: You might implement something like the code shown below that uses an anonymous delegate to pass the property name to an appropriate handler. Note however that this is C#, and to my understanding this can't be done in C++/CLI, since there it does not support managed lambdas. Mayby you could wrap a helper class like this in a separate assembly and use it from your C++/CLI code.
public delegate void PropertyChangedHandler(object component, string propertyName);
public static class DependencyPropertyDescriptorExt
{
public static void AddPropertyChangedHandler(
this object component, string propertyName, PropertyChangedHandler handler)
{
var propertyDescriptor = DependencyPropertyDescriptor.FromName(
propertyName, component.GetType(), component.GetType());
propertyDescriptor.AddValueChanged(component, (o, e) => handler(o, propertyName));
}
}
Now you could write and use such a PropertyChangedHandler like this:
this.AddPropertyChangedHandler("ActualHeight", PropertyChanged);
...
private void PropertyChanged(object component, string propertyName)
{
...
}

DependencyProperty.Register() usage?

I have 2 controls A and B that need to share a dependency property.
A has the property defined as:
public static readonly DependencyProperty PathProperty= DependencyProperty.Register("PathProperty", typeof(string), typeof(A),
new PropertyMetadata(string.Empty, OnPathChanged));
public string Path
{
get { return (string)GetValue(PathProperty); }
private set { SetValue(PathProperty, value); }
}
private static void OnPathChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs args)
{
//Dos something
}
Inside of class B,
I have
public static readonly DependencyProperty Path = A.PathProperty.AddOwner(typeof(B));
public string Path
{
get { return (string)GetValue(Path); }
set { SetValue(Path, value); }
}
Now, if I set the Dependency property Path on B explictly...(from code like Binstance.Path = "value" )
I would expect the OnPathChangedmethod to fire inside of A control?
Isnt that the expected behavior or am I missing something? How do I get this to work? ... i.e changing path property on B should fire OnPAthChanged on A
Thanks!
I think you've misunderstood the concept of DependencyProperties... Two separate controls do not receive updates of each other's events - nor does two Dependency-derived objects receive notifications of other objects' changes (E.g. if you have two textboxes - changing one's TextProperty, does nothing to the other). If you really want your second Control type to fire the static validation-callback - you need to make it public and call it in your registration of the DependencyProperty on class B. I wouldn't recommend it though - it gives you very tight coupling between the two classes that otherwise have nothing in common (as I understand your example).

Resources