如何从ResourceDictionary的代码隐藏访问UI控件

时间:2018-04-12 09:20:11

标签: c# .net wpf xaml

所以我有一个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!

知道怎么做吗?

1 个答案:

答案 0 :(得分:2)

首先Style.ResourceResourceDictionary,在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方法每次调用时都会生成新创建的元素,并且修改它不会反映先前由该模板创建的元素。