Browse Source

Merge branch 'master' into control-validation-message-fix

pull/4704/head
Steven Kirk 6 years ago
committed by GitHub
parent
commit
7350909939
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      samples/ControlCatalog/Pages/DataGridPage.xaml
  2. 5
      src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
  3. 34
      src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
  4. 231
      src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
  5. 22
      src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs
  6. 21
      src/Avalonia.Controls/Primitives/Popup.cs
  7. 45
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

17
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -11,12 +11,17 @@
<Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" /> <Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" />
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<Grid RowDefinitions="Auto,*"> <Grid RowDefinitions="Auto,Auto,*">
<StackPanel Orientation="Vertical" Spacing="4" Grid.Row="0"> <StackPanel Orientation="Vertical" Spacing="4" Grid.Row="0">
<TextBlock Classes="h1">DataGrid</TextBlock> <TextBlock Classes="h1">DataGrid</TextBlock>
<TextBlock Classes="h2">A control for displaying and interacting with a data source.</TextBlock> <TextBlock Classes="h2">A control for displaying and interacting with a data source.</TextBlock>
</StackPanel> </StackPanel>
<TabControl Grid.Row="1"> <StackPanel Grid.Row="1" Spacing="4" Orientation="Horizontal" IsVisible="{Binding #EditableTab.IsSelected}">
<TextBlock Text="FontSize:" VerticalAlignment="Center"/>
<Slider x:Name="FontSizeSlider" Minimum="5" Maximum="30" Value="14" Width="100" VerticalAlignment="Center" />
<CheckBox x:Name="IsThreeStateCheckBox" IsChecked="False" Content="IsThreeState"/>
</StackPanel>
<TabControl Grid.Row="2">
<TabItem Header="DataGrid"> <TabItem Header="DataGrid">
<DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"> <DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All">
<DataGrid.Columns> <DataGrid.Columns>
@ -39,13 +44,13 @@
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
</TabItem> </TabItem>
<TabItem Header="Editable"> <TabItem x:Name="EditableTab" Header="Editable">
<Grid RowDefinitions="*,Auto"> <Grid RowDefinitions="*,Auto">
<DataGrid Name="dataGridEdit" Margin="12" Grid.Row="0"> <DataGrid Name="dataGridEdit" Margin="12" Grid.Row="0">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="2*" /> <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" />
<DataGridTextColumn Header="Last" Binding="{Binding LastName}" Width="2*" /> <DataGridTextColumn Header="Last" Binding="{Binding LastName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" />
<DataGridCheckBoxColumn Header="Is Banned" Binding="{Binding IsBanned}" Width="*" /> <DataGridCheckBoxColumn Header="Is Banned" Binding="{Binding IsBanned}" Width="*" IsThreeState="{Binding #IsThreeStateCheckBox.IsChecked, Mode=OneWay}" />
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
<Button Grid.Row="1" Name="btnAdd" Margin="12,0,12,12" Content="Add" HorizontalAlignment="Right" /> <Button Grid.Row="1" Name="btnAdd" Margin="12,0,12,12" Content="Add" HorizontalAlignment="Right" />

5
src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt

@ -0,0 +1,5 @@
Compat issues with assembly Avalonia.Controls.DataGrid:
MembersMustExist : Member 'public Avalonia.StyledProperty<System.String> Avalonia.StyledProperty<System.String> 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

34
src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs

@ -17,9 +17,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public class DataGridCheckBoxColumn : DataGridBoundColumn public class DataGridCheckBoxColumn : DataGridBoundColumn
{ {
private bool _beganEditWithKeyboard; private bool _beganEditWithKeyboard;
private bool _isThreeState;
private CheckBox _currentCheckBox; private CheckBox _currentCheckBox;
private DataGrid _owningGrid; private DataGrid _owningGrid;
@ -31,6 +29,12 @@ namespace Avalonia.Controls
BindingTarget = CheckBox.IsCheckedProperty; BindingTarget = CheckBox.IsCheckedProperty;
} }
/// <summary>
/// Defines the <see cref="IsThreeState"/> property.
/// </summary>
public static StyledProperty<bool> IsThreeStateProperty =
CheckBox.IsThreeStateProperty.AddOwner<DataGridCheckBoxColumn>();
/// <summary> /// <summary>
/// Gets or sets a value that indicates whether the hosted <see cref="T:System.Windows.Controls.CheckBox" /> controls allow three states or two. /// Gets or sets a value that indicates whether the hosted <see cref="T:System.Windows.Controls.CheckBox" /> controls allow three states or two.
/// </summary> /// </summary>
@ -39,17 +43,17 @@ namespace Avalonia.Controls
/// </returns> /// </returns>
public bool IsThreeState public bool IsThreeState
{ {
get get => GetValue(IsThreeStateProperty);
{ set => SetValue(IsThreeStateProperty, value);
return _isThreeState; }
}
set protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == IsThreeStateProperty)
{ {
if (_isThreeState != value) NotifyPropertyChanged(change.Property.Name);
{
_isThreeState = value;
NotifyPropertyChanged(nameof(IsThreeState));
}
} }
} }
@ -203,9 +207,9 @@ namespace Avalonia.Controls
{ {
throw new ArgumentNullException("element"); throw new ArgumentNullException("element");
} }
if(element is CheckBox checkBox) if (element is CheckBox checkBox)
{ {
checkBox.IsThreeState = IsThreeState; DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
} }
else else
{ {
@ -229,7 +233,7 @@ namespace Avalonia.Controls
{ {
checkBox.HorizontalAlignment = HorizontalAlignment.Center; checkBox.HorizontalAlignment = HorizontalAlignment.Center;
checkBox.VerticalAlignment = VerticalAlignment.Center; checkBox.VerticalAlignment = VerticalAlignment.Center;
checkBox.IsThreeState = IsThreeState; DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
} }
private bool EnsureOwningGrid() private bool EnsureOwningGrid()

231
src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs

@ -20,11 +20,6 @@ namespace Avalonia.Controls
{ {
private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin"; private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin";
private double? _fontSize;
private FontStyle? _fontStyle;
private FontWeight? _fontWeight;
private IBrush _foreground;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridTextColumn" /> class. /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridTextColumn" /> class.
/// </summary> /// </summary>
@ -36,18 +31,24 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Identifies the FontFamily dependency property. /// Identifies the FontFamily dependency property.
/// </summary> /// </summary>
public static readonly StyledProperty<string> FontFamilyProperty = public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
AvaloniaProperty.Register<DataGridTextColumn, string>(nameof(FontFamily)); TextBlock.FontFamilyProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets the font name. /// Gets or sets the font name.
/// </summary> /// </summary>
public string FontFamily public FontFamily FontFamily
{ {
get { return GetValue(FontFamilyProperty); } get => GetValue(FontFamilyProperty);
set { SetValue(FontFamilyProperty, value); } set => SetValue(FontFamilyProperty, value);
} }
/// <summary>
/// Identifies the FontSize dependency property.
/// </summary>
public static readonly AttachedProperty<double> FontSizeProperty =
TextBlock.FontSizeProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets the font size. /// Gets or sets the font size.
/// </summary> /// </summary>
@ -55,74 +56,66 @@ namespace Avalonia.Controls
[DefaultValue(double.NaN)] [DefaultValue(double.NaN)]
public double FontSize public double FontSize
{ {
get get => GetValue(FontSizeProperty);
{ set => SetValue(FontSizeProperty, value);
return _fontSize ?? Double.NaN;
}
set
{
if (_fontSize != value)
{
_fontSize = value;
NotifyPropertyChanged(nameof(FontSize));
}
}
} }
/// <summary>
/// Identifies the FontStyle dependency property.
/// </summary>
public static readonly AttachedProperty<FontStyle> FontStyleProperty =
TextBlock.FontStyleProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets the font style. /// Gets or sets the font style.
/// </summary> /// </summary>
public FontStyle FontStyle public FontStyle FontStyle
{ {
get get => GetValue(FontStyleProperty);
{ set => SetValue(FontStyleProperty, value);
return _fontStyle ?? FontStyle.Normal;
}
set
{
if (_fontStyle != value)
{
_fontStyle = value;
NotifyPropertyChanged(nameof(FontStyle));
}
}
} }
/// <summary>
/// Identifies the FontWeight dependency property.
/// </summary>
public static readonly AttachedProperty<FontWeight> FontWeightProperty =
TextBlock.FontWeightProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets the font weight or thickness. /// Gets or sets the font weight or thickness.
/// </summary> /// </summary>
public FontWeight FontWeight public FontWeight FontWeight
{ {
get get => GetValue(FontWeightProperty);
{ set => SetValue(FontWeightProperty, value);
return _fontWeight ?? FontWeight.Normal;
}
set
{
if (_fontWeight != value)
{
_fontWeight = value;
NotifyPropertyChanged(nameof(FontWeight));
}
}
} }
/// <summary>
/// Identifies the Foreground dependency property.
/// </summary>
public static readonly AttachedProperty<IBrush> ForegroundProperty =
TextBlock.ForegroundProperty.AddOwner<DataGridTextColumn>();
/// <summary> /// <summary>
/// Gets or sets a brush that describes the foreground of the column cells. /// Gets or sets a brush that describes the foreground of the column cells.
/// </summary> /// </summary>
public IBrush Foreground public IBrush Foreground
{ {
get get => GetValue(ForegroundProperty);
{ set => SetValue(ForegroundProperty, value);
return _foreground; }
}
set protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == FontFamilyProperty
|| change.Property == FontSizeProperty
|| change.Property == FontStyleProperty
|| change.Property == FontWeightProperty
|| change.Property == ForegroundProperty)
{ {
if (_foreground != value) NotifyPropertyChanged(change.Property.Name);
{
_foreground = value;
NotifyPropertyChanged(nameof(Foreground));
}
} }
} }
@ -154,26 +147,7 @@ namespace Avalonia.Controls
Background = new SolidColorBrush(Colors.Transparent) Background = new SolidColorBrush(Colors.Transparent)
}; };
if (IsSet(FontFamilyProperty)) SyncProperties(textBox);
{
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;
}
return textBox; return textBox;
} }
@ -192,26 +166,8 @@ namespace Avalonia.Controls
VerticalAlignment = VerticalAlignment.Center VerticalAlignment = VerticalAlignment.Center
}; };
if (IsSet(FontFamilyProperty)) SyncProperties(textBlockElement);
{
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;
}
if (Binding != null) if (Binding != null)
{ {
textBlockElement.Bind(TextBlock.TextProperty, Binding); textBlockElement.Bind(TextBlock.TextProperty, Binding);
@ -261,99 +217,42 @@ namespace Avalonia.Controls
throw new ArgumentNullException("element"); throw new ArgumentNullException("element");
} }
if(element is TextBox textBox) if (element is AvaloniaObject content)
{ {
if (propertyName == nameof(FontFamily)) if (propertyName == nameof(FontFamily))
{ {
textBox.FontFamily = FontFamily; DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
} }
else if (propertyName == nameof(FontSize)) else if (propertyName == nameof(FontSize))
{ {
SetTextFontSize(textBox, TextBox.FontSizeProperty); DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
} }
else if (propertyName == nameof(FontStyle)) else if (propertyName == nameof(FontStyle))
{ {
textBox.FontStyle = FontStyle; DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
} }
else if (propertyName == nameof(FontWeight)) else if (propertyName == nameof(FontWeight))
{ {
textBox.FontWeight = FontWeight; DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
} }
else if (propertyName == nameof(Foreground)) else if (propertyName == nameof(Foreground))
{ {
textBox.Foreground = Foreground; DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
}
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;
}
} }
} }
else 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; DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
if (double.IsNaN(newFontSize)) DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
{ DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
textElement.ClearValue(fontSizeProperty); DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
} DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
else
{
textElement.SetValue(fontSizeProperty, newFontSize);
}
} }
} }
} }

22
src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs

@ -0,0 +1,22 @@
namespace Avalonia.Controls
{
internal static class DataGridHelper
{
internal static void SyncColumnProperty<T>(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty<T> property)
{
SyncColumnProperty(column, content, property, property);
}
internal static void SyncColumnProperty<T>(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty<T> contentProperty, AvaloniaProperty<T> columnProperty)
{
if (!column.IsSet(columnProperty))
{
content.ClearValue(contentProperty);
}
else
{
content.SetValue(contentProperty, column.GetValue(columnProperty));
}
}
}
}

21
src/Avalonia.Controls/Primitives/Popup.cs

@ -128,6 +128,7 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<bool> TopmostProperty = public static readonly StyledProperty<bool> TopmostProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(Topmost)); AvaloniaProperty.Register<Popup, bool>(nameof(Topmost));
private bool _isOpenRequested = false;
private bool _isOpen; private bool _isOpen;
private bool _ignoreIsOpenChanged; private bool _ignoreIsOpenChanged;
private PopupOpenState? _openState; private PopupOpenState? _openState;
@ -361,17 +362,19 @@ namespace Avalonia.Controls.Primitives
if (placementTarget == null) if (placementTarget == null)
{ {
throw new InvalidOperationException("Popup has no logical parent and PlacementTarget is null"); _isOpenRequested = true;
return;
} }
var topLevel = placementTarget.VisualRoot as TopLevel; var topLevel = placementTarget.VisualRoot as TopLevel;
if (topLevel == null) if (topLevel == null)
{ {
throw new InvalidOperationException( _isOpenRequested = true;
"Attempted to open a popup not attached to a TopLevel"); return;
} }
_isOpenRequested = false;
var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver); var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
var handlerCleanup = new CompositeDisposable(5); var handlerCleanup = new CompositeDisposable(5);
@ -492,6 +495,17 @@ namespace Avalonia.Controls.Primitives
return new Size(); return new Size();
} }
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (_isOpenRequested)
{
Open();
}
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
@ -552,6 +566,7 @@ namespace Avalonia.Controls.Primitives
private void CloseCore() private void CloseCore()
{ {
_isOpenRequested = false;
if (_openState is null) if (_openState is null)
{ {
using (BeginIgnoringIsOpen()) using (BeginIgnoringIsOpen())

45
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -21,7 +21,50 @@ namespace Avalonia.Controls.UnitTests.Primitives
public class PopupTests public class PopupTests
{ {
protected bool UsePopupHost; 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] [Fact]
public void Setting_Child_Should_Set_Child_Controls_LogicalParent() public void Setting_Child_Should_Set_Child_Controls_LogicalParent()
{ {

Loading…
Cancel
Save