[英]How to access UI control from code-behind of ResourceDictionary
因此,我有一个ResourceDictionary来定义我的自定义窗口样式。 我正在努力做的是从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!
任何想法如何做到这一点?
首先 , Style.Resource
是ResourceDictionary
,在ResourceDictionary.FindName
方法文档中有两点需要注意:
摘要部分说:
此Dictionary实现不支持。
和返回值部分说:
始终返回null 。
第二 ,即使您尝试通过键检索ViewBox
,也必须将其定义为资源:
<Style x:Key="MyCustomWindowStyle" TargetType="{x:Type Window}">
<Style.Resources>
<ViewBox x:Key="HamburgerMenu" />
</Style.Resources>
</Style>
事实并非如此。 它是ControlTemplate
可视树的一部分。
第三 , ControlTemplate
不包含实际元素,而是创建它们的方法。 因此, ControlTemplate
内部没有实际的ViewBox
可供检索。 请注意, 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 。
拥有窗口后,只需使用前面提到的ControlTemplate.FindName
方法:
var window = (...);
var hm = (ViewBox)window.Template.FindName(name: "HamburgerMenu", templatedParent: window);
请注意,无需访问定义了模板的资源字典。
至于为什么以前的解决方案尝试失败的原因-这是因为ControlTemplate.LoadContent
方法每次调用都会产生新创建的元素,而对其进行修改不会反映该模板先前创建的元素。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.