c# wpf - Object reference not set to an instance of an object - c#

I don't know whats wrong with my code and how can i solve it.
public class ExampleViewModel<T> : ViewModelBase where T : IAppointment
{
private Uri appointmentsSource;
private ObservableCollection<T> appointments;
public ICommand AppointmentCreatedCommand { get; set; }
public Uri AppointmentsSource
{
get { return this.appointmentsSource; }
set { this.appointmentsSource = value; }
}
public ExampleViewModel()
{
this.AppointmentCreatedCommand = new DelegateCommand(OnAppointmentCreatedCommandExecute);
}
private void OnAppointmentCreatedCommandExecute(object obj)
{
var createdAppointment = ((AppointmentCreatedEventArgs)(obj)).CreatedAppointment as Appointment;
ObservableAppointmentCollection apps = System.Windows.Markup.XamlReader.Load(File.OpenRead("../../Appointments.xaml")) as ObservableAppointmentCollection;
apps.Add(createdAppointment);
File.WriteAllText("../../Appointments.xaml", System.Windows.Markup.XamlWriter.Save(apps));
string text = File.ReadAllText("../../Appointments.xaml");
text = text.Replace("<Appointment.TimeZone><s:TimeZoneInfo /></Appointment.TimeZone>", " ");
File.WriteAllText("../../Appointments.xaml", text);
}
public ObservableCollection<T> Appointments
{
get
{
if (this.appointments == null)
{
this.appointments = new ObservableCollection<T>(LoadAppointmentsSource(this.AppointmentsSource));
}
return this.appointments;
}
}
protected static IEnumerable<T> LoadAppointmentsSource(Uri appointmentsSource)
{
if (appointmentsSource != null)
{
IEnumerable<T> appointments = Application.LoadComponent(appointmentsSource) as IEnumerable<T>;
return appointments;
}
return Enumerable.Empty<T>();
}
private static DateTime GetStart(T a)
{
return a.Start.Date;
}
}
XAML
<i:Interaction.Triggers>
<i:EventTrigger EventName="AppointmentCreated">
<i:InvokeCommandAction Command="{Binding AppointmentCreatedCommand, Mode=TwoWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
there is an error
NullReferenceException was unhandled by user code
Object reference not set to an instance of an object.
on
var createdAppointment = ((AppointmentCreatedEventArgs)(obj)).CreatedAppointment as Appointment;
everytime i create appointment in my RadScheduleView (Telerik), it was supposed to create appointment then write it in the Appointments.xaml.

Seeing your XAML and the types you are using in your command, I think you are confusing Commands and Events.
Commands are a way to execute a method upon one single predetermined action, usually hitting a button (typically by a click, a tap or a key stroke) whereas events are a way for an object to react to all sorts of conditions. Typically events expose many events, whereas there can be only one single command on a user control.
Now, the big difference here is that on an event, the sender populates and passes an eventArgs object, containing details on the nature of the situation which triggered the event. As for the command, this does not happen. It is possible to pass a parameter to the method which will handle the execution of a command when triggered, but you have to choose that object yourself. The way you choose the object to be passed is by means of a data binding, pretty much like the Command itself is bound, but with the CommandParameter attribute instead of Command.
CommandParameter="{Binding ...}"

In this line :
var createdAppointment = ((AppointmentCreatedEventArgs)(obj)).CreatedAppointment as Appointment;
Check for null value in obj , and CreatedAppointment. It looks like either of them are not having their values set.

Related

Method Passing ViewModel vs Passing Bound Variables

I have a ComboBox bound to a ViewModel string Quality_SelectedItem.
And I have a Method named Quality, which inside accesses the value of the SelectedItem in an if statement.
I have two ways of accessing the value, by passing the ViewModel through the Method, or by passing the string Quality_SelectedItem.
Which way should I be using it and which performs faster?
XAML
<ComboBox x:Name="cboQuality"
ItemsSource="{Binding Quality_Items}"
SelectedItem="{Binding Quality_SelectedItem, Mode=TwoWay}"
HorizontalAlignment="Left"
Margin="0,2,0,0"
VerticalAlignment="Top"
Width="105"
Height="22"/>
ViewModel Class
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void OnPropertyChanged(string prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
// Quality Selected Item
private string _Quality_SelectedItem { get; set; }
public string Quality_SelectedItem
{
get { return _Quality_SelectedItem; }
set
{
if (_Quality_SelectedItem == value)
{
return;
}
_Quality_SelectedItem = value;
OnPropertyChanged("Quality_SelectedItem");
}
}
...
Example 1 - Passing ViewModel
In the Quality Method, I access vm.Quality_SelectedItem directly from the if statement.
public ViewModel vm = new ViewModel();
// MainWindow
public MainWindow()
{
InitializeComponent();
DataContext = vm;
// Quality Method
Quality(vm); // <---
}
// Quality Method
public static void Quality(ViewModel vm)
{
if (vm.Quality_SelectedItem == "High")
{
// do something
}
else if (vm.Quality_SelectedItem == "Low")
{
// do something
}
}
Example 2 - Passing String SelectedItem
I pass vm.Quality_SelectedItem through the Quality Method and give it the string name quality.
public ViewModel vm = new ViewModel();
// MainWindow
public MainWindow()
{
InitializeComponent();
DataContext = vm;
// Quality Method
Quality(vm.Quality_SelectedItem); // <---
}
// Quality Method
public static void Quality(string quality)
{
if (quality == "High")
{
// do something
}
else if (quality == "Low")
{
// do something
}
}
As a general rule, you should make your code as simple as possible. Remember the KISS principle. This also plays well with SOLID ("simple" is a good way to achieve Single responsibility and Interface segregation).
Avoid reaching into one object to get another.
If you need only a string value in the method, only pass that string value. Don't force your method to dig into object hierarchies and dependencies to get that value.
If the method needs to modify a string property value, then pass the object where to modify the property.
From the performance point of view, you will not notice any change. Accessing an object by-reference is a very cheap operation. (Unless you're implementing loops with billions of iterations.)
From the design point of view, keeping the things simple makes your code SOLID and easily allows re-usage.
It depends on what is //do something.
If you have to handle/interact your viewmodel-object, then pass
viewmodel as parameter
otherwise use a string for less dependencies and possibility of common use.
If you have your viewmodel as singleton, then it doesn't matter which way you go.

How to get invalid values in a WPF view

If I have a WPF view with a textbox that has a binding to a decimal (or any other number format) I automatically get a visual hint if I enter a letter or any other invald character and the value is not transferred to the viewmodel (the breakpoint on the setter is never reached). If I enter a number, everything works fine. To disable my save-Button (ICommand), I would like to get the info in my viewmodel that there is an error in the view in a MVVM-like fashion. Hints to where this behavior is documented are very welcome!
So the target situation looks like this:
what I want would be a disable "save and close":
XAML:
<TextBox Text="{Binding Path=SelectedItem.Punkte_Seite_max, UpdateSourceTrigger=PropertyChanged}"/>
ViewModel
public int Punkte_Seite_max
{
get { return _punkte_Seite_max; }
set
{
_punkte_Seite_max = value;
Changed(); //INotifyPropertyChanged call
}
}
What you want to be using is INotifyDataErrorInfo documentation found here. This lets you provide custom validation on the properties that you have bound to your ViewModel.
This is a sample I have shamelessly copied from CodeProject but I have done so to prevent any link rot. I have also tried to adapt it slightly to match your example.
ViewModel
public class ViewModel : INotifyDataErrorInfo
{
// A place to store all error messages for all properties.
private IDictionary<string, List<string>> propertyErrors = new Dictionary<string, List<string>>();
public string Preis
{
get { return _preis; }
set
{
// Only update if the value has actually changed.
if (!string.Equals(_preis, value, StringComparison.Ordinal))
{
_preis = value;
Changed();
this.Validate();
}
}
}
// The event to raise when the error state changes.
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
// A method of getting all errors for the given known property.
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if (propertyName != null)
{
if (propertyErrors.TryGetValue(propertyName, out var errors))
{
return errors;
}
}
return null;
}
// Whether there are any errors on the ViewModel
public bool HasErrors
{
get
{
return propertyErrors.Values.Any(r =>r.Any());
}
}
private void Validate()
{
// 1. HERE YOU CAN CHECK WHETHER Preis IS VALID AND ANY OTHER PROPERTIES
// 2. Update the 'propertyErrors' dictionary with the errors
// 3. Raise the ErrorsChanged event.
}
}
XAML
You will need to change your XAML to something like this:
<TextBox>
<Binding Path="Preis" UpdateSourceTrigger="PropertyChanged" ValidatesOnNotifyDataErrors="True"/>
</TextBox>
Thanks to Bijington I got on the right track and found an answer which satisfies MVVM and also doesn't need code behind. In case someone is interested here's my solution to this issue.
The error shown above is created in the view because there is no converter in WPF from letters to int (how should there be one). To raise this issue the binding in needs to have NotifyOnValidationError=True.
<TextBox Text="{Binding Path=SelectedItem.Punkte_Seite_max, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True}"
This raises a bubbling up Validation.Error event that can be captured anywhere in the tree. I decided to capture it via a routed event trigger like so:
XAML:
<Window
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" >
<i:Interaction.Triggers>
<userInterface:RoutedEventTrigger RoutedEvent="{x:Static Validation.ErrorEvent}" >
<userInterface:ViewErrorCounterAction ViewErrorCounter="{Binding Path=ViewValidationErrorCount, Mode=TwoWay}"/>
</userInterface:RoutedEventTrigger>
</i:Interaction.Triggers>
So the twoway-binding is the MVVM-okayish link to my viewmodel.
ViewErrorCounterAction is based on this SO answer:
public class ViewErrorCounterAction : TriggerAction<DependencyObject> {
public ViewErrorCounterAction()
{
ViewErrorCounter = 0; // initalize with 0 as there should not be such errors when the window is loaded
}
public int ViewErrorCounter
{
get
{
return System.Convert.ToInt32(GetValue(ViewErrorCounterProperty));
}
set
{
SetValue(ViewErrorCounterProperty, value);
}
}
public static readonly DependencyProperty ViewErrorCounterProperty = DependencyProperty.Register("ViewErrorCounter", typeof(int), typeof(ViewErrorCounterAction), new PropertyMetadata(null));
protected override void Invoke(object parameter)
{
var e = (ValidationErrorEventArgs)parameter;
if ((e.Action == ValidationErrorEventAction.Added))
ViewErrorCounter = ViewErrorCounter + 1;
else if ((e.Action == ValidationErrorEventAction.Removed))
ViewErrorCounter = ViewErrorCounter - 1;
}
}
Finally routed Event Trigger is based on https://sergecalderara.wordpress.com/2012/08/23/how-to-attached-an-mvvm-eventtocommand-to-an-attached-event/
Hope this helps and I'd appreciate comments on how to better solve this issue if there are more elegant ways :)

Two Way binding to a Dependency Property in a User Control and call a method

I know, title is a little confusing so let me explain. I have a user control that has a dependency property. I access this dependency property with a regular property called Input. In my view model I also have a property called Input. I have these two properties bound together in XAML using two-way binding as shown below:
<uc:rdtDisplay x:Name="rdtDisplay" Input="{Binding Input, Mode=TwoWay}" Line1="{Binding myRdt.Line1}" Line2="{Binding myRdt.Line2}" Height="175" Width="99" Canvas.Left="627" Canvas.Top="10"/>
Okay in my view model, I call a method whenever the value of Input is changed as shown in my property:
public string Input
{
get
{
return input;
}
set
{
input = value;
InputChanged();
}
}
The problem with this is that when I set the value of Input in my view model it only updates the value of the variable input as per my setter in my property. How can I get this to update back to the dependency property in the user control? If I leave the code input = value; out then I get a compilation error.
I need something like this:
public string Input
{
get
{
return UserControl.Input;
}
set
{
UserControl.Input = value;
InputChanged();
}
}
If I make the Input property in my view model look like this:
public string Input
{
get; set;
}
then it works, however, I am unable to call the InputChanged() method that I need to call when the Property is changed. All suggestions are appreciated.
Implement INotifyPropertyChanged in your ViewModel
public class Sample : INotifyPropertyChanged
{
private string input = string.Empty;
public string Input
{
get
{
return input;
}
set
{
input = value;
NotifyPropertyChanged("Input");
InputChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
In your case, you can do it in the code behind of your usercontrol

Bind control to property not working

I am creating an application that uses several threads as a result I want to try to use UIControls in my code behind as few as possible. The way I do it is by binding the controls to a property in my code behind that way I will be able to update the control by changing that property it does not matter if that property is updated on a different thread. Anyways I am creating the following code in order for the class to create the bindings form me.
public static class MyExtensionMethods
{
public static TextBoxBind<T> BindTextBox<T>(this TextBox textbox, string property=null)
{
return new TextBoxBind<T>(textbox,property);
}
}
public class TextBoxBind<T> : INotifyPropertyChanged
{
string property;
protected T _Value;
public T Value
{
get { return _Value; }
set { _Value = value; OnPropertyChanged(property); }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged(string propertyName){
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public TextBoxBind(TextBox textbox, string property)
{
if (property == null)
{
property = "Value";
}
this.property = property;
Binding b = new Binding(property)
{
Source = this
};
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
textbox.SetBinding(TextBox.TextProperty, b);
}
}
And on my XAML I have:
<TextBox Name="textBox2" />
Therefore I will be able to use the first code that I posted as:
var newTextBox2 = textBox2.BindTextBox<int>();
newTextBox2.Value = 50; // this will update the textBox2.Text = "2"
// also every time I update the value of textBox2 newTextBox2.Value will update as well
The problem is when I try to bind it to a custom object. Take this code for example:
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
public override string ToString()
{
return Age.ToString();
}
}
void LogIn_Loaded(object sender, RoutedEventArgs e)
{
txtUsuario.Focus();
var newTextBox2 = textBox2.BindTextBox<Person>("Age");
// here newTextBox2 never updates....
}
When it comes to data binding one should update an object (doesn't matter CLR property or DependencyObject) from the same thread, as the UI is running at. If you have a UI element bound to something in code, updating that from a separate thread will lead to exception. However, you can always retrieve your UI thread and perform property update there.
Here's a piece of code, that I am using in a similar situation as you have:
ThreadStart updateLogs = delegate()
{
ObservableCollection<LogMessage> newLogs = this._parcer.Parce();
foreach (LogMessage log in newLogs)
LogMessages.Add(log);
};
App.Current.Dispatcher.BeginInvoke(updateLogs, null);
This block of code is running in a thread different to one UI is running at. So I extract the code, that actually updates the binding source (which is LogMessages) into a delegate updateLogs and then run this delegate in a UI thread, passing it to the application dispatcher.
Nevertheless, WPF application can have more than one Dispather if, for example, you create separate windows in separate threads, although this approach is rare. But just in case, DependencyObject class has a Dispatcher property, which references the Dispather that owns this object.
OnPropertyChanged(property); should be pointing to Value, since that's the Name of your Property.
This should not be pointing to the type T.
So this code is not right:
if (property == null)
{
property = "Value";
}
because property should always be "Value"
public T Value
{
get { return _Value; }
set { _Value = value; OnPropertyChanged("Value"); }
}

WPF Binding UI events to commands in ViewModel

I’m doing some refactoring of a simple application to follow MVVM and my question is how do I move a SelectionChanged event out of my code behind to the viewModel? I’ve looked at some examples of binding elements to commands but didn’t quite grasp it. Can anyone assist with this. Thanks!
Can anyone provide a solution using the code below? Many thanks!
public partial class MyAppView : Window
{
public MyAppView()
{
InitializeComponent();
this.DataContext = new MyAppViewModel ();
// Insert code required on object creation below this point.
}
private void contactsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
//TODO: Add event handler implementation here.
//for each selected contact get the labels and put in collection
ObservableCollection<AggregatedLabelModel> contactListLabels = new ObservableCollection<AggregatedLabelModel>();
foreach (ContactListModel contactList in contactsList.SelectedItems)
{
foreach (AggregatedLabelModel aggLabel in contactList.AggLabels)
{
contactListLabels.Add(aggLabel);
}
}
//aggregate the contactListLabels by name
ListCollectionView selectedLabelsView = new ListCollectionView(contactListLabels);
selectedLabelsView.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
tagsList.ItemsSource = selectedLabelsView.Groups;
}
}
You should use an EventTrigger in combination with InvokeCommandAction from the Windows.Interactivity namespace. Here is an example:
<ListBox ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
You can reference System.Windows.Interactivity by going Add reference > Assemblies > Extensions.
And the full i namespace is: xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity".
This question has a similar issue.
WPF MVVM : Commands are easy. How to Connect View and ViewModel with RoutedEvent
The way I deal with this issue is to have a SelectedItem property in the ViewModel, and then bind the SelectedItem of your ListBox or whatever to that property.
To refactor this you need to shift your thinking. You will no longer be handling a "selection changed" event, but rather storing the selected item in your viewmodel. You would then use two-way data binding so that when the user selects an item, your viewmodel is updated, and when you change the selected item, your view it updated.
Consider Microsoft.Xaml.Behaviors.Wpf, its owner is Microsoft which you can see in that page.
System.Windows.Interactivity.WPF owner is mthamil, anybody can tell me is it reliable ?
Example of Microsoft.Xaml.Behaviors.Wpf:
<UserControl ...
xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
...>
<Button x:Name="button">
<behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="Click" SourceObject="{Binding ElementName=button}">
<behaviors:InvokeCommandAction Command="{Binding ClickCommand}" />
</behaviors:EventTrigger>
</behaviors:Interaction.Triggers>
</Button>
</UserControl>
Your best bet is using Windows.Interactivity. Use EventTriggers to attach an ICommand to any RoutedEvent.
Here is an article to get you started : Silverlight and WPF Behaviours and Triggers
I know it's a bit late but, Microsoft has made their Xaml.Behaviors open source and it's now much easier to use interactivity with just one namespace.
First add Microsoft.Xaml.Behaviors.Wpf Nuget packge to your project.
https://www.nuget.org/packages/Microsoft.Xaml.Behaviors.Wpf/
add xmlns:behaviours="http://schemas.microsoft.com/xaml/behaviors" namespace to your
xaml.
Then use it like this,
<Button Width="150" Style="{DynamicResource MaterialDesignRaisedDarkButton}">
<behaviours:Interaction.Triggers>
<behaviours:EventTrigger EventName="Click">
<behaviours:InvokeCommandAction Command="{Binding OpenCommand}" PassEventArgsToCommand="True"/>
</behaviours:EventTrigger>
</behaviours:Interaction.Triggers>
Open
</Button>
PassEventArgsToCommand="True" should be set as True and the RelayCommand that you implement can take RoutedEventArgs or objects as template. If you are using object as the parameter type just cast it to the appropriate event type.
The command will look something like this,
OpenCommand = new RelayCommand<object>(OnOpenClicked, (o) => { return true; });
The command method will look something like this,
private void OnOpenClicked(object parameter)
{
Logger.Info(parameter?.GetType().Name);
}
The 'parameter' will be the Routed event object.
And the log incase you are curious,
2020-12-15 11:40:36.3600|INFO|MyApplication.ViewModels.MainWindowViewModel|RoutedEventArgs
As you can see the TypeName logged is RoutedEventArgs
RelayCommand impelmentation can be found here.
Why RelayCommand
PS : You can bind to any event of any control. Like Closing event of Window and you will get the corresponding events.
<ListBox SelectionChanged="{eb:EventBinding Command=SelectedItemChangedCommand, CommandParameter=$e}">
</ListBox>
Command
{eb:EventBinding} (Simple naming pattern to find Command)
{eb:EventBinding Command=CommandName}
CommandParameter
$e (EventAgrs)
$this or $this.Property
string
https://github.com/JonghoL/EventBindingMarkup
I would follow the top answer in this question
Basically your view model will contain a list of all your items and a list of selected items. You can then attach a behaviour to your listbox that manages your list of selected items.
Doing this means you having nothing in the code behind and the xaml is fairly easy to follow, also the behaviour can be re-used elsewhere in your app.
<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" />
Sometimes solution of binding event to command through Interactivity trigger doesn't work, when it's needed to bind the event of custom usercontrol.
In this case you can use custom behavior.
Declare binding behavior like:
public class PageChangedBehavior
{
#region Attached property
public static ICommand PageChangedCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(PageChangedCommandProperty);
}
public static void SetPageChangedCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(PageChangedCommandProperty, value);
}
public static readonly DependencyProperty PageChangedCommandProperty =
DependencyProperty.RegisterAttached("PageChangedCommand", typeof(ICommand), typeof(PageChangedBehavior),
new PropertyMetadata(null, OnPageChanged));
#endregion
#region Attached property handler
private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as PageControl;
if (control != null)
{
if (e.NewValue != null)
{
control.PageChanged += PageControl_PageChanged;
}
else
{
control.PageChanged -= PageControl_PageChanged;
}
}
}
static void PageControl_PageChanged(object sender, int page)
{
ICommand command = PageChangedCommand(sender as DependencyObject);
if (command != null)
{
command.Execute(page);
}
}
#endregion
}
And then bind it to command in xaml:
<controls:PageControl
Grid.Row="2"
CurrentPage="{Binding Path=UsersSearchModel.Page,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PerPage="{Binding Path=UsersSearchModel.PageSize,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Count="{Binding Path=UsersSearchModel.SearchResults.TotalItemCount}"
behaviors:PageChangedBehavior.PageChangedCommand="{Binding PageChangedCommand}">
</controls:PageControl>
As #Cameron MacFarland mentions, I would simply two-way bind to a property on the viewModel. In the property setter you could do whatever logic you require, such as adding to a list of contacts, depending on your requirements.
However, i wouldn't necessarily call the property 'SelectedItem' as the viewModel shouldn't know about the view layer and how it's interacting with it's properties. I'd call it something like CurrentContact or something.
Obviously this is unless you just want to create commands as an exercise to practice etc.
This is an implementation using a MarkupExtension. Despite the low level nature (which is required in this scenario), the XAML code is very straight forward:
XAML
<SomeControl Click="{local:EventBinding EventToCommand}" CommandParameter="{local:Int32 12345}" />
Marup Extension
public class EventBindingExtension : MarkupExtension
{
private static readonly MethodInfo EventHandlerImplMethod = typeof(EventBindingExtension).GetMethod(nameof(EventHandlerImpl), new[] { typeof(object), typeof(string) });
public string Command { get; set; }
public EventBindingExtension()
{
}
public EventBindingExtension(string command) : this()
{
Command = command;
}
// Do not use!!
public static void EventHandlerImpl(object sender, string commandName)
{
if (sender is FrameworkElement frameworkElement)
{
object dataContext = frameworkElement.DataContext;
if (dataContext?.GetType().GetProperty(commandName)?.GetValue(dataContext) is ICommand command)
{
object commandParameter = (frameworkElement as ICommandSource)?.CommandParameter;
if (command.CanExecute(commandParameter)) command.Execute(commandParameter);
}
}
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget targetProvider &&
targetProvider.TargetObject is FrameworkElement targetObject &&
targetProvider.TargetProperty is MemberInfo memberInfo)
{
Type eventHandlerType;
if (memberInfo is EventInfo eventInfo) eventHandlerType = eventInfo.EventHandlerType;
else if (memberInfo is MethodInfo methodInfo) eventHandlerType = methodInfo.GetParameters()[1].ParameterType;
else return null;
MethodInfo handler = eventHandlerType.GetMethod("Invoke");
DynamicMethod method = new DynamicMethod("", handler.ReturnType, new[] { typeof(object), typeof(object) });
ILGenerator ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg, 0);
ilGenerator.Emit(OpCodes.Ldstr, Command);
ilGenerator.Emit(OpCodes.Call, EventHandlerImplMethod);
ilGenerator.Emit(OpCodes.Ret);
return method.CreateDelegate(eventHandlerType);
}
else
{
throw new InvalidOperationException("Could not create event binding.");
}
}
}

Categories