I'm trying to use the MVVM pattern in my Silverlight 3 application and am having problems getting binding to a command property of a view model working. First off, I'm trying to add an attached property called ClickCommand, like this:
public static class Command
{
public static readonly DependencyProperty ClickCommandProperty =
DependencyProperty.RegisterAttached(
"ClickCommand", typeof(Command<RoutedEventHandler>),
typeof(Command), null);
public static Command<RoutedEventHandler> GetClickCommand(
DependencyObject target)
{
return target.GetValue(ClickCommandProperty)
as Command<RoutedEventHandler>;
}
public static void SetClickCommand(
DependencyObject target, Command<RoutedEventHandler> value)
{
// Breakpoints here are never reached
var btn = target as ButtonBase;
if (btn != null)
{
var oldValue = GetClickCommand(target);
btn.Click -= oldValue.Action;
target.SetValue(ClickCommandProperty, value);
btn.Click += value.Action;
}
}
}
The generic Command class is a wrapper around a delegate. I'm only wrapping a delegate because I wondered if having a delegate type for a property was the reason things weren't working for me originally. Here's that class:
public class Command<T> /* I'm not allowed to constrain T to a delegate type */
{
public Command(T action)
{
this.Action = action;
}
public T Action { get; set; }
}
Here's how I am using the attached property:
<Button u:Command.ClickCommand="{Binding DoThatThing}" Content="New"/>
The syntax seems to be accepted, and I think that when I tested all of this with a string property type, that worked fine. Here's the view model class that is being bound to:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public Command<RoutedEventHandler> DoThatThing
{
get
{
return new Command<RoutedEventHandler>(
(s, e) => Debug.WriteLine("Never output!"));
}
}
}
The delegate contained in the Command property is never invoked. Also, when I place breakpoints in the getter and setter of the attached property, they are never reached.
In trying to isolate the problem, I changing the property type to string; the breakpoint in the getter and setter was also never reached, yet throwing an exception in them did cause the application to terminate, so I am thinking it's a framework eccentricity.
Why is this stuff not working? I also welcome alternate, hopefully simpler ways to bind event handlers to view models.
You have at least two problems here.
First, you are relying on the SetXxx method being executed. The CLR wrappers for dependency properties (the property setter or SetXxx method) are not executed when the DP is set from XAML; rather, WPF sets the value of the internally managed DP "slot" directly. (This also explains why your breakpoints were never hit.) Therefore, your logic for handling changes must always occur in the OnXxxChanged callback, not in the setter; WPF will call that callback for you when the property changes regardless of where that change comes from. Thus (example taken from a slightly different implementation of commands, but should give you the idea):
// Note callback in PropertyMetadata
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(Click),
new PropertyMetadata(OnCommandChanged));
// GetXxx and SetXxx wrappers contain boilerplate only
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
// WPF will call the following when the property is set, even when it's set in XAML
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ButtonBase button = d as ButtonBase;
if (button != null)
{
// do something with button.Click here
}
}
Second, even with this change, setting ClickCommand on a control that doesn't already have a value set will cause an exception, because oldValue is null and therefore oldValue.Action causes a NullReferenceException. You need to check for this case (you should also check for newValue == null though this is unlikely ever to happen).
Related
I'm trying to use a class to simplify dynamically binding and unbinding a method callback to a delegate. When the Bound property is changed to a different value, the callback is added or removed from the actionEvent delegate depending on whether bound is true or false.
public class Binding
{
private bool bound;
private Action actionEvent;
private Action callback;
//Constructor
public Binding(ref Action actionEvent, Action callback, bool isBound = true)
{
this.actionEvent = actionEvent; //actionEvent is either being cloned here, or whenever it's referenced
this.callback = callback;
this.bound = isBound;
if (this.bound)
{
Debug.Log("Binding callback to event!");
this.actionEvent += this.callback; //This is a reference to the plrInput class
}
}
//Bound property binds and unbinds the callback function to the event when changed
public bool Bound
{
get { return bound; }
set
{
if (bound != value)
{
bound = value;
if (bound)
{
Debug.Log("Rebinding callback!");
actionEvent += callback;
return;
}
actionEvent -= callback;
Debug.Log("Unbinding callback!");
}
}
}
}
The class is instantiated in the start method like so:
void Start
{
Binding JumpBinding = new Binding(ref plrInput.OnSpacePressed, Jump);
// plrInput.OnSpacePressed is a delegate from another class which is invoked when I press space
// Jump is a method which causes the player to jump
}
I'm new to c# and I learned about delegates and the ref keyword yesterday, so maybe my problem stems from a misunderstanding. The problem I'm running into is that the class member actionEvent in Binding is just a clone of the actionEvent argument passed through the constructor, even though I pass it by reference. I was able to confirm this through testing.
When I invoke plrInput.OnSpacePressed while the callback is bound to the Binding class member actionEvent, the callback is not being called. Does anyone have an explanation for why I'm getting this behavior, and how I can potentially fix this problem or if there's a better approach?
In my View I'm using a component (custom control), which provides some functions. I want to invoke one of them when my ViewModel receives an event it is subscribed to.
I want to do this as cleanly as possible, since there might be more functions I would be using this way.
I know I can create a variable like "InvokeFunctionA", bind to this variable and create OnChange method in my View which will invoke the corresponding function. But it's quite a lot of code required just to invoke a single function. And an extra variable, which seems quite unnesessary, too.
Is there a better way to do this? Like, maybe a View can pass some kind of a handler function to ViewModel which will do the work? I've made quite a lot of research but haven't yet found anything that suits my problem. Or maybe I'm missing something obvious?
[ edit ]
Haukinger solution works for now (done this way: https://blog.machinezoo.com/expose-wpf-control-to-view-model-iii ), but I don't think it's the cleanest solution (Instead of providing access to a few functions, I'm exposing whole control to the ViewModel).
In a perfect MVVM-world (as you are asking for a clean solution), the ViewModel does not call anything that is located in the view (neither directly nor indirectly). I'd approach the problem like this:
If 'component' is NOT a usercontrol, try moving it to the ViewModel and use bindings or commands in the view to operate your 'component'.
If 'component' is a usercontrol, give 'component' a dependency property and fill it via a binding with your property of the ViewModel. Inside of 'compontent' you can register value change callback of your dependency property to start your work. <local:UserControlComponent MyDependencyProperty="{Binding PropertyInViewModel}" />
As a last resort:
You could add a C# event to the viewmodel and handle it in your code-behind inside the view.
Instead of an event, you could alternatively use IObservable pattern (https://learn.microsoft.com/en-us/dotnet/api/system.iobservable-1?view=netframework-4.8, https://github.com/dotnet/reactive)
For completeness sake a no-go option: Prism has an EventAggregator that can be used for loose communication. I've had to remove the usage of EventAggregator from a rather big App, because it was not maintainable any more.
Expose a dependency property in your view whose type is the provided interface, bind it to a property on your view model, then call the method on the interface on the view model property from the view model.
To clarify, I don't mean to expose the component itself, rather an interface that contains exactly one method. The view has to have a private class that implements the interface and routes to the actual component, as well as converting arguments and results so that types belonging to the components need not be present in the interface.
But I'm with sa.he in that this whole situation should be avoided in the first place. It may not be possible, depending on the third party components used, though.
Yes, invoking view's methods from VM is very much against pure MVVM and there's not going to be a 'clean' solution.
But it can be done at least half decently. You would need to create a special attached property (or behavior, but property seems to be a better choice in this scenario) and an ICommand property in VM, then bind the AP to the property with OneWayToSource binding and use command invocation in VM. It would still be a lot of code, but once it's done, you would only need to create new properties in the VM.
Below is some code that I wrote, consider it as a starting point, you can add support for command parameters and converters.
public class MethodDelegation : DependencyObject
{
public static readonly DependencyProperty CommandDelegatesProperty =
DependencyProperty.RegisterAttached("CommandDelegatesInternal", typeof(CommandDelegatesCollection), typeof(MethodDelegation), new PropertyMetadata(null));
private MethodDelegation() { }
public static CommandDelegatesCollection GetCommandDelegates(DependencyObject obj)
{
if (obj.GetValue(CommandDelegatesProperty) is null)
{
SetCommandDelegates(obj, new CommandDelegatesCollection(obj));
}
return (CommandDelegatesCollection)obj.GetValue(CommandDelegatesProperty);
}
public static void SetCommandDelegates(DependencyObject obj, CommandDelegatesCollection value)
{
obj.SetValue(CommandDelegatesProperty, value);
}
}
public class CommandDelegatesCollection : FreezableCollection<CommandDelegate>
{
public CommandDelegatesCollection()
{
}
public CommandDelegatesCollection(DependencyObject targetObject)
{
TargetObject = targetObject;
((INotifyCollectionChanged)this).CollectionChanged += UpdateDelegatesTargetObjects;
}
public DependencyObject TargetObject { get; }
protected override Freezable CreateInstanceCore()
{
return new CommandDelegatesCollection();
}
private void UpdateDelegatesTargetObjects(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (CommandDelegate commandDelegate in e?.NewItems ?? Array.Empty<CommandDelegate>())
{
commandDelegate.TargetObject = TargetObject;
}
}
}
public class CommandDelegate : Freezable
{
public static readonly DependencyProperty MethodNameProperty =
DependencyProperty.Register("MethodName", typeof(string), typeof(CommandDelegate), new PropertyMetadata(string.Empty, MethodName_Changed));
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandDelegate), new PropertyMetadata(null));
public static readonly DependencyProperty TargetObjectProperty =
DependencyProperty.Register("TargetObject", typeof(DependencyObject), typeof(CommandDelegate), new PropertyMetadata(null, TargetObject_Changed));
private MethodInfo _method;
public string MethodName
{
get { return (string)GetValue(MethodNameProperty); }
set { SetValue(MethodNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public DependencyObject TargetObject
{
get { return (DependencyObject)GetValue(TargetObjectProperty); }
set { SetValue(TargetObjectProperty, value); }
}
protected override Freezable CreateInstanceCore()
{
return new CommandDelegate();
}
private static void MethodName_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var del = (CommandDelegate)d;
del.UpdateMethod();
del.UpdateCommand();
}
private static void TargetObject_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var del = (CommandDelegate)d;
del.UpdateMethod();
del.UpdateCommand();
}
private void UpdateMethod()
{
_method = TargetObject?.GetType()?.GetMethod(MethodName);
}
private void UpdateCommand()
{
Command = new RelayCommand(() => _method.Invoke(TargetObject, Array.Empty<object>()));
}
}
The XAML usage is as follows:
<TextBox>
<l:MethodDelegation.CommandDelegates>
<l:CommandDelegate MethodName="Focus"
Command="{Binding TestCommand, Mode=OneWayToSource}" />
</l:MethodDelegation.CommandDelegates>
</TextBox>
Bubble your event upwards. Have your VM publish some event of its own. Your V can subscribe to it (if it wishes).
The downside is that you'll need codebehind, where ideally a V should be XAML-only as far as possible. The upside is that your VM remains quite aloof (i.e. it's not dependent on any specific controls used by the V). It says "something has happened worthy of note", but it doesn't assume either that (a) anyone is particularly listening, or (b) it leaves it to the listener (in your case, the V) to decide exactly what to action to take (i.e. how to change the UI).
It's a perennial problem - how does a VM cause a V to update somehow, and as far as I can tell it is still something to be debated.
The mechanism above, I've got a vague recollection that Prism itself might include something similar. I'm fairly sure it uses something akin to INotifyPropertyChanged (i.e. some interface or other) rather than an "event" as we might understand it just from a working knowledge of .net. You might even be able to use this mechanism to dispense with codebehind altogether. The downside of using Prism in the first place is its bulk, but if you're already using it anyway...
It's for you to decide how clean this is. I decided that a bit of codebehind was preferable to the VM meddling directly with the UI.
I want to create something like late binding for a DependencyProperty.
I have a ListView with icons. I want icons loaded only when they are actually needed / displayed. When the icon elements are displayed, GetValue on IconProperty is called, but it only can return default value (which is null). I want to inject code to load relevant icon when initial value is null.
My first approach was creating custom getter / setter for the property without using DependencyProperty at all. It works, however I wonder if it's optimal.
When I use DependencyProperty I can easily determine when it's changed via OnPropertyChanged override. I have no idea when should I inject initialization for getter.
public class DisplayItem : DependencyObject {
// ...
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(
"Icon",
typeof(ImageSource),
typeof(DisplayItem),
null
);
public ImageSource Icon {
get { return (ImageSource)GetValue(IconProperty); }
private set { SetValue(IconProperty, value); }
}
private void GetIcon() {
// Some code to actually fetch the icon image...
// ...
Icon = loadedImageSource;
}
// ...
}
Consider the code above: how to call GetIcon() exactly before first GetValue() occurs?
Do not use a dependency property.
A plain CLR property (with an optional INotifyPropertyChanged implementation) is sufficient:
public class DisplayItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ImageSource icon;
public ImageSource Icon
{
get
{
if (icon == null)
{
icon = ... // load here
}
return icon;
}
private set
{
icon = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Icon"));
}
}
}
Why do you want to know when the dependency property value is being accessed?
If bound to some property in the view, the property will be accessed on initially loading the components. Hence, you can call that GetIcon() when on Loaded. If you’re using MVVM, just bind the Loaded event to some command, else simply handle the event and call the function.
If you’re planning to move to MVVM pattern, just using a CLR property as the other answer suggests, would do the trick.
I have a ViewModel like this
Public class AboutPageViewModel
{
public AboutPageViewModel()
{
AppName = Settings.MyAppName;
}
private string _appName;
public string AppName
{
get{return _appName;}
set{_appName = value; RaisePropertyChanged("AppName");}
}
}
Now in a static class
public static class Settings
{
public static string MyAppName{get;set;} = "LOL"
}
How do I notify the ViewModel everytime MyAppName is changed, and update it to the Binded UI?
Thanks!
As you define it in your question, Settings isn't a static class (ah, I see in comments that was a typo, and it's static in your code). It should not be static. PropertyChanged notifications on a static class are theoretically possible but it's not worth your time to mess with, and there's no need to bother.
Have Settings implement INotifyPropertyChanged, just like your viewmodel. When MyAppName changes, Settings should raise PropertyChanged, just as AboutPageViewModel does when its own AppName property changes.
Now give Settings a static property called Instance:
public static Settings Instance { get; private set; }
static Settings()
{
Instance = new Settings();
}
And handle its PropertyChanged event in AboutPageViewModel:
public AboutPageViewModel()
{
AppName = Settings.Instance.MyAppName;
Settings.Instance.PropertyChanged += (s,e) =>
{
// If you're in C#6:
//if (e.PropertyName == nameof(Settings.MyAppName))
if (e.PropertyName == "MyAppName")
{
AppName = Settings.Instance.MyAppName;
}
}
}
Option Number Two
Arguably a better option; I've done it this way more than once.
In comments, #MikeEason makes the very good point that this could also be done with a custom *Changed event such as MyAppNameChanged, which has two advantages: It lets you go back to a static class, and it lets you skip the check on the property name, which is extra code and also a "magic string". Working with INotifyPropertyChanged we get a little bit numb to the danger of magic strings, but they are in fact bad. If you're in C#6, you can and absolutely should use the nameof() operator, but not all of us are in C#6 just yet. My main responsibility at work is an application that we're hoping to migrate to C#6 this summer.
public static event EventHandler<String> MyAppNameChanged;
private static String _myAppName = "";
public static String MyAppName {
get { return _myAppName; }
set {
if (_myAppName != value)
{
_myAppName = value;
// C#6 again. Note (thanks OP!) you can't pass this for sender
// in a static property.
MyAppNameChanged?.Invoke(null, value);
}
}
}
The drawback of this is that, well, this class is called Settings, not Setting. Maybe it's got a dozen properties changing here and there. That gets to be a real thicket of distinct property-changed events ("so what?" you may ask -- and you may have a point). My tendency is to stick with PropertyChanged if there's a whole sheaf of them, and to add an event if the class has only one or two important properties that somebody needs to keep an eye on. Either way is annoying in my view; try both and you'll eventually settle on a preference.
You don't need to store value in ViewModel if you already have it somewhere (I assume what you are not going to change it in ViewModel itself):
public class AboutPageViewModel : INotifyPropertyChanged
{
public string AppName => Settings.MyAppName;
}
And as for View to know when this property is changed you need 2 things: 1) there should be a way to inform ViewModel when value is changed 2) rise PropertyChanged(nameof(AppName)) (notice INotifyPropertyChanged).
Several possibilities to make it:
Settings should rise event when MyAppName value is changed, ViewModel subscribe to it and rises PropertyChanged;
Store initial value, check periodically if value is changed;
Use another type which implement INotifyPropertyChanged, bind to that type property instead, this will update view automatically if that type rises PropertyChanged.
You have to implement INotifyPropertyChanged interface on Settings class!
then use the same piece of code like this:
private string _myAppName;
public string MyAppName
{
get{return _myAppName;}
set{_appName = value; RaisePropertyChanged("MyAppName");}
}
I followed this tutorial but I couldn't apply what I learned to my project.
I have a LineGraph object (Dynamic Data Display) and I want to create an event that is raised when the thickness of the LineGraph is equal to 0.
How am I supposed to write it following this tutorial ?
Here is how I would do it with a RoutedEvent:
Create a class that derives from LineGraph, let's say CustomLineGraph:
public class CustomLineGraph : LineGraph {
}
Create our routed event like this:
public class CustomLineGraph : LineGraph {
public static readonly RoutedEvent ThicknessEvent = EventManager.RegisterRoutedEvent("Thickness", RoutingStrategy.Bubble, typeof(RoutedEventHandler, typeof(CustomLineGraph));
// .NET event wrapper
public event RoutedEventHandler Thickness
{
add { AddHandler(CustomLineGraph.ThicknessEvent, value); }
remove { RemoveHandler(CustomLineGraph.ThicknessEvent, value); }
}
}
Now we override the StrokeThickness property so we can raise our custom routed event when the value of that property is 0.
public class CustomLineGraph : LineGraph {
public static readonly RoutedEvent ThicknessEvent = EventManager.RegisterRoutedEvent("Thickness", RoutingStrategy.Bubble, typeof(RoutedEventHandler, typeof(CustomLineGraph));
// .NET event wrapper
public event RoutedEventHandler Thickness
{
add { AddHandler(CustomLineGraph.ThicknessEvent, value); }
remove { RemoveHandler(CustomLineGraph.ThicknessEvent, value); }
}
public override double StrokeThickness {
get { return base.StrokeThickness; }
set
{
base.StrokeThickness = value;
if (value == 0)
RaiseEvent(new RoutedEventArgs(CustomLineGraph.ThicknessEvent, this));
}
}
}
We are done !
Personally, I usually avoid creating events, preferring instead to create delegates. If there is some particular reason that you specifically need an event, then please ignore this answer. The reasons that I prefer to use delegates are that you don't need to create additional EventArgs classes and I can also set my own parameter types.
First, let's create a delegate:
public delegate void TypeOfDelegate(YourDataType dataInstance);
Now a getter and setter:
public TypeOfDelegate DelegateProperty { get; set; }
Now let's create a method that matches the in and out parameters of the delegate:
public void CanBeCalledAnything(YourDataType dataInstance)
{
// do something with the dataInstance parameter
}
Now we can set this method as one (of many) handlers for this delegate:
DelegateProperty += CanBeCalledAnything;
Finally, let's call our delegate... this is equivalent to raising the event:
if (DelegateProperty != null) DelegateProperty(dataInstanceOfTypeYourDataType);
Note the important check for null. So that's it! If you want more or less parameters, just add or remove them from the delegate declaration and the handling method... simple.