WPF должно быть проще, чем я это делаю

У меня ужасное время, чтобы понять это: скажем, у меня есть две кнопки и три текстовых блока. Я хочу, чтобы любая кнопка запускала простую раскадровку на ВСЕХ текстовых блоках. В настоящее время я пытаюсь определить общий стиль Textblock, который содержит раскадровку, а затем триггер исходит от любого нажатия кнопки. Это самое близкое, что я подобрал, но приложение вылетает при запуске... в чем я здесь не ошибаюсь:

<Window.Resources>

<Style TargetType="TextBlock" >
    <Setter Property="Foreground" Value="Blue" />
    <Style.Resources>
        <Storyboard x:Key="TextBlockOpacity" Storyboard.TargetProperty="Opacity">
            <DoubleAnimation From="0" To="1" />
        </Storyboard>
    </Style.Resources>      
</Style>

<Window.Triggers>
    <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button">
        <BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}"/>
    </EventTrigger>
</Window.Triggers>


<Grid x:Name="LayoutRoot">
    <Button x:Name="button" HorizontalAlignment="Left" Margin="51,54,0,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button"/>

    <TextBlock x:Name="textBlock1" Margin="228,54,172,0" VerticalAlignment="Top" Height="45" FontSize="26.667" Text="TextBlock" TextWrapping="Wrap" />
    <TextBlock x:Name="textBlock2" Margin="228,103,172,0" VerticalAlignment="Top" Height="45" FontSize="26.667" Text="Hello" TextWrapping="Wrap"/>
</Grid>

person LSTayon    schedule 06.08.2009    source источник
comment
Не могли бы вы попробовать дать раскадровке имя, а затем посмотреть, сможете ли вы в коде программной части правильно начать()?   -  person Jeff Wilcox    schedule 06.08.2009
comment
Вы задаете тот же вопрос здесь: stackoverflow.com/questions/1238817/wpf-animation -вопрос или вы ищете триггеры с двумя нажатиями кнопок?   -  person SergioL    schedule 06.08.2009
comment
Я думаю, что кто-то с представителем должен объединить этот вопрос с тем, на который я ссылался в комментарии выше.   -  person SergioL    schedule 06.08.2009


Ответы (3)


На основе решения kek444, предназначенного только для Xaml, я представляю слегка улучшенную версию, которая не зависит от DataContext кнопки и может иметь несколько триггеров.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="WpfApplication1.MainWindow"
    x:Name="Window"
    Title="MainWindow"
    Width="640" Height="480">
    <Window.Resources>
        <UIElement x:Key="OpacityCounter" Opacity="0"/>
        <Style TargetType="TextBlock">
            <Setter Property="Opacity" Value="{Binding Source={StaticResource OpacityCounter}, Path=Opacity}" />
        </Style>
        <Storyboard x:Key="OnClick1">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.Target="{StaticResource OpacityCounter}" Storyboard.TargetProperty="(UIElement.Opacity)">
                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
            <BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button2">
            <BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
        </EventTrigger>
    </Window.Triggers>

    <Grid x:Name="LayoutRoot">
        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button x:Name="button1" Width="131" Height="37" Content="Button 1" Margin="0,0,0,22"/>
                <Button x:Name="button2" Width="131" Height="37" Content="Button 2" Margin="0,0,0,22"/>
            </StackPanel>
            <TextBlock x:Name="textBlock" Height="27" Text="TextBlock 1" TextWrapping="Wrap" />
            <TextBlock x:Name="textBlock1" Height="27" Text="TextBlock 2" TextWrapping="Wrap" />
            <TextBlock x:Name="textBlock2" Height="27" Text="TextBlock 3" TextWrapping="Wrap" />
            <TextBlock x:Name="textBlock3" Height="27" Text="TextBlock 4" TextWrapping="Wrap" />
        </StackPanel>
    </Grid>
</Window>

Чтобы использовать ListBox в качестве механизма триггера (при условии, что где-то у вас есть ListBox с именем «listbox1», добавьте в Window.Triggers следующее:

<EventTrigger RoutedEvent="Selector.SelectionChanged" SourceName="listbox1">
    <BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>

или для запуска определенного ListBoxItem вам понадобится (где item1 - это именованный ListBoxItem):

<EventTrigger RoutedEvent="ListBoxItem.Selected" SourceName="item1">
    <BeginStoryboard Storyboard="{StaticResource OnClick1}"/>
</EventTrigger>
person SergioL    schedule 06.08.2009
comment
Это очень мило! Я хотел избежать создания UIElement в качестве ресурсов, но люди должны выбирать то, что им больше подходит. Однако назвать этот подход улучшенным на основании того факта, что он не использует DataContext и поддерживает несколько триггеров, было бы несколько несправедливо. Использование ресурсного UIElement поверх DataContext — это просто вопрос личных предпочтений; и оба решения прекрасно поддерживают несколько триггеров. В остальном хорошая работа. ;) - person Kenan E. K.; 06.08.2009
comment
Хорошо... теперь, чтобы сделать это немного сложнее... Я на самом деле не пытаюсь вызвать кнопку, но изменение ListBoxItem из ListBox, который находится внутри StackPanel, внутри границы, внутри сетки в главное окно. Я пробовал это часами, но не могу получить правильное RoutedEvent... все события ListBoxItem выдают ошибку о закрытии класса или что-то в этом роде... - person LSTayon; 06.08.2009
comment
Я назвал его улучшенным только потому, что он не требует отдельного DataContext для каждого механизма триггера (то есть кнопок). Я не хотел обидеть. - person SergioL; 06.08.2009
comment
Все в порядке, без обид, просто хотел указать на это. Вы должны отметить, что несколько DataContexts не являются необходимыми. Я немного расширил пример для полноты картины. - person Kenan E. K.; 06.08.2009
comment
@unknown(google): ошибка возникает из-за того, что сборка не создана для доступа к новым ресурсам. Это должно работать после сборки. - person Kenan E. K.; 06.08.2009
comment
Хорошо, это, наконец, собирается вместе... Я получаю сообщение об ошибке о том, что событие было запечатано при попытке вызвать из ListBox... оказывается, это потому, что я использовал это событие в объявлении Listbox... Я избавился этого звонка пока... Всем спасибо!!! - person LSTayon; 06.08.2009

Если вы «посвятите» кнопку изменению непрозрачности, вы можете использовать ее DataContext и анимировать. Затем просто привяжите элементы Opacity к DataContext:

(Я также немного реорганизовал ваш xaml)

<Window x:Class="SomeNamespace.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:System="clr-namespace:System;assembly=mscorlib"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>        
        <Storyboard x:Key="TextBlockOpacity" Storyboard.TargetName="button1" Storyboard.TargetProperty="DataContext" >
            <DoubleAnimation From="0.1" To="1"/>
        </Storyboard>        
        <Style TargetType="TextBlock" >
            <Setter Property="Foreground" Value="Blue" />
            <Setter Property="Background" Value="LightGray" />
            <Setter Property="FontSize" Value="26.667" />
            <Setter Property="TextWrapping" Value="Wrap" />
            <Setter Property="Height" Value="45" />            
            <Setter Property="Opacity" Value="{Binding ElementName=button1, Path=DataContext}"/>
        </Style>
    </Window.Resources>

    <Window.Triggers>
        <EventTrigger RoutedEvent="ButtonBase.Click">
            <BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}" >
            </BeginStoryboard>
        </EventTrigger>

        <EventTrigger RoutedEvent="ListBox.SelectionChanged">
            <BeginStoryboard Storyboard="{StaticResource TextBlockOpacity}" >
            </BeginStoryboard>
        </EventTrigger>
    </Window.Triggers>

    <Grid x:Name="LayoutRoot">
        <Button x:Name="button1" HorizontalAlignment="Left" Margin="51,54,0,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button">
            <Button.DataContext>
                <System:Double>0</System:Double>
            </Button.DataContext>
        </Button>

        <Button x:Name="button2" HorizontalAlignment="Right" Margin="0,54,29,0" VerticalAlignment="Top" Width="96" Height="45" Content="Button"/>

        <ListBox x:Name="listBox1" Height="50" VerticalAlignment="Top">
            <ListBox.Items>
                <System:String>Text1</System:String>
                <System:String>Text2</System:String>
            </ListBox.Items>
        </ListBox>

        <TextBlock x:Name="textBlock1" Margin="51,114,61,0" Text="TextBlock" Height="45" VerticalAlignment="Top" Width="166" />
        <TextBlock x:Name="textBlock2" Margin="51,0,74,42" Text="Hello" Height="45" Width="153" VerticalAlignment="Bottom" />
    </Grid>
</Window>

Также обратите внимание на одну вещь: этот подход следует использовать, если вы хотите минимизировать свой код, и сделать все это в xaml. Ваш подход оживил бы Opacity всего окна. Вот почему в приведенном выше коде TextBlocks привязываются к DataContext кнопки, которая сама анимирована.

Это, конечно, выполнимо без привязки к общему значению (DataContext), но тогда вам нужно повторить X анимаций (потому что вам нужно установить X TargetNames). Описанный выше подход легче расширять и поддерживать.

ИЗМЕНИТЬ

Добавил еще одну кнопку и ListBox для разнообразия :)

person Kenan E. K.    schedule 06.08.2009

В вашем образце вы определяете раскадровку внутри стиля как ресурс, но затем пытаетесь получить к ней доступ как к ресурсу окна. Попробуйте переместить объявление Storyboard в Window.Resources, а затем сослаться на Storyboard в Style.

Я не знаю сразу, будет ли это делать то, что вы хотите, но я бы начал с этого.

person Joel Cochran    schedule 06.08.2009
comment
Хорошо, я бегал вокруг этого вечно, но все сводится к NameScope. У меня есть ListBox, в котором я хочу запустить анимацию для TextBlocks, но они не находятся в одном и том же NameScope, поэтому, если я помещу StoryBoard в Windows.Resources, тогда триггер ListBox может найти StoryBoard, но StoryBoard не может найти TargetName текстовых блоков. Если я помещу StoryBoard в Grid.Resource (где находятся TextBlocks), то триггер изменения ListBox не сможет найти StoryBoard. Есть ли какое-то соглашение об именах, которое я могу использовать для пересечения Namescopes? Фу! Спасибо! - person LSTayon; 06.08.2009