【问题标题】:How can I find a style trigger-embedded element by name in WPF?如何在 WPF 中按名称查找样式触发器嵌入的元素?
【发布时间】:2014-02-24 01:15:18
【问题描述】:

首先,问题的核心:如果一个元素通过样式触发器指定为 ContentControl 的内容,我似乎无法通过名称找到它。

现在,了解更多细节:我有一个面板,其布局和功能根据其数据上下文变化很大,这是来自错误库的错误。当该错误为空时,它是一个搜索表单,当它为非空时,它是该错误属性的简单查看器。 XAML 然后看起来像:

<ContentControl DataContext="...">
    <ContentControl.Style>
        <Style TargetType="ContentControl">
            <Setter Property="Content">
                <Setter.Value>
                    ...
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <DataTrigger Binding="{Binding}" Value="{x:Null}">
                    <Setter Property="Content">
                        <StackPanel>
                            <TextBox Name="Waldo"/>
                            <Button .../>
                        </StackPanel>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

当用户单击文本框旁边的按钮时,我会在后面的代码中收到回调。从那时起,我希望能够访问文本框的各种属性。问题是,Waldo 在哪里? :)

在后面的代码中,我尝试了以下几种变体,但都收效甚微:

this.FindName("Waldo"); // Always returns null

我看到很多关于这个主题的讨论,因为它与templates 相关,但与直接使用触发器设置内容无关。也许是因为我这样做违反了各种最佳实践:)

谢谢!

【问题讨论】:

    标签: c# wpf xaml triggers contentcontrol


    【解决方案1】:

    如果通过样式触发器将元素指定为ContentControlContent,我似乎无法按名称找到它。

    如果您需要在触发发生之前访问Content,这很可能是不可能的。在这种情况下,主要的事情发生在DataTrigger之后。

    我这样做违反了各种最佳实践

    也许这不是在 WPF 中使用Сontrol 的正确方法,您仍然需要访问 动态 内容的权限越多,以后可以更改这些内容。但无论如何,有两种方法可以使用 Сontrol - 就像现在一样,也可以采用 MVVM 风格。 MVVM 风格最适合具有不同业务逻辑的大型和不太复杂的应用程序。如果在您的情况下,为了方便应用,在这种情况下,我认为没有任何问题。除了做一个MVVM风格的项目需要从头开始之外,结合常规的方法和正确的方法并不是一个好办法。

    我创建了一个小示例来演示特定情况下的访问控制。有一个属性对应Content的类型,默认是Init。如果您为此属性分配 null,则会加载动态内容。

    这就是我访问TextBox的方式:

    private void GetAccessToTextBox_Click(object sender, RoutedEventArgs e)
    {
        TextBox MyTextBox = null;
        StackPanel panel = MainContentControl.Content as StackPanel;
    
        foreach (object child in panel.Children)
        {
            if (child is TextBox)
            {
                MyTextBox = child as TextBox;
            }
        }
    
        if (MyTextBox != null) 
        {
            MyTextBox.Background = Brushes.Gainsboro;
            MyTextBox.Height = 100;
            MyTextBox.Text = "Got access to me!";
        }
    }
    

    下面是一个完整的例子:

    XAML

    <Window x:Class="AccessToElementInContentControl.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:this="clr-namespace:AccessToElementInContentControl"
            WindowStartupLocation="CenterScreen"
            Title="MainWindow" Height="350" Width="525">
    
        <Window.DataContext>
            <this:TestData />
        </Window.DataContext>
    
        <Window.Resources>
            <Style TargetType="{x:Type ContentControl}">
                <Setter Property="Content">
                    <Setter.Value>
                        <Label Content="InitContent"
                               HorizontalAlignment="Center"
                               VerticalAlignment="Center" />
                    </Setter.Value>
                </Setter>
    
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=TypeContent}" Value="{x:Null}">
                        <Setter Property="Content">
                            <Setter.Value>
                                <StackPanel Name="NullStackPanel">
                                    <TextBox Name="Waldo" Text="DynamicText" />
                                    <Button Width="100" Height="30" Content="DynamicButton" />
                                </StackPanel>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
    
        <Grid>
            <ContentControl Name="MainContentControl" />
    
            <Button Name="SetContentType"
                    Width="100"
                    Height="30" 
                    HorizontalAlignment="Left"
                    Content="SetContentType"
                    Click="SetContentType_Click" />
    
            <Button Name="GetAccessToButton"
                    Width="110"
                    Height="30" 
                    HorizontalAlignment="Right"
                    Content="GetAccessToTextBox"
                    Click="GetAccessToTextBox_Click" />
        </Grid>
    </Window>
    

    Code-behind

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void SetContentType_Click(object sender, RoutedEventArgs e)
        {
            TestData test = this.DataContext as TestData;
    
            test.TypeContent = null;
        }
    
        private void GetAccessToTextBox_Click(object sender, RoutedEventArgs e)
        {
            TextBox MyTextBox = null;
            StackPanel panel = MainContentControl.Content as StackPanel;
    
            foreach (object child in panel.Children)
            {
               if (child is TextBox)
               {
                    MyTextBox = child as TextBox;
               }
            }
    
            if (MyTextBox != null) 
            {
                MyTextBox.Background = Brushes.Gainsboro;
                MyTextBox.Height = 100;
                MyTextBox.Text = "Got access to me!";
            }
        }
    }
    
    public class TestData : NotificationObject
    {
        private string _typeContent = "Init";
    
        public string TypeContent
        {
            get
            {
                return _typeContent;
            }
    
            set
            {
                _typeContent = value;
                NotifyPropertyChanged("TypeContent");   
            }
        }
    }
    
    public class NotificationObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    【讨论】:

    • 感谢周到详细的解释!这种方法具有将 UI 结构的知识强制到代码隐藏中的属性(即我们正在搜索的文本框是命名 StackPanel 的子项),但它似乎是总体上最不痛苦的方法.就像你说的那样,对于一个小型应用程序来说,最好不要太纠结于 V/VM 拆分:)
    • Anatoliy 有点急于开枪,它只是闪过我的脑海,我离开了电脑一会儿。尽管有反对的反应,我还是继续前进,并在应有的地方给予赞扬:) 这是一个很好的答案,再次感谢。
    猜你喜欢
    • 2012-09-17
    • 1970-01-01
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-22
    相关资源
    最近更新 更多