BindingTests_Method.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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_To_Command_Collected()
  126. {
  127. WeakReference<ViewModel> MakeRef()
  128. {
  129. var weakVm = new WeakReference<ViewModel>(null);
  130. {
  131. var vm = new ViewModel()
  132. {
  133. Parameter = null,
  134. };
  135. weakVm.SetTarget(vm);
  136. var canExecuteCount = 0;
  137. var action = new Action<object>(vm.Do);
  138. var command = new Avalonia.Data.Converters.MethodToCommandConverter(action);
  139. command.CanExecuteChanged += (s, e) => canExecuteCount++;
  140. vm.Parameter = 0;
  141. Threading.Dispatcher.UIThread.RunJobs();
  142. vm.Parameter = null;
  143. Threading.Dispatcher.UIThread.RunJobs();
  144. Assert.Equal(2, canExecuteCount);
  145. }
  146. return weakVm;
  147. }
  148. bool IsAlive(WeakReference<ViewModel> @ref)
  149. {
  150. return @ref.TryGetTarget(out var instance)
  151. && instance is null == false;
  152. }
  153. var vmref = MakeRef();
  154. var beforeCollect = IsAlive(vmref);
  155. GC.Collect();
  156. GC.WaitForPendingFinalizers();
  157. var afterCollect = IsAlive(vmref);
  158. Assert.True(beforeCollect, "Invalid ViewModel instance, it is already collected.");
  159. Assert.False(afterCollect, "ViewModel instance was not collected");
  160. }
  161. static void PerformClick(Button button)
  162. {
  163. button.RaiseEvent(new KeyEventArgs
  164. {
  165. RoutedEvent = InputElement.KeyDownEvent,
  166. Key = Input.Key.Enter,
  167. });
  168. }
  169. private class ViewModel : INotifyPropertyChanged
  170. {
  171. public event PropertyChangedEventHandler PropertyChanged;
  172. public string Method() => Value = "Called";
  173. public string Method1(int i) => Value = $"Called {i}";
  174. public string Method2(int i, int j) => Value = $"Called {i},{j}";
  175. public string Value { get; private set; } = "Not called";
  176. object _parameter;
  177. public object Parameter
  178. {
  179. get
  180. {
  181. return _parameter;
  182. }
  183. set
  184. {
  185. if (_parameter == value)
  186. {
  187. return;
  188. }
  189. _parameter = value;
  190. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Parameter)));
  191. }
  192. }
  193. public void Do(object parameter)
  194. {
  195. Value = $"Do {parameter}";
  196. }
  197. [Metadata.DependsOn(nameof(Parameter))]
  198. public bool CanDo(object parameter)
  199. {
  200. return ReferenceEquals(null, parameter) == false;
  201. }
  202. }
  203. }
  204. }