所以我有一个ResourceDictionary来定义我的自定义Window样式。我正在努力做的是从XAML文件访问控件。 ResourceDictionary看起来像这样
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="MyCustomWindowStyle" TargetType="{x:Type Window}">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="30"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<!-- the root window -->
<Border BorderThickness="0.3" BorderBrush="{DynamicResource GeneralDarkBlue}">
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
</Border>
<DockPanel Height="30" Background="{TemplateBinding Background}" VerticalAlignment="Top" LastChildFill="False">
<Viewbox x:Name="HamburgerMenu" DockPanel.Dock="Left" WindowChrome.IsHitTestVisibleInChrome="True">
<Viewbox.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{Binding SettingsClick}"/>
</Viewbox.InputBindings>
<Border Width="47" Height="32" Background="Transparent">
<Canvas>
<Path x:Name="TopbarIconHamburgerMenu" Margin="14,10" Data="M12.5,19h19.2v1H12.5V19z M12.5,13.7h19.2v1H12.5V13.7z M12.5,8.5h19.2v1H12.5V8.5z" Stretch="UniformToFill" Fill="#FFFFFF"/>
</Canvas>
</Border>
</Viewbox>
// the rest of viewboxes for minimize, maximize controls...
</DockPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
让我们说我想访问HamburgerMenu,所以我做这样的事情
public partial class MyCustomWindowStyle : ResourceDictionary
{
public MyCustomWindowStyle()
{
InitializeComponent();
}
public void DoSomething()
{
var window = (Style)Application.Current.Resources["MyCustomWindowStyle"];
var hm = (Viewbox)window.Resources.FindName("HamburgerMenu");
}
}
并且在hm中返回null!
知道怎么做吗?
答案 0 :(得分:2)
首先,Style.Resource
是ResourceDictionary
,在ResourceDictionary.FindName
方法documentation中需要注意两件重要事项:< / p>
摘要部分说:
此Dictionary实现不支持。
和返回值部分说:
始终返回 null 。
其次,即使您尝试按键检索ViewBox
,也必须将其定义为资源:
<Style x:Key="MyCustomWindowStyle" TargetType="{x:Type Window}">
<Style.Resources>
<ViewBox x:Key="HamburgerMenu" />
</Style.Resources>
</Style>
事实并非如此。它是ControlTemplate
视觉树的一部分。
第三个,ControlTemplate
不包含实际元素,而是包含创建它们的配方。因此ViewBox
内部没有实际的ControlTemplate
来检索。请注意,ControlTemplate.FindName
需要一个额外的参数来指定模板已实现的元素。
但是,ControlTemplate
确实有一个LoadContent
方法,它基本上加载了该模板定义的可视树,我认为你可以使用它,然后在根上调用FindName
元件。为了简化ControlTemplate
的检索,首先让它成为资源:
<Style x:Key="MyCustomWindowStyle" TargetType="{x:Type Window}">
<Style.Resources>
<ControlTemplate x:Key="Template" TargetType="{x:Type Window}">
<Grid>
(...)
<ViewBox x:Key="HamburgerMenu" />
(...)
</Grid>
</ControlTemplate>
</Style.Resources>
<Setter Property="Template" Value="{StaticResource Template}" />
</Style>
然后这应该为你做的伎俩:
var window = (Style)Application.Current.Resources["MyCustomWindowStyle"];
var template = (ControlTemplate)window.Resources["Template"];
var root = (FrameworkElement)template.LoadContent();
var hm = (ViewBox)root.FindName("HamburgerMenu");
如果您的目标是在应用该模板的现有窗口中获取ViewBox
,首先您需要知道如何获取该特定窗口。它可能是Application.Current.MainWindow
,否则您极有可能在Application.Current.Windows
集合中找到它。您还可以为该窗口实现单例模式,或者使用其他方法,例如在应用程序的某个位置引用静态属性,或者使用第三方工具,例如 Prism中的Service Locator < / em>的
手中有窗口后,您只需使用前面提到的ControlTemplate.FindName
方法:
var window = (...);
var hm = (ViewBox)window.Template.FindName(name: "HamburgerMenu", templatedParent: window);
请注意,访问定义模板的资源字典不是必需的。
至于为什么尝试使用先前的解决方案失败 - 这是因为ControlTemplate.LoadContent
方法每次调用时都会生成新创建的元素,并且修改它不会反映先前由该模板创建的元素。