繁体   English   中英

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

[英]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.ResourceResourceDictionary ,在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM