Browse Source

Merge branch 'master' into fixes/7815-overlaylayer-measure

pull/7832/head
Dan Walmsley 5 years ago
committed by GitHub
parent
commit
459a3c7470
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      samples/ControlCatalog/Converter/MathSubtractConverter.cs
  2. 3
      samples/ControlCatalog/MainView.xaml
  3. 82
      samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml
  4. 19
      samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml.cs
  5. 309
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  6. 2
      samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml
  7. 15
      src/Avalonia.Base/Data/BindingOperations.cs
  8. 28
      src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
  9. 51
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/BindingSetterViewModel.cs
  10. 25
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
  11. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs
  12. 20
      src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
  13. 43
      src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml
  14. 6
      src/Avalonia.Themes.Fluent/Controls/TextBox.xaml
  15. 29
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

18
samples/ControlCatalog/Converter/MathSubtractConverter.cs

@ -0,0 +1,18 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace ControlCatalog.Converter;
public class MathSubtractConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (double)value - (double)parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

3
samples/ControlCatalog/MainView.xaml

@ -145,6 +145,9 @@
<TabItem Header="ToolTip">
<pages:ToolTipPage />
</TabItem>
<TabItem Header="TransitioningContentControl">
<pages:TransitioningContentControlPage />
</TabItem>
<TabItem Header="TreeView">
<pages:TreeViewPage />
</TabItem>

82
samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml

@ -0,0 +1,82 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:ControlCatalog.ViewModels"
xmlns:converter="clr-namespace:ControlCatalog.Converter"
xmlns:system="using:System"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="vm:TransitioningContentControlPageViewModel"
x:CompileBindings="True"
x:Class="ControlCatalog.Pages.TransitioningContentControlPage">
<UserControl.DataContext>
<vm:TransitioningContentControlPageViewModel />
</UserControl.DataContext>
<UserControl.Styles>
<Style Selector="HeaderedContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="HeaderCol" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{TemplateBinding Header}"
Grid.Column="0"
VerticalAlignment="Center" />
<ContentPresenter Content="{TemplateBinding Content}"
Grid.Column="1"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Styles>
<UserControl.Resources>
<converter:MathSubtractConverter x:Key="MathSubtractConverter" />
<system:Double x:Key="TopMargin">8</system:Double>
</UserControl.Resources>
<DockPanel LastChildFill="True"
Height="{Binding Path=Bounds.Height, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}, Converter={StaticResource MathSubtractConverter},ConverterParameter={StaticResource TopMargin}}">
<TextBlock DockPanel.Dock="Top" Classes="h2">The TransitioningContentControl control allows you to show a page transition whenever the Content changes.</TextBlock>
<ExperimentalAcrylicBorder DockPanel.Dock="Bottom" Margin="10" CornerRadius="5" >
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial BackgroundSource="Digger" TintColor="White" />
</ExperimentalAcrylicBorder.Material>
<StackPanel Margin="5" Spacing="5" Grid.IsSharedSizeScope="True">
<HeaderedContentControl Header="Select a transition">
<ComboBox Items="{Binding PageTransitions}" SelectedItem="{Binding SelectedTransition}" />
</HeaderedContentControl>
<HeaderedContentControl Header="Duration">
<NumericUpDown Value="{Binding Duration}" Increment="250" Minimum="100" />
</HeaderedContentControl>
<HeaderedContentControl Header="Clip to Bounds">
<ToggleSwitch IsChecked="{Binding ClipToBounds}" />
</HeaderedContentControl>
</StackPanel>
</ExperimentalAcrylicBorder>
<Button DockPanel.Dock="Left" Command="{Binding PrevImage}" Content="&lt;" />
<Button DockPanel.Dock="Right" Command="{Binding NextImage}" Content=">" />
<Border ClipToBounds="{Binding ClipToBounds}" Margin="5">
<TransitioningContentControl Content="{Binding SelectedImage}"
PageTransition="{Binding SelectedTransition.Transition}" >
<TransitioningContentControl.ContentTemplate>
<DataTemplate DataType="Bitmap">
<Image Source="{Binding}" />
</DataTemplate>
</TransitioningContentControl.ContentTemplate>
</TransitioningContentControl>
</Border>
</DockPanel>
</UserControl>

19
samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml.cs

@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public partial class TransitioningContentControlPage : UserControl
{
public TransitioningContentControlPage()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

309
samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs

@ -0,0 +1,309 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.VisualTree;
using MiniMvvm;
namespace ControlCatalog.ViewModels
{
public class TransitioningContentControlPageViewModel : ViewModelBase
{
public TransitioningContentControlPageViewModel()
{
var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>();
var images = new string[]
{
"delicate-arch-896885_640.jpg",
"hirsch-899118_640.jpg",
"maple-leaf-888807_640.jpg"
};
foreach (var image in images)
{
var path = $"avares://ControlCatalog/Assets/{image}";
Images.Add(new Bitmap(assetLoader.Open(new Uri(path))));
}
SetupTransitions();
SelectedTransition = PageTransitions[1];
SelectedImage = Images[0];
}
public List<PageTransition> PageTransitions { get; } = new List<PageTransition>();
public List<Bitmap> Images { get; } = new List<Bitmap>();
private Bitmap? _SelectedImage;
/// <summary>
/// Gets or Sets the selected image
/// </summary>
public Bitmap? SelectedImage
{
get { return _SelectedImage; }
set { this.RaiseAndSetIfChanged(ref _SelectedImage, value); }
}
private PageTransition _SelectedTransition;
/// <summary>
/// Gets or sets the transition to play
/// </summary>
public PageTransition SelectedTransition
{
get { return _SelectedTransition; }
set { this.RaiseAndSetIfChanged(ref _SelectedTransition, value); }
}
private bool _ClipToBounds;
/// <summary>
/// Gets or sets if the content should be clipped to bounds
/// </summary>
public bool ClipToBounds
{
get { return _ClipToBounds; }
set { this.RaiseAndSetIfChanged(ref _ClipToBounds, value); }
}
private int _Duration = 500;
/// <summary>
/// Gets or Sets the duration
/// </summary>
public int Duration
{
get { return _Duration; }
set
{
this.RaiseAndSetIfChanged(ref _Duration , value);
SetupTransitions();
}
}
private void SetupTransitions()
{
if (PageTransitions.Count == 0)
{
PageTransitions.AddRange(new[]
{
new PageTransition("None"),
new PageTransition("CrossFade"),
new PageTransition("Slide horizontally"),
new PageTransition("Slide vertically"),
new PageTransition("Composite"),
new PageTransition("Custom")
});
}
PageTransitions[1].Transition = new CrossFade(TimeSpan.FromMilliseconds(Duration));
PageTransitions[2].Transition = new PageSlide(TimeSpan.FromMilliseconds(Duration), PageSlide.SlideAxis.Horizontal);
PageTransitions[3].Transition = new PageSlide(TimeSpan.FromMilliseconds(Duration), PageSlide.SlideAxis.Vertical);
var compositeTransition = new CompositePageTransition();
compositeTransition.PageTransitions.Add(PageTransitions[1].Transition);
compositeTransition.PageTransitions.Add(PageTransitions[2].Transition);
compositeTransition.PageTransitions.Add(PageTransitions[3].Transition);
PageTransitions[4].Transition = compositeTransition;
PageTransitions[5].Transition = new CustomTransition(TimeSpan.FromMilliseconds(Duration));
}
public void NextImage()
{
var index = Images.IndexOf(SelectedImage) + 1;
if (index >= Images.Count)
{
index = 0;
}
SelectedImage = Images[index];
}
public void PrevImage()
{
var index = Images.IndexOf(SelectedImage) - 1;
if (index < 0)
{
index = Images.Count-1;
}
SelectedImage = Images[index];
}
}
public class PageTransition : ViewModelBase
{
public PageTransition(string displayTitle)
{
DisplayTitle = displayTitle;
}
public string DisplayTitle { get; }
private IPageTransition _Transition;
/// <summary>
/// Gets or sets the transition
/// </summary>
public IPageTransition Transition
{
get { return _Transition; }
set { this.RaiseAndSetIfChanged(ref _Transition, value); }
}
public override string ToString()
{
return DisplayTitle;
}
}
public class CustomTransition : IPageTransition
{
/// <summary>
/// Initializes a new instance of the <see cref="CustomTransition"/> class.
/// </summary>
public CustomTransition()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomTransition"/> class.
/// </summary>
/// <param name="duration">The duration of the animation.</param>
public CustomTransition(TimeSpan duration)
{
Duration = duration;
}
/// <summary>
/// Gets the duration of the animation.
/// </summary>
public TimeSpan Duration { get; set; }
public async Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
var tasks = new List<Task>();
var parent = GetVisualParent(from, to);
var scaleProperty = ScaleTransform.ScaleYProperty;
if (from != null)
{
var animation = new Animation
{
FillMode = FillMode.Forward,
Children =
{
new KeyFrame
{
Setters = { new Setter { Property = scaleProperty, Value = 1d } },
Cue = new Cue(0d)
},
new KeyFrame
{
Setters =
{
new Setter
{
Property = scaleProperty,
Value = 0d
}
},
Cue = new Cue(1d)
}
},
Duration = Duration
};
tasks.Add(animation.RunAsync(from, null, cancellationToken));
}
if (to != null)
{
to.IsVisible = true;
var animation = new Animation
{
FillMode = FillMode.Forward,
Children =
{
new KeyFrame
{
Setters =
{
new Setter
{
Property = scaleProperty,
Value = 0d
}
},
Cue = new Cue(0d)
},
new KeyFrame
{
Setters = { new Setter { Property = scaleProperty, Value = 1d } },
Cue = new Cue(1d)
}
},
Duration = Duration
};
tasks.Add(animation.RunAsync(to, null, cancellationToken));
}
await Task.WhenAll(tasks);
if (from != null && !cancellationToken.IsCancellationRequested)
{
from.IsVisible = false;
}
}
/// <summary>
/// Gets the common visual parent of the two control.
/// </summary>
/// <param name="from">The from control.</param>
/// <param name="to">The to control.</param>
/// <returns>The common parent.</returns>
/// <exception cref="ArgumentException">
/// The two controls do not share a common parent.
/// </exception>
/// <remarks>
/// Any one of the parameters may be null, but not both.
/// </remarks>
private static IVisual GetVisualParent(IVisual? from, IVisual? to)
{
var p1 = (from ?? to)!.VisualParent;
var p2 = (to ?? from)!.VisualParent;
if (p1 != null && p2 != null && p1 != p2)
{
throw new ArgumentException("Controls for PageSlide must have same parent.");
}
return p1 ?? throw new InvalidOperationException("Cannot determine visual parent.");
}
}
}

2
samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml

@ -22,7 +22,7 @@
<Styles.Resources>
<x:Double x:Key="PaneCompactWidth">40</x:Double>
<x:Double x:Key="PaneExpandWidth">200</x:Double>
<x:Double x:Key="PaneExpandWidth">220</x:Double>
<x:Double x:Key="HeaderHeight">36</x:Double>
<x:Double x:Key="NavigationItemHeight">36</x:Double>
<x:Double x:Key="HamburgerMenuButtonHeight">32</x:Double>

15
src/Avalonia.Base/Data/BindingOperations.cs

@ -99,14 +99,15 @@ namespace Avalonia.Data
private sealed class TwoWayBindingDisposable : IDisposable
{
private readonly IDisposable _first;
private readonly IDisposable _second;
private readonly IDisposable _toTargetSubscription;
private readonly IDisposable _fromTargetSubsription;
private bool _isDisposed;
public TwoWayBindingDisposable(IDisposable first, IDisposable second)
public TwoWayBindingDisposable(IDisposable toTargetSubscription, IDisposable fromTargetSubsription)
{
_first = first;
_second = second;
_toTargetSubscription = toTargetSubscription;
_fromTargetSubsription = fromTargetSubsription;
}
public void Dispose()
@ -116,8 +117,8 @@ namespace Avalonia.Data
return;
}
_first.Dispose();
_second.Dispose();
_fromTargetSubsription.Dispose();
_toTargetSubscription.Dispose();
_isDisposed = true;
}

28
src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs

@ -2,6 +2,8 @@
using System.Globalization;
using System.Linq;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives
@ -58,18 +60,17 @@ namespace Avalonia.Controls.Primitives
private Vector _offset;
private bool _hasInit;
private bool _suppressUpdateOffset;
private ListBoxItem? _pressedItem;
public DateTimePickerPanel()
{
FormatDate = DateTime.Now;
AddHandler(ListBoxItem.PointerPressedEvent, OnItemPointerDown, Avalonia.Interactivity.RoutingStrategies.Bubble);
AddHandler(ListBoxItem.PointerReleasedEvent, OnItemPointerUp, Avalonia.Interactivity.RoutingStrategies.Bubble);
AddHandler(TappedEvent, OnItemTapped, RoutingStrategies.Bubble);
}
static DateTimePickerPanel()
{
FocusableProperty.OverrideDefaultValue<DateTimePickerPanel>(true);
BackgroundProperty.OverrideDefaultValue<DateTimePickerPanel>(Brushes.Transparent);
AffectsMeasure<DateTimePickerPanel>(ItemHeightProperty);
}
@ -523,26 +524,13 @@ namespace Avalonia.Controls.Primitives
return newValue;
}
private void OnItemPointerDown(object? sender, PointerPressedEventArgs e)
private void OnItemTapped(object? sender, TappedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed &&
e.Source is IVisual source)
{
_pressedItem = GetItemFromSource(source);
e.Handled = true;
}
}
private void OnItemPointerUp(object? sender, PointerReleasedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased &&
_pressedItem != null &&
e.Source is IVisual source &&
GetItemFromSource(source) is ListBoxItem item &&
item.Tag is int tag)
if (e.Source is IVisual source &&
GetItemFromSource(source) is ListBoxItem listBoxItem &&
listBoxItem.Tag is int tag)
{
SelectedValue = tag;
_pressedItem = null;
e.Handled = true;
}
}

51
src/Avalonia.Diagnostics/Diagnostics/ViewModels/BindingSetterViewModel.cs

@ -0,0 +1,51 @@
using System;
using Avalonia.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Media;
namespace Avalonia.Diagnostics.ViewModels
{
internal class BindingSetterViewModel : SetterViewModel
{
public BindingSetterViewModel(AvaloniaProperty property, object? value) : base(property, value)
{
switch (value)
{
case Binding binding:
Path = binding.Path;
Tint = Brushes.CornflowerBlue;
break;
case CompiledBindingExtension binding:
Path = binding.Path.ToString();
Tint = Brushes.DarkGreen;
break;
case TemplateBinding binding:
if (binding.Property is AvaloniaProperty templateProperty)
{
Path = $"{templateProperty.OwnerType.Name}.{templateProperty.Name}";
}
else
{
Path = "Unassigned";
}
Tint = Brushes.OrangeRed;
break;
default:
throw new ArgumentException("Invalid binding type", nameof(value));
}
}
public IBrush Tint { get; }
public string Path { get; }
public override void CopyValue()
{
CopyToClipboard(Path);
}
}
}

25
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs

@ -8,6 +8,7 @@ using System.Reflection;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Styling;
using Avalonia.VisualTree;
@ -87,7 +88,16 @@ namespace Avalonia.Diagnostics.ViewModels
}
else
{
setterVm = new SetterViewModel(regularSetter.Property, setterValue);
var isBinding = IsBinding(setterValue);
if (isBinding)
{
setterVm = new BindingSetterViewModel(regularSetter.Property, setterValue);
}
else
{
setterVm = new SetterViewModel(regularSetter.Property, setterValue);
}
}
setters.Add(setterVm);
@ -117,6 +127,19 @@ namespace Avalonia.Diagnostics.ViewModels
return null;
}
private bool IsBinding(object? value)
{
switch (value)
{
case Binding:
case CompiledBindingExtension:
case TemplateBinding:
return true;
}
return false;
}
public TreePageViewModel TreePage { get; }
public DataGridCollectionView? PropertiesView

2
src/Avalonia.Diagnostics/Diagnostics/ViewModels/SetterViewModel.cs

@ -34,7 +34,7 @@ namespace Avalonia.Diagnostics.ViewModels
IsVisible = true;
}
public void CopyValue()
public virtual void CopyValue()
{
var textToCopy = Value?.ToString();

20
src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml

@ -138,6 +138,26 @@
</StackPanel>
</DataTemplate>
<DataTemplate DataType="vm:BindingSetterViewModel">
<Panel Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacity}}" IsVisible="{Binding IsVisible}" HorizontalAlignment="Left">
<Panel.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy property name" Command="{Binding CopyPropertyName} "/>
<MenuItem Header="Copy value" Command="{Binding CopyValue} "/>
</ContextMenu>
</Panel.ContextMenu>
<StackPanel Orientation="Horizontal" Spacing="2">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
<TextBlock Text=":" />
<TextBlock>{</TextBlock>
<Rectangle Height="8" Width="8" VerticalAlignment="Center" Fill="{Binding Tint}"/>
<TextBlock Text="{Binding Path}" />
<TextBlock>}</TextBlock>
</StackPanel>
<Rectangle Height="1" Fill="#6C6C6C" VerticalAlignment="Center" IsVisible="{Binding !IsActive}" />
</Panel>
</DataTemplate>
<DataTemplate DataType="Color">
<StackPanel Orientation="Horizontal" Spacing="2">
<Border BorderThickness="1" BorderBrush="Black" Width="8" Height="8">

43
src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml

@ -101,23 +101,17 @@
IsVisible="False"
HorizontalAlignment="Right" />
<Viewbox UseLayoutRounding="False"
MinHeight="{DynamicResource ComboBoxMinHeight}"
Grid.Row="1"
Grid.Column="1"
IsHitTestVisible="False"
Margin="0,0,10,0"
Height="12"
Width="12"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<Panel>
<Panel Height="12"
Width="12" />
<Path x:Name="DropDownGlyph"
<PathIcon x:Name="DropDownGlyph"
Grid.Row="1"
Grid.Column="1"
UseLayoutRounding="False"
IsHitTestVisible="False"
Height="12"
Width="12"
Margin="0,0,10,0"
HorizontalAlignment="Right"
VerticalAlignment="Center" />
</Panel>
</Viewbox>
<Popup Name="PART_Popup"
WindowManagerAddShadowHint="False"
IsOpen="{TemplateBinding IsDropDownOpen, Mode=TwoWay}"
@ -159,10 +153,9 @@
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="ComboBox /template/ Path#DropDownGlyph">
<Setter Property="Fill" Value="{DynamicResource ComboBoxDropDownGlyphForeground}" />
<Style Selector="ComboBox /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForeground}" />
<Setter Property="Data" Value="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z" />
<Setter Property="Stretch" Value="Uniform" />
</Style>
<!-- PointerOver State -->
@ -195,8 +188,8 @@
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForegroundDisabled}" />
</Style>
<Style Selector="ComboBox:disabled /template/ Path#DropDownGlyph">
<Setter Property="Fill" Value="{DynamicResource ComboBoxDropDownGlyphForegroundDisabled}" />
<Style Selector="ComboBox:disabled /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundDisabled}" />
</Style>
<!-- Focused State -->
@ -213,8 +206,8 @@
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxForegroundFocused}" />
</Style>
<Style Selector="ComboBox:focus-visible /template/ Path#DropDownGlyph">
<Setter Property="Fill" Value="{DynamicResource ComboBoxDropDownGlyphForegroundFocused}" />
<Style Selector="ComboBox:focus-visible /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundFocused}" />
</Style>
<!-- Focus Pressed State -->
@ -226,8 +219,8 @@
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxPlaceHolderForegroundFocusedPressed}" />
</Style>
<Style Selector="ComboBox:focused:pressed /template/ Path#DropDownGlyph">
<Setter Property="Fill" Value="{DynamicResource ComboBoxDropDownGlyphForegroundFocusedPressed}" />
<Style Selector="ComboBox:focused:pressed /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundFocusedPressed}" />
</Style>
<!-- Error State -->

6
src/Avalonia.Themes.Fluent/Controls/TextBox.xaml

@ -1,4 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
<Design.PreviewWith>
<Border Padding="20">
<TextBox Text="Sample"
@ -168,7 +170,7 @@
<Setter Property="InnerRightContent">
<Template>
<ToggleButton Classes="passwordBoxRevealButton"
IsChecked="{CompiledBinding $parent[TextBox].RevealPassword, Mode=TwoWay}" />
IsChecked="{Binding $parent[TextBox].RevealPassword, Mode=TwoWay}" />
</Template>
</Setter>
</Style>

29
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -829,6 +829,30 @@ namespace Avalonia.Base.UnitTests
subscription.Dispose();
}
[Fact]
public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_With_Value()
{
var target = new Class1();
var source = new TestTwoWayBindingViewModel() { Value = 1 };
source.ResetSetterCalled();
target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
Assert.False(source.SetterCalled);
}
[Fact]
public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_Indexer_With_Value()
{
var target = new Class1();
var source = new TestTwoWayBindingViewModel() { [0] = 1 };
source.ResetSetterCalled();
target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source });
Assert.False(source.SetterCalled);
}
/// <summary>
/// Returns an observable that returns a single value but does not complete.
/// </summary>
@ -943,6 +967,11 @@ namespace Avalonia.Base.UnitTests
}
public bool SetterCalled { get; private set; }
public void ResetSetterCalled()
{
SetterCalled = false;
}
}
}
}

Loading…
Cancel
Save