diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml
index d045626c2c..cacc2204bd 100644
--- a/samples/ControlCatalog/Pages/DataGridPage.xaml
+++ b/samples/ControlCatalog/Pages/DataGridPage.xaml
@@ -11,12 +11,17 @@
-
+
DataGrid
A control for displaying and interacting with a data source.
-
+
+
+
+
+
+
@@ -39,13 +44,13 @@
-
+
-
-
-
+
+
+
diff --git a/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
new file mode 100644
index 0000000000..82472c505a
--- /dev/null
+++ b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
@@ -0,0 +1,5 @@
+Compat issues with assembly Avalonia.Controls.DataGrid:
+MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.DataGridTextColumn.FontFamilyProperty' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public System.String Avalonia.Controls.DataGridTextColumn.FontFamily.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Controls.DataGridTextColumn.FontFamily.set(System.String)' does not exist in the implementation but it does exist in the contract.
+Total Issues: 3
diff --git a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
index f1bbea9949..e2a067ac61 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
@@ -17,9 +17,7 @@ namespace Avalonia.Controls
///
public class DataGridCheckBoxColumn : DataGridBoundColumn
{
-
private bool _beganEditWithKeyboard;
- private bool _isThreeState;
private CheckBox _currentCheckBox;
private DataGrid _owningGrid;
@@ -31,6 +29,12 @@ namespace Avalonia.Controls
BindingTarget = CheckBox.IsCheckedProperty;
}
+ ///
+ /// Defines the property.
+ ///
+ public static StyledProperty IsThreeStateProperty =
+ CheckBox.IsThreeStateProperty.AddOwner();
+
///
/// Gets or sets a value that indicates whether the hosted controls allow three states or two.
///
@@ -39,17 +43,17 @@ namespace Avalonia.Controls
///
public bool IsThreeState
{
- get
- {
- return _isThreeState;
- }
- set
+ get => GetValue(IsThreeStateProperty);
+ set => SetValue(IsThreeStateProperty, value);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == IsThreeStateProperty)
{
- if (_isThreeState != value)
- {
- _isThreeState = value;
- NotifyPropertyChanged(nameof(IsThreeState));
- }
+ NotifyPropertyChanged(change.Property.Name);
}
}
@@ -203,9 +207,9 @@ namespace Avalonia.Controls
{
throw new ArgumentNullException("element");
}
- if(element is CheckBox checkBox)
+ if (element is CheckBox checkBox)
{
- checkBox.IsThreeState = IsThreeState;
+ DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
}
else
{
@@ -229,7 +233,7 @@ namespace Avalonia.Controls
{
checkBox.HorizontalAlignment = HorizontalAlignment.Center;
checkBox.VerticalAlignment = VerticalAlignment.Center;
- checkBox.IsThreeState = IsThreeState;
+ DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
}
private bool EnsureOwningGrid()
diff --git a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
index d31204b9e6..1cf6ab68ac 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
@@ -20,11 +20,6 @@ namespace Avalonia.Controls
{
private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin";
- private double? _fontSize;
- private FontStyle? _fontStyle;
- private FontWeight? _fontWeight;
- private IBrush _foreground;
-
///
/// Initializes a new instance of the class.
///
@@ -36,18 +31,24 @@ namespace Avalonia.Controls
///
/// Identifies the FontFamily dependency property.
///
- public static readonly StyledProperty FontFamilyProperty =
- AvaloniaProperty.Register(nameof(FontFamily));
+ public static readonly AttachedProperty FontFamilyProperty =
+ TextBlock.FontFamilyProperty.AddOwner();
///
/// Gets or sets the font name.
///
- public string FontFamily
+ public FontFamily FontFamily
{
- get { return GetValue(FontFamilyProperty); }
- set { SetValue(FontFamilyProperty, value); }
+ get => GetValue(FontFamilyProperty);
+ set => SetValue(FontFamilyProperty, value);
}
+ ///
+ /// Identifies the FontSize dependency property.
+ ///
+ public static readonly AttachedProperty FontSizeProperty =
+ TextBlock.FontSizeProperty.AddOwner();
+
///
/// Gets or sets the font size.
///
@@ -55,74 +56,66 @@ namespace Avalonia.Controls
[DefaultValue(double.NaN)]
public double FontSize
{
- get
- {
- return _fontSize ?? Double.NaN;
- }
- set
- {
- if (_fontSize != value)
- {
- _fontSize = value;
- NotifyPropertyChanged(nameof(FontSize));
- }
- }
+ get => GetValue(FontSizeProperty);
+ set => SetValue(FontSizeProperty, value);
}
+ ///
+ /// Identifies the FontStyle dependency property.
+ ///
+ public static readonly AttachedProperty FontStyleProperty =
+ TextBlock.FontStyleProperty.AddOwner();
+
///
/// Gets or sets the font style.
///
public FontStyle FontStyle
{
- get
- {
- return _fontStyle ?? FontStyle.Normal;
- }
- set
- {
- if (_fontStyle != value)
- {
- _fontStyle = value;
- NotifyPropertyChanged(nameof(FontStyle));
- }
- }
+ get => GetValue(FontStyleProperty);
+ set => SetValue(FontStyleProperty, value);
}
+ ///
+ /// Identifies the FontWeight dependency property.
+ ///
+ public static readonly AttachedProperty FontWeightProperty =
+ TextBlock.FontWeightProperty.AddOwner();
+
///
/// Gets or sets the font weight or thickness.
///
public FontWeight FontWeight
{
- get
- {
- return _fontWeight ?? FontWeight.Normal;
- }
- set
- {
- if (_fontWeight != value)
- {
- _fontWeight = value;
- NotifyPropertyChanged(nameof(FontWeight));
- }
- }
+ get => GetValue(FontWeightProperty);
+ set => SetValue(FontWeightProperty, value);
}
+ ///
+ /// Identifies the Foreground dependency property.
+ ///
+ public static readonly AttachedProperty ForegroundProperty =
+ TextBlock.ForegroundProperty.AddOwner();
+
///
/// Gets or sets a brush that describes the foreground of the column cells.
///
public IBrush Foreground
{
- get
- {
- return _foreground;
- }
- set
+ get => GetValue(ForegroundProperty);
+ set => SetValue(ForegroundProperty, value);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == FontFamilyProperty
+ || change.Property == FontSizeProperty
+ || change.Property == FontStyleProperty
+ || change.Property == FontWeightProperty
+ || change.Property == ForegroundProperty)
{
- if (_foreground != value)
- {
- _foreground = value;
- NotifyPropertyChanged(nameof(Foreground));
- }
+ NotifyPropertyChanged(change.Property.Name);
}
}
@@ -154,26 +147,7 @@ namespace Avalonia.Controls
Background = new SolidColorBrush(Colors.Transparent)
};
- if (IsSet(FontFamilyProperty))
- {
- textBox.FontFamily = FontFamily;
- }
- if (_fontSize.HasValue)
- {
- textBox.FontSize = _fontSize.Value;
- }
- if (_fontStyle.HasValue)
- {
- textBox.FontStyle = _fontStyle.Value;
- }
- if (_fontWeight.HasValue)
- {
- textBox.FontWeight = _fontWeight.Value;
- }
- if (_foreground != null)
- {
- textBox.Foreground = _foreground;
- }
+ SyncProperties(textBox);
return textBox;
}
@@ -192,26 +166,8 @@ namespace Avalonia.Controls
VerticalAlignment = VerticalAlignment.Center
};
- if (IsSet(FontFamilyProperty))
- {
- textBlockElement.FontFamily = FontFamily;
- }
- if (_fontSize.HasValue)
- {
- textBlockElement.FontSize = _fontSize.Value;
- }
- if (_fontStyle.HasValue)
- {
- textBlockElement.FontStyle = _fontStyle.Value;
- }
- if (_fontWeight.HasValue)
- {
- textBlockElement.FontWeight = _fontWeight.Value;
- }
- if (_foreground != null)
- {
- textBlockElement.Foreground = _foreground;
- }
+ SyncProperties(textBlockElement);
+
if (Binding != null)
{
textBlockElement.Bind(TextBlock.TextProperty, Binding);
@@ -261,99 +217,42 @@ namespace Avalonia.Controls
throw new ArgumentNullException("element");
}
- if(element is TextBox textBox)
+ if (element is AvaloniaObject content)
{
if (propertyName == nameof(FontFamily))
{
- textBox.FontFamily = FontFamily;
+ DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
}
else if (propertyName == nameof(FontSize))
{
- SetTextFontSize(textBox, TextBox.FontSizeProperty);
+ DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
}
else if (propertyName == nameof(FontStyle))
{
- textBox.FontStyle = FontStyle;
+ DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
}
else if (propertyName == nameof(FontWeight))
{
- textBox.FontWeight = FontWeight;
+ DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
}
else if (propertyName == nameof(Foreground))
{
- textBox.Foreground = Foreground;
- }
- else
- {
- if (FontFamily != null)
- {
- textBox.FontFamily = FontFamily;
- }
- SetTextFontSize(textBox, TextBox.FontSizeProperty);
- textBox.FontStyle = FontStyle;
- textBox.FontWeight = FontWeight;
- if (Foreground != null)
- {
- textBox.Foreground = Foreground;
- }
- }
-
- }
- else if (element is TextBlock textBlock)
- {
- if (propertyName == nameof(FontFamily))
- {
- textBlock.FontFamily = FontFamily;
- }
- else if (propertyName == nameof(FontSize))
- {
- SetTextFontSize(textBlock, TextBlock.FontSizeProperty);
- }
- else if (propertyName == nameof(FontStyle))
- {
- textBlock.FontStyle = FontStyle;
- }
- else if (propertyName == nameof(FontWeight))
- {
- textBlock.FontWeight = FontWeight;
- }
- else if (propertyName == nameof(Foreground))
- {
- textBlock.Foreground = Foreground;
- }
- else
- {
- if (FontFamily != null)
- {
- textBlock.FontFamily = FontFamily;
- }
- SetTextFontSize(textBlock, TextBlock.FontSizeProperty);
- textBlock.FontStyle = FontStyle;
- textBlock.FontWeight = FontWeight;
- if (Foreground != null)
- {
- textBlock.Foreground = Foreground;
- }
+ DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
}
}
else
{
- throw DataGridError.DataGrid.ValueIsNotAnInstanceOfEitherOr("element", typeof(TextBox), typeof(TextBlock));
+ throw DataGridError.DataGrid.ValueIsNotAnInstanceOf("element", typeof(AvaloniaObject));
}
}
- private void SetTextFontSize(AvaloniaObject textElement, AvaloniaProperty fontSizeProperty)
+ private void SyncProperties(AvaloniaObject content)
{
- double newFontSize = FontSize;
- if (double.IsNaN(newFontSize))
- {
- textElement.ClearValue(fontSizeProperty);
- }
- else
- {
- textElement.SetValue(fontSizeProperty, newFontSize);
- }
+ DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
+ DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
+ DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
+ DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
+ DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
}
-
}
}
diff --git a/src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs b/src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs
new file mode 100644
index 0000000000..2cf41082ca
--- /dev/null
+++ b/src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs
@@ -0,0 +1,22 @@
+namespace Avalonia.Controls
+{
+ internal static class DataGridHelper
+ {
+ internal static void SyncColumnProperty(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty property)
+ {
+ SyncColumnProperty(column, content, property, property);
+ }
+
+ internal static void SyncColumnProperty(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty contentProperty, AvaloniaProperty columnProperty)
+ {
+ if (!column.IsSet(columnProperty))
+ {
+ content.ClearValue(contentProperty);
+ }
+ else
+ {
+ content.SetValue(contentProperty, column.GetValue(columnProperty));
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index a676892384..1e5e80d144 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -128,6 +128,7 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty TopmostProperty =
AvaloniaProperty.Register(nameof(Topmost));
+ private bool _isOpenRequested = false;
private bool _isOpen;
private bool _ignoreIsOpenChanged;
private PopupOpenState? _openState;
@@ -361,17 +362,19 @@ namespace Avalonia.Controls.Primitives
if (placementTarget == null)
{
- throw new InvalidOperationException("Popup has no logical parent and PlacementTarget is null");
+ _isOpenRequested = true;
+ return;
}
var topLevel = placementTarget.VisualRoot as TopLevel;
if (topLevel == null)
{
- throw new InvalidOperationException(
- "Attempted to open a popup not attached to a TopLevel");
+ _isOpenRequested = true;
+ return;
}
+ _isOpenRequested = false;
var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
var handlerCleanup = new CompositeDisposable(5);
@@ -492,6 +495,17 @@ namespace Avalonia.Controls.Primitives
return new Size();
}
+
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ if (_isOpenRequested)
+ {
+ Open();
+ }
+ }
+
///
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
@@ -552,6 +566,7 @@ namespace Avalonia.Controls.Primitives
private void CloseCore()
{
+ _isOpenRequested = false;
if (_openState is null)
{
using (BeginIgnoringIsOpen())
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
index 53a8db2176..f032186bcd 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
@@ -21,7 +21,50 @@ namespace Avalonia.Controls.UnitTests.Primitives
public class PopupTests
{
protected bool UsePopupHost;
-
+
+ [Fact]
+ public void Popup_Open_Without_Target_Should_Attach_Itself_Later()
+ {
+ using (CreateServices())
+ {
+ int openedEvent = 0;
+ var target = new Popup();
+ target.Opened += (s, a) => openedEvent++;
+ target.IsOpen = true;
+
+ var window = PreparedWindow(target);
+ window.Show();
+ Assert.Equal(1, openedEvent);
+ }
+ }
+
+ [Fact]
+ public void Popup_Without_TopLevel_Shouldnt_Call_Open()
+ {
+ int openedEvent = 0;
+ var target = new Popup();
+ target.Opened += (s, a) => openedEvent++;
+ target.IsOpen = true;
+
+ Assert.Equal(0, openedEvent);
+ }
+
+ [Fact]
+ public void Opening_Popup_Shouldnt_Throw_When_Not_In_Visual_Tree()
+ {
+ var target = new Popup();
+ target.IsOpen = true;
+ }
+
+ [Fact]
+ public void Opening_Popup_Shouldnt_Throw_When_In_Tree_Without_TopLevel()
+ {
+ Control c = new Control();
+ var target = new Popup();
+ ((ISetLogicalParent)target).SetParent(c);
+ target.IsOpen = true;
+ }
+
[Fact]
public void Setting_Child_Should_Set_Child_Controls_LogicalParent()
{