BindingTests_Method.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using System;
  2. using System.ComponentModel;
  3. using Avalonia.Controls;
  4. using Avalonia.Input;
  5. using Avalonia.UnitTests;
  6. using Xunit;
  7. namespace Avalonia.Markup.Xaml.UnitTests.Data
  8. {
  9. public class BindingTests_Method : XamlTestBase
  10. {
  11. [Fact]
  12. public void Binding_Method_To_Command_Works()
  13. {
  14. using (UnitTestApplication.Start(TestServices.StyledWindow))
  15. {
  16. var xaml = @"
  17. <Window xmlns='https://github.com/avaloniaui'
  18. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  19. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
  20. <Button Name='button' Command='{Binding Method}'/>
  21. </Window>";
  22. var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
  23. var button = window.FindControl<Button>("button");
  24. var vm = new ViewModel();
  25. button.DataContext = vm;
  26. window.ApplyTemplate();
  27. Assert.NotNull(button.Command);
  28. PerformClick(button);
  29. Assert.Equal("Called", vm.Value);
  30. }
  31. }
  32. [Fact]
  33. public void Binding_Method_With_Parameter_To_Command_Works()
  34. {
  35. using (UnitTestApplication.Start(TestServices.StyledWindow))
  36. {
  37. var xaml = @"
  38. <Window xmlns='https://github.com/avaloniaui'
  39. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  40. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
  41. <Button Name='button' Command='{Binding Method1}' CommandParameter='5'/>
  42. </Window>";
  43. var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
  44. var button = window.FindControl<Button>("button");
  45. var vm = new ViewModel();
  46. button.DataContext = vm;
  47. window.ApplyTemplate();
  48. Assert.NotNull(button.Command);
  49. PerformClick(button);
  50. Assert.Equal("Called 5", vm.Value);
  51. }
  52. }
  53. [Fact]
  54. public void Binding_Method_To_TextBlock_Text_Works()
  55. {
  56. using (UnitTestApplication.Start(TestServices.StyledWindow))
  57. {
  58. var xaml = @"
  59. <Window xmlns='https://github.com/avaloniaui'
  60. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  61. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
  62. <TextBlock Name='textBlock' Text='{Binding Method}'/>
  63. </Window>";
  64. var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
  65. var textBlock = window.FindControl<TextBlock>("textBlock");
  66. var vm = new ViewModel();
  67. textBlock.DataContext = vm;
  68. window.ApplyTemplate();
  69. Assert.NotNull(textBlock.Text);
  70. }
  71. }
  72. [Theory]
  73. [InlineData(null, "Not called")]
  74. [InlineData("A", "Do A")]
  75. public void Binding_Method_With_Parameter_To_Command_CanExecute(object commandParameter, string result)
  76. {
  77. using (UnitTestApplication.Start(TestServices.StyledWindow))
  78. {
  79. var xaml = @"
  80. <Window xmlns='https://github.com/avaloniaui'
  81. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  82. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
  83. <Button Name='button' Command='{Binding Do}' CommandParameter='{Binding Parameter, Mode=OneTime}'/>
  84. </Window>";
  85. var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
  86. var button = window.FindControl<Button>("button");
  87. var vm = new ViewModel()
  88. {
  89. Parameter = commandParameter
  90. };
  91. button.DataContext = vm;
  92. window.ApplyTemplate();
  93. Assert.NotNull(button.Command);
  94. PerformClick(button);
  95. Assert.Equal(vm.Value, result);
  96. }
  97. }
  98. [Fact]
  99. public void Binding_Method_With_Parameter_To_Command_CanExecute_DependsOn()
  100. {
  101. using (UnitTestApplication.Start(TestServices.StyledWindow))
  102. {
  103. var xaml = @"
  104. <Window xmlns='https://github.com/avaloniaui'
  105. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  106. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
  107. <Button Name='button' Command='{Binding Do}' CommandParameter='{Binding Parameter, Mode=OneWay}'/>
  108. </Window>";
  109. var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
  110. var button = window.FindControl<Button>("button");
  111. var vm = new ViewModel()
  112. {
  113. Parameter = null,
  114. };
  115. button.DataContext = vm;
  116. window.ApplyTemplate();
  117. Assert.NotNull(button.Command);
  118. Assert.Equal(button.IsEffectivelyEnabled, false);
  119. vm.Parameter = true;
  120. Threading.Dispatcher.UIThread.RunJobs();
  121. Assert.Equal(button.IsEffectivelyEnabled, true);
  122. }
  123. }
  124. [Fact]
  125. public void Binding_Method_Preserves_Correct_Order()
  126. {
  127. using (UnitTestApplication.Start(TestServices.StyledWindow))
  128. {
  129. var xaml = @"
  130. <Window xmlns='https://github.com/avaloniaui'
  131. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  132. xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
  133. <Button Name='button' Command='{Binding Method3}' CommandParameter='5'/>
  134. </Window>";
  135. var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
  136. var button = window.FindControl<Button>("button");
  137. var vm = new ViewModel();
  138. button.DataContext = vm;
  139. window.ApplyTemplate();
  140. PerformClick(button);
  141. Assert.Equal("Called Method with parameter of object type. Argument value is 5", vm.Value);
  142. }
  143. }
  144. [Fact]
  145. public void Binding_Method_To_Command_Collected()
  146. {
  147. using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
  148. WeakReference<ViewModel> MakeRef()
  149. {
  150. var weakVm = new WeakReference<ViewModel>(null);
  151. {
  152. var vm = new ViewModel()
  153. {
  154. Parameter = null,
  155. };
  156. weakVm.SetTarget(vm);
  157. var canExecuteCount = 0;
  158. var action = new Action<object>(vm.Do);
  159. var command = new Avalonia.Data.Converters.MethodToCommandConverter(action);
  160. command.CanExecuteChanged += (s, e) => canExecuteCount++;
  161. vm.Parameter = 0;
  162. Threading.Dispatcher.UIThread.RunJobs();
  163. vm.Parameter = null;
  164. Threading.Dispatcher.UIThread.RunJobs();
  165. Assert.Equal(2, canExecuteCount);
  166. }
  167. return weakVm;
  168. }
  169. bool IsAlive(WeakReference<ViewModel> @ref)
  170. {
  171. return @ref.TryGetTarget(out var instance)
  172. && instance is null == false;
  173. }
  174. var vmref = MakeRef();
  175. var beforeCollect = IsAlive(vmref);
  176. GC.Collect();
  177. GC.WaitForPendingFinalizers();
  178. var afterCollect = IsAlive(vmref);
  179. Assert.True(beforeCollect, "Invalid ViewModel instance, it is already collected.");
  180. Assert.False(afterCollect, "ViewModel instance was not collected");
  181. }
  182. static void PerformClick(Button button)
  183. {
  184. button.RaiseEvent(new KeyEventArgs
  185. {
  186. RoutedEvent = InputElement.KeyDownEvent,
  187. Key = Input.Key.Enter,
  188. });
  189. }
  190. private class ViewModel : INotifyPropertyChanged
  191. {
  192. public event PropertyChangedEventHandler PropertyChanged;
  193. public string Method() => Value = "Called";
  194. public string Method1(object i) => Value = $"Called {i}";
  195. public string Method2(int i, int j) => Value = $"Called {i},{j}";
  196. public string Method3() => Value = "Called";
  197. public string Method3(object obj) => Value = $"Called Method with parameter of object type. Argument value is {obj}";
  198. public string Value { get; private set; } = "Not called";
  199. object _parameter;
  200. public object Parameter
  201. {
  202. get => _parameter;
  203. set
  204. {
  205. if (_parameter == value)
  206. {
  207. return;
  208. }
  209. _parameter = value;
  210. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Parameter)));
  211. }
  212. }
  213. public void Do(object parameter)
  214. {
  215. Value = $"Do {parameter}";
  216. }
  217. [Metadata.DependsOn(nameof(Parameter))]
  218. public bool CanDo(object parameter)
  219. {
  220. return ReferenceEquals(null, parameter) == false;
  221. }
  222. }
  223. }
  224. }