通过C#访问XAML控件

时间:2015-11-15 19:46:27

标签: c# wpf xaml

我有一些TextBlocks x:Name,我可以使用C#轻松访问此控件。不过我还有一些DoubleAnimations x:Name但无法访问这些,为什么会这样?

我试过这个,但它不起作用,我收到错误说Object reference not set to an instance of an object

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

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

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;
                    }
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

创建自定义WindowUserControl时,会为其生成一个(部分)类,其中包含任何已命名子元素的字段。然而,这对样式和模板不起作用 - 可能因为那些可以应用于多个控件,因此名称不一定映射到单个项目(或者根本不是映射到项目)。

艰难的方式

因此,您正在寻找的动画是样式的一部分,它是您窗口中的资源。确切地说:你的风格有一系列触发器。它的第一个触发器有一个&#39;开始故事板&#39;动作,并且该动作保存了您感兴趣的故事板。它将采取一些搜索和类型转换来实现目标:

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

正如你所看到的那样,这很脆弱。要解决这个问题,你需要做的就是在XAML中重新排序事件触发器......

简单方法

但是为什么还要查看那个故事板呢?只需将其Completed事件直接绑定到XAML中的事件处理程序:

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

可悲的是,由于"The event 'Completed' cannot be specified on a Target tag in a Style. Use an EventSetter instead."错误,它无法直接运行。并且EventSetter无效,因为Storyboard.Completed不是路由事件...

幸运的是,您可以通过将故事板本身作为资源并在begin-storyboard动作中引用它来解决这个问题:

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

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

顺便提一下,这也会使查看故事板更容易,更可靠。