문제

ViewModel/프레젠테이션 모델이 사용 중일 때 진행 애니메이션을 트리거하려고 합니다.IsBusy 속성이 있고 ViewModel이 UserControl의 DataContext로 설정되어 있습니다.IsBusy 속성이 true인 경우 "progressAnimation" 스토리보드를 트리거하는 가장 좋은 방법은 무엇입니까?Blend에서는 UserControl 수준에 이벤트 트리거만 추가할 수 있으며 데이터 템플릿에서만 속성 트리거를 생성할 수 있습니다.

"progressAnimation"은 사용자 컨트롤의 리소스로 정의됩니다.

DataTrigger를 UserControl의 스타일로 추가하려고 시도했지만 StoryBoard를 시작하려고 하면 다음 오류가 발생합니다.

'System.Windows.Style' value cannot be assigned to property 'Style' 
of object'Colorful.Control.SearchPanel'. A Storyboard tree in a Style 
cannot specify a TargetName. Remove TargetName 'progressWheel'.

ProgressWheel은 애니메이션을 적용하려는 개체의 이름이므로 대상 이름을 제거하는 것은 분명히 내가 원하는 것이 아닙니다.

저는 코드를 통해 이벤트를 노출하고 애니메이션을 시작/중지하는 대신 데이터 바인딩 기술을 사용하여 XAML에서 이 문제를 해결하고 싶었습니다.

도움이 되었습니까?

해결책

ProgressWheel 자체에 애니메이션을 선언하면 원하는 것이 가능합니다.XAML:

<UserControl x:Class="TriggerSpike.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<UserControl.Resources>
    <DoubleAnimation x:Key="SearchAnimation" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:4"/>
    <DoubleAnimation x:Key="StopSearchAnimation" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:4"/>
</UserControl.Resources>
<StackPanel>
    <TextBlock Name="progressWheel" TextAlignment="Center" Opacity="0">
        <TextBlock.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsBusy}" Value="True">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <StaticResource ResourceKey="SearchAnimation"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                        <DataTrigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                   <StaticResource ResourceKey="StopSearchAnimation"/> 
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.ExitActions>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
        Searching
    </TextBlock>
    <Label Content="Here your search query"/>
    <TextBox Text="{Binding SearchClause}"/>
    <Button Click="Button_Click">Search!</Button>
    <TextBlock Text="{Binding Result}"/>
</StackPanel>

코드 숨김:

    using System.Windows;
using System.Windows.Controls;

namespace TriggerSpike
{
    public partial class UserControl1 : UserControl
    {
        private MyViewModel myModel;

        public UserControl1()
        {
            myModel=new MyViewModel();
            DataContext = myModel;
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            myModel.Search(myModel.SearchClause);
        }
    }
}

뷰모델:

    using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace TriggerSpike
{
    class MyViewModel:DependencyObject
    {

        public string SearchClause{ get;set;}

        public bool IsBusy
        {
            get { return (bool)GetValue(IsBusyProperty); }
            set { SetValue(IsBusyProperty, value); }
        }

        public static readonly DependencyProperty IsBusyProperty =
            DependencyProperty.Register("IsBusy", typeof(bool), typeof(MyViewModel), new UIPropertyMetadata(false));



        public string Result
        {
            get { return (string)GetValue(ResultProperty); }
            set { SetValue(ResultProperty, value); }
        }

        public static readonly DependencyProperty ResultProperty =
            DependencyProperty.Register("Result", typeof(string), typeof(MyViewModel), new UIPropertyMetadata(string.Empty));

        public void Search(string search_clause)
        {
            Result = string.Empty;
            SearchClause = search_clause;
            var worker = new BackgroundWorker();
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            IsBusy = true;
            worker.RunWorkerAsync();
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            IsBusy=false;
            Result = "Sorry, no results found for: " + SearchClause;
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(5000);
        }
    }
}

도움이 되었기를 바랍니다!

다른 팁

애니메이션을 적용할 요소에 직접 애니메이션을 첨부하는 방법을 제안하는 답변이 간단한 경우에는 이 문제를 해결하지만, 여러 요소를 대상으로 해야 하는 복잡한 애니메이션이 있는 경우 실제로는 실행 가능하지 않습니다.(물론 각 요소에 애니메이션을 첨부할 수 있지만 관리하기가 매우 어렵습니다.)

따라서 이 문제를 해결할 수 있는 다른 방법이 있습니다. DataTrigger 명명된 요소를 대상으로 하는 애니메이션을 실행합니다.

WPF에는 트리거를 연결할 수 있는 세 가지 위치가 있습니다.요소, 스타일, 템플릿.그러나 처음 두 옵션은 여기서 작동하지 않습니다.첫 번째는 WPF가 사용을 지원하지 않기 때문에 제외됩니다. DataTrigger 요소에 직접.(제가 아는 한 특별한 이유는 없습니다.제가 기억하는 한, 몇 년 전 WPF 팀 사람들에게 이에 대해 물었을 때 그들은 지원하고 싶었지만 이를 작동시킬 시간이 없었다고 말했습니다.) 그리고 스타일은 사라졌습니다. 보고하신 오류 메시지에는 스타일과 연결된 애니메이션에서 명명된 요소를 대상으로 지정할 수 없다고 나와 있습니다.

그러면 템플릿이 남습니다.이를 위해 컨트롤 또는 데이터 템플릿을 사용할 수 있습니다.

<ContentControl>
    <ContentControl.Template>
        <ControlTemplate TargetType="ContentControl">
            <ControlTemplate.Resources>
                <Storyboard x:Key="myAnimation">

                    <!-- Your animation goes here... -->

                </Storyboard>
            </ControlTemplate.Resources>
            <ControlTemplate.Triggers>
                <DataTrigger
                    Binding="{Binding MyProperty}"
                    Value="DesiredValue">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard
                            x:Name="beginAnimation"
                            Storyboard="{StaticResource myAnimation}" />
                    </DataTrigger.EnterActions>
                    <DataTrigger.ExitActions>
                        <StopStoryboard
                            BeginStoryboardName="beginAnimation" />
                    </DataTrigger.ExitActions>
                </DataTrigger>
            </ControlTemplate.Triggers>

            <!-- Content to be animated goes here -->

        </ControlTemplate>
    </ContentControl.Template>
<ContentControl>

이 구성을 통해 WPF에서는 애니메이션이 템플릿 내부의 명명된 요소를 참조할 수 있게 되었습니다.(여기서는 애니메이션과 템플릿 콘텐츠를 모두 비워 두었습니다. 당연히 실제 애니메이션 콘텐츠로 채울 것입니다.)

이것이 템플릿에서는 작동하지만 스타일에서는 작동하지 않는 이유는 템플릿을 적용할 때 정의된 명명된 요소가 항상 존재하므로 해당 템플릿 범위 내에 정의된 애니메이션이 해당 요소를 참조하는 것이 안전하기 때문입니다.일반적으로 스타일의 경우는 그렇지 않습니다. 왜냐하면 스타일은 서로 다른 여러 요소에 적용될 수 있으며 각 요소는 매우 다른 시각적 트리를 가질 수 있기 때문입니다.(필요한 요소가 있을 것이라고 확신할 수 있는 시나리오에서도 이 작업을 수행할 수 없다는 점은 조금 실망스럽습니다. 하지만 애니메이션이 오른쪽에 명명된 요소에 바인딩되는 것을 매우 어렵게 만드는 것이 있을 수 있습니다. 시간.스타일 요소를 효율적으로 재사용할 수 있도록 WPF에 최적화가 많이 있다는 것을 알고 있으므로 아마도 그 중 하나가 이 기능을 지원하기 어렵게 만드는 것 같습니다.)

IsBusy 속성 대신 RoutedEvent를 사용하는 것이 좋습니다.OnBusyStarted 및 OnBusyStopped 이벤트를 실행하고 적절한 요소에 이벤트 트리거를 사용하세요.

DataObject 클래스의 PropertyChanged 이벤트를 구독하고 Usercontrol 수준에서 RoutedEvent가 실행되도록 할 수 있습니다.

RoutedEvent가 작동하려면 종속성 개체(DependancyObject)에서 파생된 클래스가 있어야 합니다.

속성이 변경되면 Trigger.EnterAction을 사용하여 애니메이션을 시작할 수 있습니다.

<Trigger Property="IsBusy" Value="true">
    <Trigger.EnterActions>
        <BeginStoryboard x:Name="BeginBusy" Storyboard="{StaticResource MyStoryboard}" />
    </Trigger.EnterActions>
    <Trigger.ExitActions>
        <StopStoryboard BeginStoryboardName="BeginBusy" />
    </Trigger.ExitActions>
</Trigger>
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top