Skip to content

Commit

Permalink
Merge pull request #1208 from unoplatform/dev/nich/template-recycling
Browse files Browse the repository at this point in the history
Check for dataContext to avoid recyclingTemplate when unneeded
  • Loading branch information
NicolasChampagne authored Aug 2, 2019
2 parents dbda38f + 590bf33 commit 5da7c8f
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 2 deletions.
1 change: 1 addition & 0 deletions doc/ReleaseNotes/_ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
* Changing the `DataContext` of an element to a new value were pushing the properties default
value on data bound properties before setting the new value.
* [Android] `.Click` on a `ButtonBase` were not raising events properly
* TemplateReuse not called when dataContext is set

## Release 1.45.0
### Features
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using Uno.UITest.Helpers;
using Uno.UITest.Helpers.Queries;

namespace SamplesApp.UITests.Windows_UI_Xaml_Controls.ToggleSwitchTests
{
[TestFixture]
public partial class ToggleSwitch_Tests : SampleControlUITestBase
{
[Test]
public void ToggleSwitch_TemplateReuseTest()
{
Run("UITests.Shared.Windows_UI_Xaml_Controls.ToggleSwitchControl.ToggleSwitch_TemplateReuse");

var toggleSwitchGroup = _app.Marked("toggleSwitchGroup");

_app.WaitForElement(toggleSwitchGroup);

var separatedToggleSwitch = _app.Marked("separatedToggleSwitch");
var unloadButton = _app.Marked("unload");
var reloadButton = _app.Marked("reload");

// Assert inital state
Assert.AreEqual("False", toggleSwitchGroup.GetDependencyPropertyValue("IsOn")?.ToString());
Assert.AreEqual("True", separatedToggleSwitch.GetDependencyPropertyValue("IsOn")?.ToString());

unloadButton.Tap();
reloadButton.Tap();

//Assert final state
Assert.AreEqual("False", toggleSwitchGroup.GetDependencyPropertyValue("IsOn")?.ToString());
Assert.AreEqual("True", separatedToggleSwitch.GetDependencyPropertyValue("IsOn")?.ToString());
}
}
}
7 changes: 7 additions & 0 deletions src/SamplesApp/UITests.Shared/UITests.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\ToggleSwitchControl\ToggleSwitch_TemplateReuse.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\DatePicker\DatePicker_SampleContent.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -2471,6 +2475,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\TextBox\TextBox_TextChanging.xaml.cs">
<DependentUpon>TextBox_TextChanging.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\ToggleSwitchControl\ToggleSwitch_TemplateReuse.xaml.cs">
<DependentUpon>ToggleSwitch_TemplateReuse.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\DatePicker\DatePicker_SampleContent.xaml.cs">
<DependentUpon>DatePicker_SampleContent.xaml</DependentUpon>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace SamplesApp.Windows_UI_Xaml_Controls.ToggleSwitchControl.Models
public class ToggleSwitchViewModel : ViewModelBase
{
private bool _isOn = true;
private bool _isOn2 = true;

public ToggleSwitchViewModel(CoreDispatcher dispatcher) : base(dispatcher)
{
Expand All @@ -22,6 +23,16 @@ public bool IsOn
}
}

public bool IsOn2
{
get => _isOn2;
set
{
_isOn2 = value;
RaisePropertyChanged();
}
}

public ICommand Flip => GetOrCreateCommand(() => IsOn = !IsOn);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<Page
x:Class="UITests.Shared.Windows_UI_Xaml_Controls.ToggleSwitchControl.ToggleSwitch_TemplateReuse"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UITests.Shared.Windows_UI_Xaml_Controls.ToggleSwitchControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Page.Resources>
<Style x:Key="BrokenToggleSwitchStyle"
TargetType="ToggleSwitch">
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="Padding"
Value="0" />
<Setter Property="FontSize"
Value="17" />
<Setter Property="BorderThickness"
Value="0,0,0,1" />
<Setter Property="FontWeight"
Value="Normal" />
<Setter Property="ManipulationMode"
Value="System,TranslateX" />
<Setter Property="UseSystemFocusVisuals"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleSwitch">
<Grid Margin="18,0"
MinHeight="50"
BorderBrush="Black"
BorderThickness="{TemplateBinding BorderThickness}"
Background="Pink">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver" />
<VisualState x:Name="Pressed" />
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="ToggleStates">
<VisualState x:Name="Dragging" />
<VisualState x:Name="Off" />
<VisualState x:Name="On">
<Storyboard>
<DoubleAnimation Duration="0"
To="16"
Storyboard.TargetProperty="X"
Storyboard.TargetName="KnobTranslateTransform" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="SwitchKnobBounds">
<DiscreteObjectKeyFrame KeyTime="0"
Value="1" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="OuterBorder">
<DiscreteObjectKeyFrame KeyTime="0"
Value="0" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="SwitchKnobOn">
<DiscreteObjectKeyFrame KeyTime="0"
Value="1" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="SwitchKnobOff">
<DiscreteObjectKeyFrame KeyTime="0"
Value="0" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ContentStates">
<VisualState x:Name="OffContent" />
<VisualState x:Name="OnContent" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
Grid.ColumnSpan="2"
AutomationProperties.AccessibilityView="Raw"
ContentTemplate="{TemplateBinding OnContentTemplate}"
Content="{TemplateBinding OnContent}"
Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<Border x:Name="OuterBorder"
Background="Blue"
Margin="1,0"
Height="14"
CornerRadius="7"
HorizontalAlignment="Left"
Width="37"
Grid.Column="1"
IsHitTestVisible="False" />
<Border x:Name="SwitchKnobBounds"
Background="WhiteSmoke"
Height="14"
Opacity="0"
CornerRadius="7"
Width="37"
Grid.Column="1"
HorizontalAlignment="Left"
IsHitTestVisible="False" />
<Grid x:Name="SwitchKnob"
HorizontalAlignment="Left"
Height="22"
Width="22"
Grid.Column="1"
IsHitTestVisible="False"
RenderTransformOrigin="0,0.5">
<Grid.RenderTransform>
<TranslateTransform x:Name="KnobTranslateTransform" />
</Grid.RenderTransform>
<Ellipse x:Name="SwitchKnobOn"
Fill="Orange"
Stroke="Brown"
StrokeThickness="1"
Height="22"
Opacity="0"
Width="22" />
<Ellipse x:Name="SwitchKnobOff"
Stroke="Yellow"
StrokeThickness="1"
Fill="SandyBrown"
Height="22"
Width="22" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>

<StackPanel>
<Button x:Name="unload">Unload</Button>
<Button x:Name="reload" IsEnabled="False">Reload</Button>
<CheckBox x:Name="chk" />
<Border x:Name="root">
<StackPanel x:Name="theStackPanel">
<ToggleSwitch x:Name="toggleSwitchGroup" IsOn="{Binding IsChecked, ElementName=chk, Mode=TwoWay}" Style="{StaticResource BrokenToggleSwitchStyle}" />
<ToggleSwitch IsOn="{Binding IsChecked, ElementName=chk, Mode=TwoWay}" Style="{StaticResource BrokenToggleSwitchStyle}" />
<TextBlock Text="{Binding IsChecked, ElementName=chk}" />
</StackPanel>
</Border>
<Border>
<ToggleSwitch x:Name="separatedToggleSwitch" IsOn="{Binding [IsOn2], Mode=TwoWay}" Style="{StaticResource BrokenToggleSwitchStyle}" />
</Border>
</StackPanel>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using SamplesApp.Windows_UI_Xaml_Controls.ToggleSwitchControl.Models;
using Uno.UI.Samples.Controls;
using Uno.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace UITests.Shared.Windows_UI_Xaml_Controls.ToggleSwitchControl
{
[SampleControlInfo("ToggleSwitch", "ToggleSwitch_TemplateReuse", typeof(ToggleSwitchViewModel))]
public sealed partial class ToggleSwitch_TemplateReuse : Page
{
public ToggleSwitch_TemplateReuse()
{
this.InitializeComponent();

SetPoolingEnabled(true);

var c = root.Child;

unload.Tapped += (snd, evt) =>
{
c = root.Child;
root.Child = null;
unload.IsEnabled = false;
reload.IsEnabled = true;
};

reload.Tapped += (snd, evt) =>
{
root.Child = c;
unload.IsEnabled = true;
reload.IsEnabled = false;
(theStackPanel as StackPanel).Children.Add(separatedToggleSwitch);
};

unload.Unloaded += (snd, evt) =>
{
SetPoolingEnabled(false);
};
}

private void SetPoolingEnabled(bool enabled)
{
#if __ANDROID || __IOS__
FeatureConfiguration.Page.IsPoolingEnabled = enabled;
FrameworkTemplatePool.IsPoolingEnabled = enabled;
#endif
}
}
}
5 changes: 3 additions & 2 deletions src/Uno.UI/UI/Xaml/FrameworkTemplatePool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,10 @@ private void OnParentChanged(object instance, object key, DependencyObjectParent

internal static void PropagateOnTemplateReused(object instance)
{
if (instance is IFrameworkTemplatePoolAware a)
// If DataContext is not null, it means it has been explicitly set (not inherited). Resetting the view could push an invalid value through 2-way binding in this case.
if (instance is IFrameworkTemplatePoolAware templateAwareElement && (instance as IFrameworkElement).DataContext == null)
{
a.OnTemplateRecycled();
templateAwareElement.OnTemplateRecycled();
}

//Try Panel.Children before ViewGroup.GetChildren - this results in fewer allocations
Expand Down

0 comments on commit 5da7c8f

Please sign in to comment.