How to get invalid values in a WPF view - c#

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 :)

Related

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

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

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.

How to show validatioin errors on different columns from IDataErrorInfo?

The Problem: I would like the default error of inclosing the input textbox on the UI to be used, but input in one field needs to highlight other multiple textboxes that have error.
Example: If all textboxes are empty, then no error. If only one textbox has a string, then both the OTHER two textbox's should be highlighted. (The textbox with the data is NOT the error, but the other two--if empty--are now in error). If the string is then removed and all textboxes are empty, then no error and nothing should be highlighted.
I can't seem to find an answer to this simple question. I have a business object (MVVM) with three properties, each of which is bound to a textbox in the XAML as:
<wc:AutoFilteredComboBox
..
Text="{Binding ReferredBy.NewReferralName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
....
/>
<TextBox
Text="{Binding ReferredBy.Phone, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
/>
<TextBox
......
Text="{Binding ReferredBy.PriorAuthorizationNumber, ValidatesOnDataErrors=True}"
/>
And the ViewModel implementing IDataErrorInfo:
public string Error
{
get { return null; }
}
// any returned non-empty string is an error.
public string this[string columnName]
{
get
{
switch (columnName)
{
case "NewReferralName":
if (!String.IsNullOrWhiteSpace(PriorAuthorizationNumber) || !String.IsNullOrWhiteSpace(Phone))
{
if (String.IsNullOrWhiteSpace(NewReferralName))
return "NewReferralName is invalid";
}
break;
case "Phone":
if (!String.IsNullOrWhiteSpace(NewReferralName) && String.IsNullOrWhiteSpace(Phone))
{
return "Phone is invalid";
}
break;
case "PriorAuthorizationNumber":
if (!String.IsNullOrWhiteSpace(NewReferralName) && String.IsNullOrWhiteSpace(PriorAuthorizationNumber))
{
return "PriorAuthorizationNumber is invalid";
}
break;
}
// string.Empty is no error.
return string.Empty;
}
}
So, if data is input to one textbox, how can errors be shown on the other textbox's? Can IDataErrorInfo be used for this, or is there a better way?
TIA
Of course you can use IDataErrorInfo to achieve this.
The trick here is "telling" the view to reevaluate your validations with the aid of INotifyPropertyChanged. When user change the value of one property, then the ViewModel not only have to notify the View that property changed, but other involved ones too.
For example:
public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
// Do this for each involved property in your ViewModel
private string _newReferralName;
public string NewReferralName
{
get { return _newReferralName; }
set
{
_name = value;
RaisePropertyChanged("NewReferralName");
// The tricky part. Notify that the related properties
// have to be refreshed (in the View) and, thus, reevaluated
RaisePropertyChanged("Phone");
RaisePorpertyChanged("PriorAuthorizationNumber");
}
}
...
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}

Silverlight CustomControl dependency property can't be bound to parent viewmodel

I'm having a custom Control that has a dependency property
public static readonly DependencyProperty SelectedUserCodeProperty = DependencyProperty.Register(
"SelectedUserCode",
typeof(decimal),
typeof(SystemUsersControl),
new PropertyMetadata(SelectedUserCodeChanged));
public decimal SelectedUserCode
{
get
{
return (decimal)this.GetValue(SelectedUserCodeProperty);
}
set
{
this.SetValue(SelectedUserCodeProperty, value);
RaisePropertyChanged("SelectedUserCode");
}
}
This control is inside another usercontrol that I'm attempting to get the dependency property above in its viewmodel
this xaml is inside the parent control
<SystemUsers:SystemUsersControl Name="ctrlSystemUsersControl" SelectedUserCode="{Binding SelectedSystemUserCode, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,2,0,0"/>
but nothing is bound to the parent control viewmodel
I don't know what's the problem, it's my first time dealing with dependency properties, I'm considering making the two controls in one :( unless I got any help :)
Don't worry,
SelectedSystemUserCode must be a property . If its a property you will see initial value ,but what will fully support binding for your class is ,implementation of INotifyPropertyChanged. This basic interface will be a messenger for us.
1)When you implement INotifyPropertyChanged,the below event will be added to your class.
public event PropertyChangedEventHandler PropertyChanged;
2)Then create a firing method
public void FirePropertyChanged(string prop)
{
if(PropertyChanged!=null)
{
PropertyChanged(prop);
}
}
3) Register this event for not getting null reference.
in constructor this.PropertyChanged(s,a)=>{ //may do nothing };
4) //You may use Lazy < T > instead of this.
public decimal SelectedSystemUserCode
{
get{
if(_selectedSystemUserCode==null)
{
_selectedSystemUserCode=default(decimal);
}
return _selectedSystemUserCode;
}
set
{
_selectedSystemUserCode=value;
FirePropertyChanged("SelectedSystemUserCode");
//This will be messanger for our binding
}
}
In addition,
As I remember is the default value so you may give a decimal value for that,SelectedUserCodeChanged is callback method its ok also.
//new PropertyMetadata(SelectedUserCodeChanged)
new PropertyMetadata(0) or null
Hope helps.

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