سؤال

أحاول تشغيل رسم متحرك للتقدم عندما يكون نموذج العرض/العرض التقديمي مشغولاً.لدي خاصية IsBusy، وتم تعيين ViewModel كـ DataContext لـ UserControl.ما هي أفضل طريقة لتشغيل لوحة القصة "progressAnimation" عندما تكون خاصية IsBusy صحيحة؟يسمح Blend فقط بإضافة مشغلات الأحداث على مستوى UserControl، ويمكنني فقط إنشاء مشغلات الخصائص في قوالب البيانات الخاصة بي.

يتم تعريف "progressAnimation" كمورد في عنصر تحكم المستخدم.

لقد حاولت إضافة DataTriggers كنمط في 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 باستخدام تقنيات ربط البيانات، بدلاً من الاضطرار إلى كشف الأحداث وبدء/إيقاف الرسوم المتحركة من خلال التعليمات البرمجية.

هل كانت مفيدة؟

المحلول

ما تريده ممكن من خلال إعلان الرسوم المتحركة على عجلة التقدم نفسها: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 لتمكين إعادة استخدام عناصر النمط بكفاءة، لذلك ربما يكون أحد هذه التحسينات هو ما يجعل دعم هذا أمرًا صعبًا.)

أوصي باستخدام RoutedEvent بدلاً من خاصية IsBusy الخاصة بك.ما عليك سوى إطلاق حدث OnBusyStarted وOnBusyStopped واستخدام مشغل الحدث على العناصر المناسبة.

يمكنك الاشتراك في حدث PropertyChanged لفئة DataObject وإنشاء RoutedEvent من مستوى Usercontrol.

لكي يعمل 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