├── .gitignore
├── .nuget
├── NuGet.Config
├── NuGet.exe
└── NuGet.targets
├── EventBindingMarkup.sln
├── LICENSE.md
├── README.md
└── src
├── EventBinding.Tests
├── EventBinding.Tests.csproj
├── EventBindingExtensionTests.cs
├── Properties
│ └── AssemblyInfo.cs
└── packages.config
├── EventBinding
├── EventBinding.csproj
├── EventBindingExtension.cs
├── MVVM
│ ├── BindableBase.cs
│ ├── DelegateCommand.cs
│ ├── DependencyObjectExtensions.cs
│ ├── ViewModelBase.cs
│ └── WeakEventHandlerManager.cs
└── Properties
│ └── AssemblyInfo.cs
├── Images
├── Thumbs.db
├── viewmodel.png
└── xaml.png
└── Sample
├── App.config
├── App.xaml
├── App.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── MainWindowViewModel.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
└── Sample.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | bld/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # MSTest test Results
20 | [Tt]est[Rr]esult*/
21 | [Bb]uild[Ll]og.*
22 |
23 | #NUNIT
24 | *.VisualState.xml
25 | TestResult.xml
26 |
27 | # Build Results of an ATL Project
28 | [Dd]ebugPS/
29 | [Rr]eleasePS/
30 | dlldata.c
31 |
32 | *_i.c
33 | *_p.c
34 | *_i.h
35 | *.ilk
36 | *.meta
37 | *.obj
38 | *.pch
39 | *.pdb
40 | *.pgc
41 | *.pgd
42 | *.rsp
43 | *.sbr
44 | *.tlb
45 | *.tli
46 | *.tlh
47 | *.tmp
48 | *.tmp_proj
49 | *.log
50 | *.vspscc
51 | *.vssscc
52 | .builds
53 | *.pidb
54 | *.svclog
55 | *.scc
56 |
57 | # Chutzpah Test files
58 | _Chutzpah*
59 |
60 | # Visual C++ cache files
61 | ipch/
62 | *.aps
63 | *.ncb
64 | *.opensdf
65 | *.sdf
66 | *.cachefile
67 |
68 | # Visual Studio profiler
69 | *.psess
70 | *.vsp
71 | *.vspx
72 |
73 | # TFS 2012 Local Workspace
74 | $tf/
75 |
76 | # Guidance Automation Toolkit
77 | *.gpState
78 |
79 | # ReSharper is a .NET coding add-in
80 | _ReSharper*/
81 | *.[Rr]e[Ss]harper
82 | *.DotSettings.user
83 |
84 | # JustCode is a .NET coding addin-in
85 | .JustCode
86 |
87 | # TeamCity is a build add-in
88 | _TeamCity*
89 |
90 | # DotCover is a Code Coverage Tool
91 | *.dotCover
92 |
93 | # NCrunch
94 | *.ncrunch*
95 | _NCrunch_*
96 | .*crunch*.local.xml
97 |
98 | # MightyMoose
99 | *.mm.*
100 | AutoTest.Net/
101 |
102 | # Web workbench (sass)
103 | .sass-cache/
104 |
105 | # Installshield output folder
106 | [Ee]xpress/
107 |
108 | # DocProject is a documentation generator add-in
109 | DocProject/buildhelp/
110 | DocProject/Help/*.HxT
111 | DocProject/Help/*.HxC
112 | DocProject/Help/*.hhc
113 | DocProject/Help/*.hhk
114 | DocProject/Help/*.hhp
115 | DocProject/Help/Html2
116 | DocProject/Help/html
117 |
118 | # Click-Once directory
119 | publish/
120 |
121 | # Publish Web Output
122 | *.[Pp]ublish.xml
123 | *.azurePubxml
124 |
125 | # NuGet Packages Directory
126 | packages/
127 | ## TODO: If the tool you use requires repositories.config uncomment the next line
128 | #!packages/repositories.config
129 |
130 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
131 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
132 | !packages/build/
133 |
134 | # Windows Azure Build Output
135 | csx/
136 | *.build.csdef
137 |
138 | # Windows Store app package directory
139 | AppPackages/
140 |
141 | # Others
142 | sql/
143 | *.Cache
144 | ClientBin/
145 | [Ss]tyle[Cc]op.*
146 | ~$*
147 | *~
148 | *.dbmdl
149 | *.dbproj.schemaview
150 | *.pfx
151 | *.publishsettings
152 | node_modules/
153 |
154 | # RIA/Silverlight projects
155 | Generated_Code/
156 |
157 | # Backup & report files from converting an old project file to a newer
158 | # Visual Studio version. Backup files are not needed, because we have git ;-)
159 | _UpgradeReport_Files/
160 | Backup*/
161 | UpgradeLog*.XML
162 | UpgradeLog*.htm
163 |
164 | # SQL Server files
165 | *.mdf
166 | *.ldf
167 |
168 | # Business Intelligence projects
169 | *.rdl.data
170 | *.bim.layout
171 | *.bim_*.settings
172 |
173 | # Microsoft Fakes
174 | FakesAssemblies/
175 |
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JonghoL/EventBindingMarkup/45a504b46d9e7ed5f9089a59a2cfd2f6f2b693ed/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 |
32 |
33 |
34 |
35 | $(SolutionDir).nuget
36 |
37 |
38 |
39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
41 |
42 |
43 |
44 | $(MSBuildProjectDirectory)\packages.config
45 | $(PackagesProjectConfig)
46 |
47 |
48 |
49 |
50 | $(NuGetToolsPath)\NuGet.exe
51 | @(PackageSource)
52 |
53 | "$(NuGetExePath)"
54 | mono --runtime=v4.0.30319 "$(NuGetExePath)"
55 |
56 | $(TargetDir.Trim('\\'))
57 |
58 | -RequireConsent
59 | -NonInteractive
60 |
61 | "$(SolutionDir) "
62 | "$(SolutionDir)"
63 |
64 |
65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
67 |
68 |
69 |
70 | RestorePackages;
71 | $(BuildDependsOn);
72 |
73 |
74 |
75 |
76 | $(BuildDependsOn);
77 | BuildPackage;
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
99 |
100 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/EventBindingMarkup.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F52B11B7-B340-4C73-9785-68CA8DC3008E}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "src\Sample\Sample.csproj", "{8374D454-668D-49C9-B2B5-50A6D5C84A24}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBinding", "src\EventBinding\EventBinding.csproj", "{930F1148-584E-44E2-AB79-E2A6822067CB}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBinding.Tests", "src\EventBinding.Tests\EventBinding.Tests.csproj", "{C02ABE7A-4758-4868-8067-A79CA3A235EE}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{1AE9A615-E7C2-4B09-BBFA-4A38BB1E339D}"
15 | ProjectSection(SolutionItems) = preProject
16 | .nuget\NuGet.Config = .nuget\NuGet.Config
17 | .nuget\NuGet.exe = .nuget\NuGet.exe
18 | .nuget\NuGet.targets = .nuget\NuGet.targets
19 | EndProjectSection
20 | EndProject
21 | Global
22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
23 | Debug|Any CPU = Debug|Any CPU
24 | Release|Any CPU = Release|Any CPU
25 | EndGlobalSection
26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
27 | {8374D454-668D-49C9-B2B5-50A6D5C84A24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {8374D454-668D-49C9-B2B5-50A6D5C84A24}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {8374D454-668D-49C9-B2B5-50A6D5C84A24}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {8374D454-668D-49C9-B2B5-50A6D5C84A24}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {930F1148-584E-44E2-AB79-E2A6822067CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {930F1148-584E-44E2-AB79-E2A6822067CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {930F1148-584E-44E2-AB79-E2A6822067CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {930F1148-584E-44E2-AB79-E2A6822067CB}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {C02ABE7A-4758-4868-8067-A79CA3A235EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36 | {C02ABE7A-4758-4868-8067-A79CA3A235EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
37 | {C02ABE7A-4758-4868-8067-A79CA3A235EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {C02ABE7A-4758-4868-8067-A79CA3A235EE}.Release|Any CPU.Build.0 = Release|Any CPU
39 | EndGlobalSection
40 | GlobalSection(SolutionProperties) = preSolution
41 | HideSolutionNode = FALSE
42 | EndGlobalSection
43 | GlobalSection(NestedProjects) = preSolution
44 | {8374D454-668D-49C9-B2B5-50A6D5C84A24} = {F52B11B7-B340-4C73-9785-68CA8DC3008E}
45 | {930F1148-584E-44E2-AB79-E2A6822067CB} = {F52B11B7-B340-4C73-9785-68CA8DC3008E}
46 | {C02ABE7A-4758-4868-8067-A79CA3A235EE} = {F52B11B7-B340-4C73-9785-68CA8DC3008E}
47 | EndGlobalSection
48 | EndGlobal
49 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014-2015 Jongho Lee
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | EventBindingMarkup
2 | ==================
3 |
4 | Markup Extensions for Events (WPF 4.5)
5 |
6 | 
7 | ## Command
8 | 1. {eb:EventBinding} (Simple naming pattern to find Command)
9 | 1. {eb:EventBinding Command=CommandName}
10 |
11 | ## CommandParameter
12 | 1. $e (EventAgrs)
13 | 2. $this or $this.Property
14 | 3. string
15 |
16 |
17 |
18 |
19 |
20 |
21 | ## License
22 |
23 | See the [LICENSE](LICENSE.md) file for license rights and limitations (MIT).
24 |
--------------------------------------------------------------------------------
/src/EventBinding.Tests/EventBinding.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Debug
8 | AnyCPU
9 | {C02ABE7A-4758-4868-8067-A79CA3A235EE}
10 | Library
11 | Properties
12 | EventBinding.Tests
13 | EventBinding.Tests
14 | v4.5
15 | 512
16 | ..\..\
17 | true
18 | 2d41c7d3
19 |
20 |
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 |
29 |
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll
48 |
49 |
50 | ..\..\packages\xunit.assert.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll
51 |
52 |
53 | ..\..\packages\xunit.extensibility.core.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {930f1148-584e-44e2-ab79-e2a6822067cb}
63 | EventBinding
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
77 |
78 |
79 |
80 |
81 |
82 |
89 |
--------------------------------------------------------------------------------
/src/EventBinding.Tests/EventBindingExtensionTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 |
4 | namespace EventBinding.Tests
5 | {
6 | public class EventBindingExtensionTests
7 | {
8 | public class FollowPropertyPathMethod
9 | {
10 | [Fact]
11 | public void TargetNull_Should_ThrowsException()
12 | {
13 | var ex = Assert.Throws(() => EventBindingExtension.FollowPropertyPath(null, null));
14 | Assert.Equal("target null", ex.ParamName);
15 | }
16 |
17 | [Fact]
18 | public void PathNull_Should_ThrowsException()
19 | {
20 | var ex = Assert.Throws(() => EventBindingExtension.FollowPropertyPath(new DummyTarget(), null));
21 | Assert.Equal("path null", ex.ParamName);
22 | }
23 |
24 | [Fact]
25 | public void PathEmpty_Should_ThrowsException2()
26 | {
27 | var ex = Assert.Throws(() => EventBindingExtension.FollowPropertyPath(new DummyTarget(), ""));
28 | Assert.Equal("property null", ex.Message);
29 | }
30 |
31 | [Fact]
32 | public void InvalidPath_Should_ThrowsException2()
33 | {
34 | var ex = Assert.Throws(() => EventBindingExtension.FollowPropertyPath(new DummyTarget(), "IsVisible"));
35 | Assert.Equal("property null", ex.Message);
36 | }
37 |
38 | [Fact]
39 | public void Verify()
40 | {
41 | var ret = EventBindingExtension.FollowPropertyPath(new DummyTarget { IsEnabled = true }, "IsEnabled");
42 | Assert.NotNull(ret);
43 | Assert.Equal("True", ret.ToString());
44 | }
45 |
46 | [Fact]
47 | public void Verify_FollowProperty()
48 | {
49 | var ret = EventBindingExtension.FollowPropertyPath(new DummyTarget { Source = new DummySource { Id = 2}}, "Source.Id");
50 | Assert.NotNull(ret);
51 | Assert.Equal("2", ret.ToString());
52 | }
53 | }
54 | }
55 |
56 | public class DummyTarget
57 | {
58 | public bool IsEnabled { get; set; }
59 | public DummySource Source { get; set; }
60 | }
61 |
62 | public class DummySource
63 | {
64 | public int Id { get; set; }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/EventBinding.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("EventBinding.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("EventBinding.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("67c84f10-e2a9-4582-921c-ede4e30f4c38")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/EventBinding.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/EventBinding/EventBinding.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {930F1148-584E-44E2-AB79-E2A6822067CB}
8 | Library
9 | Properties
10 | EventBinding
11 | EventBinding
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
62 |
--------------------------------------------------------------------------------
/src/EventBinding/EventBindingExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Reflection.Emit;
4 | using System.Windows.Input;
5 | using System.Windows.Markup;
6 | using System.Windows;
7 | using System.Reflection;
8 | using EventBinding.MVVM;
9 |
10 | namespace EventBinding
11 | {
12 | public class EventBindingExtension : MarkupExtension
13 | {
14 | public string Command { get; set; }
15 | public string CommandParameter { get; set; }
16 |
17 | public override object ProvideValue(IServiceProvider serviceProvider)
18 | {
19 | var targetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
20 | if (targetProvider == null)
21 | {
22 | throw new InvalidOperationException();
23 | }
24 |
25 | var targetObject = targetProvider.TargetObject as FrameworkElement;
26 | if (targetObject == null)
27 | {
28 | throw new InvalidOperationException();
29 | }
30 |
31 | var memberInfo = targetProvider.TargetProperty as MemberInfo;
32 | if (memberInfo == null)
33 | {
34 | throw new InvalidOperationException();
35 | }
36 |
37 | if (string.IsNullOrWhiteSpace(Command))
38 | {
39 | Command = memberInfo.Name.Replace("Add", "");
40 | if (Command.Contains("Handler"))
41 | {
42 | Command = Command.Replace("Handler", "Command");
43 | }
44 | else
45 | {
46 | Command = Command + "Command";
47 | }
48 | }
49 |
50 | return CreateHandler(memberInfo, Command, targetObject.GetType());
51 | }
52 |
53 | private Type GetEventHandlerType(MemberInfo memberInfo)
54 | {
55 | Type eventHandlerType = null;
56 | if (memberInfo is EventInfo)
57 | {
58 | var info = memberInfo as EventInfo;
59 | var eventInfo = info;
60 | eventHandlerType = eventInfo.EventHandlerType;
61 | }
62 | else if (memberInfo is MethodInfo)
63 | {
64 | var info = memberInfo as MethodInfo;
65 | var methodInfo = info;
66 | ParameterInfo[] pars = methodInfo.GetParameters();
67 | eventHandlerType = pars[1].ParameterType;
68 | }
69 |
70 | return eventHandlerType;
71 | }
72 |
73 | private object CreateHandler(MemberInfo memberInfo, string cmdName, Type targetType)
74 | {
75 | Type eventHandlerType = GetEventHandlerType(memberInfo);
76 |
77 | if (eventHandlerType == null) return null;
78 |
79 | var handlerInfo = eventHandlerType.GetMethod("Invoke");
80 | var method = new DynamicMethod("", handlerInfo.ReturnType,
81 | new Type[]
82 | {
83 | handlerInfo.GetParameters()[0].ParameterType,
84 | handlerInfo.GetParameters()[1].ParameterType,
85 | });
86 |
87 | var gen = method.GetILGenerator();
88 | gen.Emit(OpCodes.Ldarg, 0);
89 | gen.Emit(OpCodes.Ldarg, 1);
90 | gen.Emit(OpCodes.Ldstr, cmdName);
91 | if (CommandParameter == null)
92 | {
93 | gen.Emit(OpCodes.Ldnull);
94 | }
95 | else
96 | {
97 | gen.Emit(OpCodes.Ldstr, CommandParameter);
98 | }
99 | gen.Emit(OpCodes.Call, getMethod);
100 | gen.Emit(OpCodes.Ret);
101 |
102 | return method.CreateDelegate(eventHandlerType);
103 | }
104 |
105 | static readonly MethodInfo getMethod = typeof(EventBindingExtension).GetMethod("HandlerIntern", new Type[] { typeof(object), typeof(object), typeof(string), typeof(string) });
106 |
107 | static void Handler(object sender, object args)
108 | {
109 | HandlerIntern(sender, args, "cmd", null);
110 | }
111 |
112 | public static void HandlerIntern(object sender, object args, string cmdName, string commandParameter)
113 | {
114 | var fe = sender as FrameworkElement;
115 | if (fe != null)
116 | {
117 | ICommand cmd = GetCommand(fe, cmdName);
118 | object commandParam = null;
119 | if (!string.IsNullOrWhiteSpace(commandParameter))
120 | {
121 | commandParam = GetCommandParameter(fe, args, commandParameter);
122 | }
123 | if ((cmd != null) && cmd.CanExecute(commandParam))
124 | {
125 | cmd.Execute(commandParam);
126 | }
127 | }
128 | }
129 |
130 | internal static ICommand GetCommand(FrameworkElement target, string cmdName)
131 | {
132 | var vm = FindViewModel(target);
133 | if (vm == null) return null;
134 |
135 | var vmType = vm.GetType();
136 | var cmdProp = vmType.GetProperty(cmdName);
137 | if (cmdProp != null)
138 | {
139 | return cmdProp.GetValue(vm) as ICommand;
140 | }
141 | #if DEBUG
142 | throw new Exception("EventBinding path error: '" + cmdName + "' property not found on '" + vmType + "' 'DelegateCommand'");
143 | #endif
144 |
145 | return null;
146 | }
147 |
148 | internal static object GetCommandParameter(FrameworkElement target, object args, string commandParameter)
149 | {
150 | object ret = null;
151 | var classify = commandParameter.Split('.');
152 | switch (classify[0])
153 | {
154 | case "$e":
155 | ret = args;
156 | break;
157 | case "$this":
158 | ret = classify.Length > 1 ? FollowPropertyPath(target, commandParameter.Replace("$this.", ""), target.GetType()) : target;
159 | break;
160 | default:
161 | ret = commandParameter;
162 | break;
163 | }
164 |
165 | return ret;
166 | }
167 |
168 | internal static ViewModelBase FindViewModel(FrameworkElement target)
169 | {
170 | if (target == null) return null;
171 |
172 | var vm = target.DataContext as ViewModelBase;
173 | if (vm != null) return vm;
174 |
175 | var parent = target.GetParentObject() as FrameworkElement;
176 |
177 | return FindViewModel(parent);
178 | }
179 |
180 | internal static object FollowPropertyPath(object target, string path, Type valueType = null)
181 | {
182 | if (target == null) throw new ArgumentNullException("target null");
183 | if (path == null) throw new ArgumentNullException("path null");
184 |
185 | Type currentType = valueType ?? target.GetType();
186 |
187 | foreach (string propertyName in path.Split('.'))
188 | {
189 | PropertyInfo property = currentType.GetProperty(propertyName);
190 | if (property == null) throw new NullReferenceException("property null");
191 |
192 | target = property.GetValue(target);
193 | currentType = property.PropertyType;
194 | }
195 | return target;
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/EventBinding/MVVM/BindableBase.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace EventBinding.MVVM
5 | {
6 | public class BindableBase : INotifyPropertyChanged
7 | {
8 | public event PropertyChangedEventHandler PropertyChanged;
9 |
10 | protected virtual bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
11 | {
12 | if (object.Equals(storage, value)) return false;
13 |
14 | storage = value;
15 | this.OnPropertyChanged(propertyName);
16 |
17 | return true;
18 | }
19 |
20 | protected virtual void OnPropertyChanged(string propertyName)
21 | {
22 | PropertyChangedEventHandler handler = PropertyChanged;
23 | if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/EventBinding/MVVM/DelegateCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using System.Windows.Input;
5 |
6 | namespace EventBinding.MVVM
7 | {
8 | public sealed class DelegateCommand : ICommand
9 | {
10 | private List _canExcuteChangeHandlers;
11 |
12 | private readonly Func