Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ColumnDefinitions as a StaticResource throws NullReferenceException when assigning to multiple Grids. #15518

Closed
huestack opened this issue Apr 26, 2024 · 6 comments · Fixed by #16644

Comments

@huestack
Copy link

Describe the bug

Assigning a ColumnDefinitions resource in two or more Grids throws runtime error.

Exception:

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at Avalonia.Controls.Grid.ValidateDefinitionsLayout(IReadOnlyList`1 definitions, Boolean treatStarAsAuto)
   at Avalonia.Controls.Grid.MeasureOverride(Size constraint)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Controls.StackPanel.MeasureOverride(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable control, Size availableSize, Thickness padding, Thickness borderThickness)
   at Avalonia.Controls.Presenters.ContentPresenter.MeasureOverride(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Layout.LayoutHelper.MeasureChild(Layoutable control, Size availableSize, Thickness padding)
   at Avalonia.Controls.Decorator.MeasureOverride(Size availableSize)
   at Avalonia.Controls.Primitives.VisualLayerManager.MeasureOverride(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureOverride(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureOverride(Size availableSize)
   at Avalonia.Controls.Window.MeasureOverride(Size availableSize)
   at Avalonia.Controls.WindowBase.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Layout.LayoutManager.Measure(Layoutable control)
   at Avalonia.Layout.LayoutManager.ExecuteInitialLayoutPass()
   at Avalonia.Controls.Window.ShowCore(Window owner)
   at Avalonia.Controls.Window.Show()
   at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.ShowMainWindow()
   at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.Start(String[] args)
   at Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(AppBuilder builder, String[] args, Action`1 lifetimeBuilder)
   at AvaloniaListBox.Program.Main(String[] args) in D:\Projects\Git\AvaloniaListBox\Program.cs:line 13

To Reproduce

This works:

<Window.Resources>
    <ColumnDefinitions x:Key="TestDef">
        <ColumnDefinition Width="150" />
        <ColumnDefinition Width="10" />
        <ColumnDefinition Width="Auto" />
    </ColumnDefinitions>
</Window.Resources>

<StackPanel>
    <Grid ColumnDefinitions="{StaticResource TestDef}">
        <TextBlock Grid.Column="0">List</TextBlock>
        <TextBox Grid.Column="2" />
    </Grid>
    <Grid ColumnDefinitions="150, 10, Auto">
        <TextBlock Grid.Column="0">List</TextBlock>
        <TextBox Grid.Column="2" />
    </Grid>
</StackPanel>

This doesn't:

<Window.Resources>
    <ColumnDefinitions x:Key="TestDef">
        <ColumnDefinition Width="150" />
        <ColumnDefinition Width="10" />
        <ColumnDefinition Width="Auto" />
    </ColumnDefinitions>
</Window.Resources>

<StackPanel>
    <Grid ColumnDefinitions="{StaticResource TestDef}">
        <TextBlock Grid.Column="0">List</TextBlock>
        <TextBox Grid.Column="2" />
    </Grid>
    <Grid ColumnDefinitions="{StaticResource TestDef}">
        <TextBlock Grid.Column="0">List</TextBlock>
        <TextBox Grid.Column="2" />
    </Grid>
</StackPanel>

Expected behavior

A resource is expected to be applied to multiple controls.

Avalonia version

11.0.10, 11.1.0-beta-2

OS

Windows

Additional context

No response

@huestack huestack added the bug label Apr 26, 2024
@maxkatz6
Copy link
Member

Never seen this application of ColumnDefinitions, but looks handy.

@rabbitism
Copy link
Contributor

rabbitism commented Apr 26, 2024

DefinitionBase holds many calculation and status of groups, especially SharedSizeGroup, i doubt if this is even possible.

@workgroupengineering
Copy link
Contributor

the problem is there

set
{
if (_extData == null) { _extData = new ExtendedData(); }
_extData.ColumnDefinitions = value;
_extData.ColumnDefinitions.Parent = this;
InvalidateMeasure();
}

exactly on line 181.

StaticResource return same instance for each grid.

two possible solutions could be:

  • Deep cloning like this code:

              set
              {
                  if (_extData == null) { _extData = new ExtendedData(); }
                  var def = value is null
                      ? new ColumnDefinitions()
                      : new ColumnDefinitions(value.Count);
                  if (value is not null)
                  {
                      foreach (var item in value)
                      {
                          def.Add(new ColumnDefinition()
                          {
                              MaxWidth = item.MaxWidth,
                              MinWidth = item.MinWidth,
                              SharedSizeGroup = item.SharedSizeGroup,
                              Width = item.Width,
                          });
                      }
                  }
                  _extData.ColumnDefinitions = def;
                  _extData.ColumnDefinitions.Parent = this;
                  InvalidateMeasure();
              }
  • x:Shared directive like WPF

Personally I prefer x:Shared

@workgroupengineering
Copy link
Contributor

Can i work on x:Shared?

@kekekeks
Copy link
Member

Given that we already have support for resource factories for lazy resource loading, x:Shared can be implemented by calling AddNotShared (or smth) instead of AddDeferred.

@timunie
Copy link
Contributor

timunie commented May 15, 2024

Can i work on x:Shared?

I talked to the team and we would be happy if you help us to get this implemented.

Nikita [15.05.2024 09:29]
it shouldn't be that hard to implement since we already have resource factories for lazy resource loading

Nikita [15.05.2024 09:30]
so it might be implemented as calling AddShared instead of AddDeferred"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants