TemplateBindingTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using Avalonia.Controls;
  6. using Avalonia.Controls.Presenters;
  7. using Avalonia.Controls.Primitives;
  8. using Avalonia.Controls.Templates;
  9. using Avalonia.Data;
  10. using Avalonia.Data.Converters;
  11. using Avalonia.UnitTests;
  12. using Avalonia.VisualTree;
  13. using Xunit;
  14. namespace Avalonia.Markup.UnitTests.Data
  15. {
  16. public class TemplateBindingTests
  17. {
  18. [Fact]
  19. public void OneWay_Binding_Should_Be_Set_Up()
  20. {
  21. var source = new Button
  22. {
  23. Template = new FuncControlTemplate<Button>((parent, _) =>
  24. new ContentPresenter
  25. {
  26. [~ContentPresenter.ContentProperty] = new TemplateBinding(ContentControl.ContentProperty)
  27. }),
  28. };
  29. source.ApplyTemplate();
  30. var target = (ContentPresenter)source.GetVisualChildren().Single();
  31. Assert.Null(target.Content);
  32. source.Content = "foo";
  33. Assert.Equal("foo", target.Content);
  34. source.Content = "bar";
  35. Assert.Equal("bar", target.Content);
  36. }
  37. [Fact]
  38. public void TwoWay_Binding_Should_Be_Set_Up()
  39. {
  40. var source = new Button
  41. {
  42. Template = new FuncControlTemplate<Button>((parent, _) =>
  43. new ContentPresenter
  44. {
  45. [~ContentPresenter.ContentProperty] = new TemplateBinding(ContentControl.ContentProperty)
  46. {
  47. Mode = BindingMode.TwoWay,
  48. }
  49. }),
  50. };
  51. source.ApplyTemplate();
  52. var target = (ContentPresenter)source.GetVisualChildren().Single();
  53. Assert.Null(target.Content);
  54. source.Content = "foo";
  55. Assert.Equal("foo", target.Content);
  56. target.Content = "bar";
  57. Assert.Equal("bar", source.Content);
  58. }
  59. [Fact]
  60. public void Converter_Should_Be_Used()
  61. {
  62. var source = new Button
  63. {
  64. Template = new FuncControlTemplate<Button>((parent, _) =>
  65. new ContentPresenter
  66. {
  67. [~ContentPresenter.ContentProperty] = new TemplateBinding(ContentControl.ContentProperty)
  68. {
  69. Mode = BindingMode.TwoWay,
  70. Converter = new PrefixConverter(),
  71. ConverterParameter = "Hello ",
  72. }
  73. }),
  74. };
  75. source.ApplyTemplate();
  76. var target = (ContentPresenter)source.GetVisualChildren().Single();
  77. Assert.Null(target.Content);
  78. source.Content = "foo";
  79. Assert.Equal("Hello foo", target.Content);
  80. target.Content = "Hello bar";
  81. Assert.Equal("bar", source.Content);
  82. }
  83. [Fact]
  84. public void Should_Work_Inside_Of_Tooltip()
  85. {
  86. using (UnitTestApplication.Start(TestServices.StyledWindow))
  87. {
  88. var window = new Window();
  89. var source = new Button
  90. {
  91. Template = new FuncControlTemplate<Button>((parent, _) =>
  92. new Decorator
  93. {
  94. [ToolTip.TipProperty] = new TextBlock
  95. {
  96. [~TextBlock.TextProperty] = new TemplateBinding(ContentControl.ContentProperty)
  97. }
  98. }),
  99. };
  100. window.Content = source;
  101. window.Show();
  102. try
  103. {
  104. var templateChild = (Decorator)source.GetVisualChildren().Single();
  105. ToolTip.SetIsOpen(templateChild, true);
  106. var target = (TextBlock)ToolTip.GetTip(templateChild)!;
  107. Assert.Null(target.Text);
  108. source.Content = "foo";
  109. Assert.Equal("foo", target.Text);
  110. source.Content = "bar";
  111. Assert.Equal("bar", target.Text);
  112. }
  113. finally
  114. {
  115. window.Close();
  116. }
  117. }
  118. }
  119. [Fact]
  120. public void Should_Work_Inside_Of_Popup()
  121. {
  122. using (UnitTestApplication.Start(TestServices.StyledWindow))
  123. {
  124. var window = new Window();
  125. var source = new Button
  126. {
  127. Template = new FuncControlTemplate<Button>((parent, _) =>
  128. new Popup
  129. {
  130. Child = new TextBlock
  131. {
  132. [~TextBlock.TextProperty] = new TemplateBinding(ContentControl.ContentProperty)
  133. }
  134. }),
  135. };
  136. window.Content = source;
  137. window.Show();
  138. try
  139. {
  140. var popup = (Popup)source.GetVisualChildren().Single();
  141. popup.IsOpen = true;
  142. var target = (TextBlock)popup.Child!;
  143. target[~TextBlock.TextProperty] = new TemplateBinding(ContentControl.ContentProperty);
  144. Assert.Null(target.Text);
  145. source.Content = "foo";
  146. Assert.Equal("foo", target.Text);
  147. source.Content = "bar";
  148. Assert.Equal("bar", target.Text);
  149. }
  150. finally
  151. {
  152. window.Close();
  153. }
  154. }
  155. }
  156. [Fact]
  157. public void Should_Work_Inside_Of_Flyout()
  158. {
  159. using (UnitTestApplication.Start(TestServices.StyledWindow))
  160. {
  161. var window = new Window();
  162. var source = new Button
  163. {
  164. Template = new FuncControlTemplate<Button>((parent, _) =>
  165. new Button
  166. {
  167. Flyout = new Flyout
  168. {
  169. Content = new TextBlock
  170. {
  171. [~TextBlock.TextProperty] = new TemplateBinding(ContentControl.ContentProperty)
  172. }
  173. }
  174. }),
  175. };
  176. window.Content = source;
  177. window.Show();
  178. try
  179. {
  180. var templateChild = (Button)source.GetVisualChildren().Single();
  181. templateChild.Flyout!.ShowAt(templateChild);
  182. var target = (TextBlock)((Flyout)templateChild.Flyout).Content!;
  183. target[~TextBlock.TextProperty] = new TemplateBinding(ContentControl.ContentProperty);
  184. Assert.Null(target.Text);
  185. source.Content = "foo";
  186. Assert.Equal("foo", target.Text);
  187. source.Content = "bar";
  188. Assert.Equal("bar", target.Text);
  189. }
  190. finally
  191. {
  192. window.Close();
  193. }
  194. }
  195. }
  196. [Fact]
  197. public void Should_Not_Pass_UnsetValue_To_MultiBinding_During_ApplyTemplate()
  198. {
  199. var converter = new MultiConverter();
  200. var source = new Button
  201. {
  202. Content = "foo",
  203. Template = new FuncControlTemplate<Button>((parent, _) =>
  204. new ContentPresenter
  205. {
  206. [~ContentPresenter.ContentProperty] = new MultiBinding
  207. {
  208. Converter = converter,
  209. Bindings =
  210. {
  211. new TemplateBinding(ContentControl.ContentProperty),
  212. }
  213. }
  214. }),
  215. };
  216. source.ApplyTemplate();
  217. var target = (ContentPresenter)source.GetVisualChildren().Single();
  218. // #8672 was caused by TemplateBinding passing "unset" to the MultiBinding during
  219. // ApplyTemplate as the TemplatedParent property doesn't get setup until after the
  220. // binding is initiated.
  221. Assert.Equal(new[] { "foo" }, converter.Values);
  222. }
  223. [Fact]
  224. public void Should_Execute_Converter_Without_Specific_TargetType()
  225. {
  226. // See https://github.com/AvaloniaUI/Avalonia/issues/9766
  227. var source = new Button
  228. {
  229. Template = new FuncControlTemplate<Button>((parent, _) =>
  230. new ContentPresenter
  231. {
  232. [~ContentPresenter.IsVisibleProperty] = new MultiBinding
  233. {
  234. Converter = BoolConverters.And,
  235. Bindings =
  236. {
  237. new TemplateBinding(ContentControl.ContentProperty)
  238. {
  239. Converter = ObjectConverters.IsNotNull
  240. }
  241. }
  242. }
  243. }),
  244. };
  245. source.ApplyTemplate();
  246. var target = (ContentPresenter)source.GetVisualChildren().Single();
  247. Assert.False(target.IsVisible);
  248. source.Content = "foo";
  249. Assert.True(target.IsVisible);
  250. }
  251. private class PrefixConverter : IValueConverter
  252. {
  253. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  254. {
  255. if (value != null && parameter != null)
  256. {
  257. return parameter.ToString() + value;
  258. }
  259. return null;
  260. }
  261. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  262. {
  263. if (value != null && parameter != null)
  264. {
  265. var s = value.ToString();
  266. var prefix = parameter.ToString();
  267. if (s.StartsWith(prefix) == true)
  268. {
  269. return s.Substring(prefix.Length);
  270. }
  271. return s;
  272. }
  273. return null;
  274. }
  275. }
  276. private class MultiConverter : IMultiValueConverter
  277. {
  278. public List<object> Values { get; } = new();
  279. public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
  280. {
  281. Values.AddRange(values);
  282. return values.FirstOrDefault();
  283. }
  284. }
  285. }
  286. }