简体   繁体   English

通过C#访问XAML控件

[英]Access XAML Control through C#

I have got some TextBlocks with x:Name , i'm able to access this control easily with C#. 我有一些带有x:Name TextBlocks ,我可以使用C#轻松访问此控件。 However I have also got some DoubleAnimations with an x:Name but unable to access these, why is this? 但是我也有一些带有x:Name DoubleAnimations ,但无法访问它们,为什么?

I have tried this but it does not work, I get an error saying Object reference not set to an instance of an object : 我已经尝试过了,但是它不起作用,我收到一条错误消息,说“ Object reference not set to an instance of an object

//Add completed methods
            DoubleAnimation da1 = (DoubleAnimation)Resources["storyboardLoad"];
            da1.Completed += DoubleAnimationCompleted;

Notifications.xaml Notifications.xaml

<Window x:Class="PhotoManagement.Views.Notification"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PhotoManagement.Views"
        mc:Ignorable="d"
        Title="NotificationWindow" WindowStyle="None" AllowsTransparency="True" Background="Transparent" Width="350" SizeToContent="Height">
    <Window.Resources>
        <FontFamily x:Key="FontAwesome">/Fonts/#FontAwesome</FontFamily>
        <SolidColorBrush x:Key="colour1" Color="#29a1d5"/>
        <SolidColorBrush x:Key="colour2" Color="#248eb8"/>

        <Style TargetType="StackPanel">
            <Setter Property="Margin" Value="20" />
        </Style>
        <Style TargetType="TextBlock">
            <Setter Property="TextWrapping" Value="Wrap" />
            <Setter Property="Margin" Value="5" />
        </Style>
        <Style x:Key="NotificationIcon" TargetType="Border">
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Background" Value="{DynamicResource colour2}" />
            <Style.Resources>
                <Style TargetType="TextBlock">
                    <Setter Property="FontFamily" Value="{StaticResource FontAwesome}" />
                    <Setter Property="Foreground" Value="White" />
                    <Setter Property="FontSize" Value="28" />
                    <Setter Property="Padding" Value="15" />
                </Style>
            </Style.Resources>
        </Style>
        <Style x:Key="NotificationText" TargetType="Border">
            <Setter Property="VerticalAlignment" Value="Stretch" />
            <Setter Property="HorizontalAlignment" Value="Stretch" />
            <Setter Property="Background" Value="{DynamicResource colour1}" />
            <Style.Resources>
                <Style TargetType="TextBlock">
                    <Setter Property="Foreground" Value="White" />
                    <Setter Property="VerticalAlignment" Value="Center" />
                    <Setter Property="HorizontalAlignment" Value="Center" />
                    <Setter Property="TextWrapping" Value="Wrap" />
                </Style>
            </Style.Resources>
        </Style>
        <Style x:Key="NotificationContainer" TargetType="Grid">
            <!-- Animation -->
            <Style.Triggers>
                <EventTrigger RoutedEvent="Window.Loaded">
                    <BeginStoryboard x:Name="StoryboardLoad">
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(UIElement.Opacity)" 
                                             From="0.0"
                                             To="1.0"
                                             Duration="0:0:0.5"/>
                            <DoubleAnimation x:Name="storyboardLoad" Storyboard.TargetProperty="(UIElement.Opacity)" 
                                             From="1.0" 
                                             To="0.0" 
                                             Duration="0:0:1" 
                                             BeginTime="0:0:2" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>

                <EventTrigger RoutedEvent="Mouse.MouseEnter">
                    <EventTrigger.Actions>
                        <RemoveStoryboard BeginStoryboardName="StoryboardLoad"/>
                        <RemoveStoryboard BeginStoryboardName="StoryboardFade"/>
                    </EventTrigger.Actions>
                </EventTrigger>

                <EventTrigger RoutedEvent="Mouse.MouseLeave">
                    <BeginStoryboard x:Name="StoryboardFade">
                        <Storyboard>
                            <DoubleAnimation x:Name="storyboardFade" 
                                             Storyboard.TargetProperty="(UIElement.Opacity)" 
                                             From="1.0" 
                                             To="0.0" 
                                             Duration="0:0:1" 
                                             BeginTime="0:0:2" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid Style="{StaticResource NotificationContainer}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Border Grid.Column="1"
                    CornerRadius="0 5 5 0"
                    Style="{StaticResource NotificationText}">
                <TextBlock Text="A request has been added!" 
                           x:Name="NotificationText"/>
            </Border>
            <Border Grid.Column="0" 
                    CornerRadius="5 0 0 5"
                    Style="{StaticResource NotificationIcon}">
                <TextBlock Text=""
                           x:Name="NotificationIcon"/>
            </Border>
        </Grid>
        <Grid.RenderTransform>
            <ScaleTransform ScaleY="1" />
        </Grid.RenderTransform>
    </Grid>
</Window>

Notifications.xaml.cs Notifications.xaml.cs

using System;
using System.Windows;

namespace PhotoManagement.Views
{
    /// <summary>
    /// Interaction logic for Notification.xaml
    /// </summary>
    public partial class Notification : Window
    {
        public Notification() : base()
        {
            InitializeComponent();

            //Move other windows down when closed
            this.Closed += this.NotificationWindowClosed;

            //Get object properties
            this.NotificationText.Text = "Hello";
            this.NotificationIcon.Text = "G";
        }

        /// <summary>
        /// Show the Notification and setup it's content and actions
        /// </summary>
        public new void Show()
        {
            this.Topmost = true;
            this.Owner = System.Windows.Application.Current.MainWindow;
            this.Closed += this.NotificationWindowClosed;
            base.Show();

            //Add completed methods


            //Position the Notification
            var workingArea = SystemParameters.WorkArea;
            this.Left = (workingArea.Width - this.ActualWidth) / 2;
            double top = workingArea.Bottom - this.ActualHeight - 5;

            //Ensure new notifications are placed above older ones
            foreach (Window window in System.Windows.Application.Current.Windows)
            {
                string windowName = window.GetType().Name;

                if (windowName.Equals("Notification") && window != this)
                {
                    window.Topmost = true;
                    top = window.Top - window.ActualHeight - 5;
                }
            }

            this.Top = top;
        }
        private void ImageMouseUp(object sender,
        System.Windows.Input.MouseButtonEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Close window once animation is complete
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void DoubleAnimationCompleted(object sender, EventArgs e)
        {
            if (!this.IsMouseOver)
            {
                this.Close();
            }
        }

        /// <summary>
        /// Move the next notification down once notification has closed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NotificationWindowClosed(object sender, EventArgs e)
        {
            foreach (Window window in System.Windows.Application.Current.Windows)
            {
                string windowName = window.GetType().Name;

                if (windowName.Equals("NotificationWindow") && window != this)
                {
                    // Adjust any windows that were above this one to drop down
                    if (window.Top < this.Top)
                    {
                        window.Top = window.Top + this.ActualHeight;
                    }
                }
            }
        }
    }
}

When you create a custom Window or UserControl , a (partial) class is generated for it that contains fields for any named child elements. 创建自定义WindowUserControl ,将为其生成一个(部分)类,其中包含任何已命名子元素的字段。 That won't work for styles and templates however - likely because those can be applied to multiple controls, so a name doesn't necessarily map to a single item (or to an item at all). 但是,这不适用于样式和模板-可能是因为它们可以应用于多个控件,所以名称不一定映射到单个项目(或完全映射到一个项目)。

The hard way 艰难的道路

So the animation you're looking for is part of a style, which is a resource in your window. 因此,您要查找的动画是样式的一部分,而样式是窗口中的资源。 To be precise: your style has a collection of triggers. 准确地说:您的样式包含一组触发器。 Its first trigger has a 'begin storyboard' action, and that action holds the storyboard you're interested in. It's going to take some searching and type-casting to get there: 它的第一个触发器有一个“开始情节提要”动作,该动作保存了您感兴趣的情节提要。它将需要一些搜索和类型转换才能到达:

Style style = (Style)Resources["NotificationContainer"];
EventTrigger trigger = (EventTrigger)style.Triggers[0];
BeginStoryboard beginStoryboard = (BeginStoryboard)trigger.Actions[0];
Storyboard storyboard = beginStoryboard.Storyboard;

As you can see, that's quite brittle. 如您所见,这非常脆弱。 All you need to do to break it is reordering the event triggers in XAML... 打破它所需要做的就是重新排列XAML中的事件触发器...

The easy way 简单的方法

But why look up that storyboard anyway? 但是为什么仍要查找该情节提要? Just bind its Completed event to an event handler in XAML directly: 只需将其Completed事件直接绑定到XAML中的事件处理程序即可:

<Storyboard Completed="WindowLoadedStoryboard_Completed">
    ...
</Storyboard>

Sadly, that's not going to work directly due to a "The event 'Completed' cannot be specified on a Target tag in a Style. Use an EventSetter instead." 遗憾的是,由于"The event 'Completed' cannot be specified on a Target tag in a Style. Use an EventSetter instead." error. 错误。 And an EventSetter is not going to work because Storyboard.Completed is not a routed event... 而且EventSetter无法正常工作,因为Storyboard.Completed不是路由事件...

Fortunately, you can work around that by making the storyboard itself a resource, and referencing it in the begin-storyboard action: 幸运的是,您可以通过将情节提要板本身作为资源,并在begin-storyboard动作中引用它来解决此问题:

<Resources>
    <Storyboard x:Key="WindowLoadedStoryboard" Completed="WindowLoadedStoryboard_Completed">
        ...
    </Storyboard>

    <Style ...>
        ...
        <BeginStoryboard Storyboard="{StaticResource WindowLoadedStoryboard}" />
        ...
    </Style>
</Resources>

Which, incidentally, would also make looking up that storyboard easier and more reliable. 顺便说一句,这也将使查找故事板更容易,更可靠。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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