Browse Source

Merge pull request #843 from AvaloniaUI/fixes/829-findcontrol-returns-null-for-usercontrol

Register namescoped controls with parent namescope.
pull/642/head
Steven Kirk 10 years ago
committed by GitHub
parent
commit
0e1877cc21
  1. 17
      src/Avalonia.Controls/Control.cs
  2. 31
      src/Avalonia.Styling/Controls/NameScope.cs
  3. 18
      tests/Avalonia.Controls.UnitTests/ControlTests_NameScope.cs

17
src/Avalonia.Controls/Control.cs

@ -671,6 +671,23 @@ namespace Avalonia.Controls
if (Name != null)
{
_nameScope?.Register(Name, this);
var visualParent = Parent as Visual;
if (this is INameScope && visualParent != null)
{
// If we have e.g. a named UserControl in a window then we want that control
// to be findable by name from the Window, so register with both name scopes.
// This differs from WPF's behavior in that XAML manually registers controls
// with name scopes based on the XAML file in which the name attribute appears,
// but we're trying to avoid XAML magic in Avalonia in order to made code-
// created UIs easy. This will cause problems if a UserControl declares a name
// in its XAML and that control is included multiple times in a parent control
// (as the name will be duplicated), however at the moment I'm fine with saying
// "don't do that".
var parentNameScope = NameScope.FindNameScope(visualParent);
parentNameScope?.Register(Name, this);
}
}
}

31
src/Avalonia.Styling/Controls/NameScope.cs

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Avalonia.LogicalTree;
namespace Avalonia.Controls
{
@ -29,6 +30,32 @@ namespace Avalonia.Controls
/// </summary>
public event EventHandler<NameScopeEventArgs> Unregistered;
/// <summary>
/// Finds the containing name scope for a visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>The containing name scope.</returns>
public static INameScope FindNameScope(Visual visual)
{
Contract.Requires<ArgumentNullException>(visual != null);
INameScope result;
while (visual != null)
{
result = visual as INameScope ?? GetNameScope(visual);
if (result != null)
{
return result;
}
visual = (visual as ILogical).LogicalParent as Visual;
}
return null;
}
/// <summary>
/// Gets the value of the attached <see cref="NameScopeProperty"/> on a visual.
/// </summary>
@ -36,6 +63,8 @@ namespace Avalonia.Controls
/// <returns>The value of the NameScope attached property.</returns>
public static INameScope GetNameScope(Visual visual)
{
Contract.Requires<ArgumentNullException>(visual != null);
return visual.GetValue(NameScopeProperty);
}
@ -46,6 +75,8 @@ namespace Avalonia.Controls
/// <param name="value">The value to set.</param>
public static void SetNameScope(Visual visual, INameScope value)
{
Contract.Requires<ArgumentNullException>(visual != null);
visual.SetValue(NameScopeProperty, value);
}

18
tests/Avalonia.Controls.UnitTests/ControlTests_NameScope.cs

@ -70,5 +70,23 @@ namespace Avalonia.Controls.UnitTests
Assert.Null(NameScope.GetNameScope((Control)root.Presenter).Find("foo"));
}
[Fact]
public void Control_That_Is_NameScope_Should_Register_With_Parent_NameScope()
{
UserControl userControl;
var root = new TestTemplatedRoot
{
Content = userControl = new UserControl
{
Name = "foo",
}
};
root.ApplyTemplate();
Assert.Same(userControl, root.FindControl<UserControl>("foo"));
Assert.Same(userControl, userControl.FindControl<UserControl>("foo"));
}
}
}

Loading…
Cancel
Save