我在Windows 8.1 Store App中有以下代码。此代码在Windows 10上运行完全正常,但在Windows 8.1上崩溃。 MainPage.xaml.cs中的第二个命名控件在Win 8.1上为null,但在Windows 10上不为。它不是计时问题,因为命名控件仍然不会填充在页面后面的任何后续事件处理程序中加载。到底是怎么回事?
总而言之,我有一个ContentControl,其ContentPresenter在其模板中定义。然后在页面上实例化ContentControl,并使用命名的子控件(使用" x:Name")作为其内容。在Windows 10上,该命名控件存在于代码隐藏中。在Windows 8.1上,它为null
MyUserControl1.xaml
<ContentControl
x:Class="App1.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<ContentControl.Template>
<ControlTemplate>
<ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
</ContentControl.Template>
MainPage.xaml中
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="TextBlock1"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextAlignment="Center"
FontSize="50"
TextWrapping="Wrap" />
<local:MyUserControl1 Grid.Column="1">
<TextBlock x:Name="TextBlock2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextAlignment="Center"
FontSize="50"
TextWrapping="Wrap" />
</local:MyUserControl1>
</Grid>
MainPage.xaml.cs中
using Windows.UI.Xaml.Controls;
namespace App1
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
TextBlock1.Text = "This works";
TextBlock2.Text = "This does not work because TextBlock2 is null";
}
}
}
答案 0 :(得分:0)
当然你不能参考这个。您的MainPage
明确被设置为另一个控件的内容,从根本上超出范围。渲染完成后,您的ControlTemplate
不再是UserControl
的孩子,而是您UserControl
中GetControl<TextBlock>("TextBlock1")
的孩子。 Windows 10的行为方式确实如此,如果它运行起来,您似乎发现了Windows 8呈现引擎中的一个错误。
有一些解决方法。第一种是向您的public bool TryGetControl<T>(string name, out T control)
{
try
{
var children = RecurseChildren(this.MyUserControl);
control = children
.Where(x => Equals(x.Name, name))
.OfType<T>()
.First();
return true;
}
catch
{
control = default(T);
return false;
}
}
public List<Control> RecurseChildren(DependencyObject parent)
{
var list = new List<Control>();
var count = VisualTreeHelper.GetChildrenCount(parent);
var children = Enumerable.Range(0, count - 1)
.Select(x => VisualTreeHelper.GetChild(parent, x));
list.AddRange(children.OfType<Control>());
foreach (var child in children)
{
list.AddRange(RecurseChildren(child));
}
return list;
}
添加属性的教科书方法,该属性添加了对此控件的访问权限。因为您允许内容是动态的,所以该属性(或方法)内的操作也需要是动态的。像UserControl
这样的东西可以找你。
UserControl
您可以做的第二件事就是通过页面本身ScrollViewer
的子层次结构来寻找控件。逻辑与第一个(上面)相同,但它将在页面内执行,而不是ListView
逻辑的一部分。当您因某种原因需要在Border
中找到Button
或Loaded
中的TextBlock1
内MainPage
时,您已经做过这类事情了。
事实证明,我已经在博客文章中对此进行了解释,您可以将其作为参考:get date as of 4 hours ago
这是第三种,也许是最简单的方法。处理sender
中TextBlock
的{{1}}事件,并在处理程序方法中将字段设置为Content
的值。您甚至可以将其强制转换为ContentPresenter
,以便输入所有内容。这为您提供了简单的访问权限,但具有潜在的时序缺点。如果在设置之前尝试访问字段值,则可能会发现它为null。但是,在大多数情况下,这是最容易的。
所以,这是处理它的三种方法。我认为您认识到这是EXPECTED行为非常重要,因为XAML元素只能有一个父级,并且您通过{{1}}的{{1}}属性设置该父级。话虽如此,它可能是预期的行为,但它显然不直观。
祝你好运。