سئوالات و مباحث WPF

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
روی دکمه btnLevel_1 کلیک می کنید، به سمت بالا و والدین اش پیمایش می کنه و چیزی برای اجرا کردن برای رخداد کلیک پیدا نمی کنه و CommonClickHandler هم اجرا نمیشه.
روی دکمه btnLevel_2 کلیک می کنید، به سمت بالا و والدین اش پیمایش می کنه و چیزی برای اجرا کردن برای رخداد کلیک پیدا نمی کنه و CommonClickHandler هم اجرا نمیشه.
روی دکمه btnLevel_3 کلیک می کنید، به سمت بالا و والدین اش پیمایش می کنه و ButtonBase.Click رو در همون شروع پیمایش در المنت خودش می بینه و CommonClickHandler اجرا میشه.
روی دکمه btnLevel_4 کلیک می کنید، به سمت بالا و والدین اش پیمایش می کنه و ButtonBase.Click رو در المنت والدش پیدا می کنه و CommonClickHandler اجرا میشه.

چه چیزی در پیمایش این رخداد ها به سمت پایین بود؟ روی btnLevel_1 که کلیک می کنید ButtonBase.ClickButtonBase.Click رو در پایین و فرزندانش پیدا می کنه و CommonClickHandler اجرا میشه؟

خیلی ممنون استاد :rose:
آها ، منظورتون اینه که وقتی رویدادی در کنترلی اتفاق میافته ، اگه استراتژی حباب را داشته باشه ، توی هر کنترلی که اون اتفاق افتاد ، به سمت بالا (به سمت والدش) پیمایش میکنه و اگه کدی که مرتبط با اون رویداد هست (از نوع Routed Event و استراتژی حباب) را در کنترل والدش دید ، اون رویداد را فقط اجرا میکنه (متصل و اضافه نمیکنه) . منظورتون اینه؟


بنابراین :
وقتی روی دکمه btnLevel_1 کلیک می کنیم ، به سمت بالا و والدین اش پیمایش می کنه و چون در هیچ یک از والدش ، اولا کنترلی از نوع ButtonBase (یا از نوع فرزندانش) وجود نداره و دوما اگه کنترلی از این نوع وجود میداشت ، باید رویدادِ ButtonBase.Click ئه مربوط به اون کنترل ، مقداردهی میشد تا در مسیرِ پیمایشِ btnLevel_1 که افتاد ، اون رویداد را اجرا کنه (نه اینکه متصل و اضافه کنه . مثل متد که فقط اجرا میشه) .

همچنین وقتی روی دکمه btnLevel_2 کلیک می کنیم ، به سمت بالا و والدین اش پیمایش می کنه . والدش که btnLevel_1 باشه ، از نوع ButtonBase هست اما چون در شی btnLevel_1 ، رویداد ButtonBase.Click ای مقداردهی نشد ، پس رویدادی اصلا در والدش وجود نداره که اون را اجرا کنه .

دکمه btnLevel_3 که معلومه . وقتی روش کلیک میکنیم ، اول ، خود همون رویدادِ ButtonBase.Click که درون خودِ همین شی مقداردهی کردیم ، اجرا میشه . بعد به سمت والدش میره که برای هیچ کدوم از والدهاش (یعنی شی btnLevel_1 و btnLevel_2) ، رویداد ButtonBase.Click مقداردهی نشد .

وقتی روی دکمه btnLevel_4 کلیک می کنیم ، به سمت بالا و والدین اش که دکمه ی btnLevel_3 باشه ، پیمایش می کنه و چون در این دکمه ، رویداد ButtonBase.Click مقداردهی شد ، پس این رویداد را اجرا میکنه (نه اینکه متصل کنه) .


نکته ی مهم اینه که Source ، همون شی ای هست که در حال پیمایش کردن هست . مثلا وقتی روی شی btnLevel_3 کلیک میکنیم ، چون این شی ، در حال پیمایش کردن به سمت والدین هست ، پس Source مون در این لحظه ، همین شی (btnLevel_3) هست . وقتی هم که روی شیِ btnLevel_4 کلیک میکنیم ، چون این شی در حال پیمایش کردن به سمت والدین اش هست ، پس Source مون در این لحظه ، همین شی (btnLevel_4) هست .

اما sender ، اون شی ای هست که در Xaml (یا سی شارپ) ، اون رویداد را براش نوشتیم . یعنی در مثالی که گفته شد ، همیشه ، sender مون ، شی btnLevel_3 هست (چون رویداد ButtonBase.Click را در این شی نوشتیم) .
در واقع ، وقتی btnLevel_4 ، در مسیر پیمایش به سمت بالا ، به btnLevel_3 رسید ، به شی btnLevel_4 اطلاع میده که رویدادِ ButtonBase.Click اش را اجرا کنه (یعنی خودِ شیِ btnLevel_4 ، اجرا کننده ی رویدادِ ButtonBase.Click در شی btnLevel_3 نیست) و چون شیِ btnLevel_3 ، رویداد را اجرا میکنه ، پس sender مون ، همون شیِ btnLevel_3 هست اما Source مون ، شیِ btnLevel_4 هست .

بنابراین ، در کد زیر :

کد:
    <Button HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"  Name="btnLevel_1" Margin="20">
        <Button ButtonBase.Click="CommonClickHandler"  Name="btnLevel_2"  Background="DarkCyan" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="100">
            <Button ButtonBase.Click="CommonClickHandler"  Name="btnLevel_3" Margin="50,50" Width="550" Height="200" Background="DarkKhaki" HorizontalContentAlignment="Left" VerticalContentAlignment="Top">
                <Button  Name="btnLevel_4" Margin="70, 30" Width="400" Height="130" Background="DarkMagenta" HorizontalContentAlignment="Left" VerticalContentAlignment="Top">

                </Button>
            </Button>
        </Button>
    </Button>

وقتی روی دکمه ی btnLevel_3 کلیک میکنیم ، اول ، رویدادِ ButtonBase.Click ئه مربوط به خودش را اجرا میکنه و بعد ، والدش را پیمایش میکنه و میبینه توی والدِ btnLevel_2 اش هم یه رویدادِ ButtonBase.Click وجود داره که این را هم مجددا اجرا میکنه (در واقع ، 2 بار در کد بالا به این دلیل ، برای این شی و همینطور شی btnLevel_4 ، رویداد اجرا میشه) .

اینهایی که در بالا گفتم ، درستن؟

و یک چیز دیگه اینکه وقتی ، هر رویدادِ مربوط به Routed Event در یک کنترل (و شی) اتفاق بیافته (صرف نظر از اینکه اون رویداد را برای اون کنترل در xaml یا سی شارپ نوشته باشیم یا ننوشته باشیم) ، باعث میشه اون شی و کنترل ، والدش را اجرا کنه؟
یعنی مثلا در کد اولی (نه کد بالا . بلکه کد اولی در پست 197) ، برای کنترل های btnLevel_1 و btnLevel_2 ، رویداد ButtonBase.Click را مقداردهی نکردیم . اما با این حال ، وقتی روی این دو کنترل کلیک کنیم ، باز باعث میشه که این کنترل ها ، والدشون را به سمت بالا (در صورت داشتنِ استراتژی حباب) پیمایش کنن؟ یا اینکه صرفا هر کنترلی را که روش کلیک کردیم و باز هم اگه رویدادِ Routed Event ده مربوط به اون کنترل را مقداردهی کرده باشیم (مثل رویداد ButtonBase.Click) ، در این صورت فقط پیمایش میکنن؟

اینکه رویداد Click توانایی routing داشته باشه که مشکلی نیست، routing اش اختیاری است، می توانید بگید نکنه.
باید تاپیک Routed Events رو بخونید، نخوندید که، اگه میخوندید سوال نمی پرسیدید. براتون مثال کد هم نوشته بود.

در حال خوندن هستم .
تا قسمت How Routed Events Are Implemented اش را خوندم و این مشکلات را داشتم که مطرح کردم .
انگار با ست کردنِ پروپرتیِ e.Handled به true در رویداد (هندلر) ، دیگه از اون به بعد ، پیمایش انجام نمیده .

نمیفهمم چی میگید، ظاهرش مگه چطوری میشه که میگید شانسی ئه؟

جوابش را در پست 198 تون دادید و متوجه شدم .

خیلی ممنون استاد :rose:
 

the_king

مدیرکل انجمن
خیلی ممنون استاد :rose:
آها ، منظورتون اینه که وقتی رویدادی در کنترلی اتفاق میافته ، اگه استراتژی حباب را داشته باشه ، توی هر کنترلی که اون اتفاق افتاد ، به سمت بالا (به سمت والدش) پیمایش میکنه و اگه کدی که مرتبط با اون رویداد هست (از نوع Routed Event و استراتژی حباب) را در کنترل والدش دید ، اون رویداد را فقط اجرا میکنه (متصل و اضافه نمیکنه) . منظورتون اینه؟
بله. منظور من اون چیزی است که می نویسم، اگه چیز دیگری بهش اضافه نکنید مشکل حل میشه. متصل و اضافه کردن رو خودتون اضافه کردید. طبعا اگر قرار باشه با چهار بار کلیک روی یک دکمه، چهار بار متد توصیف شده در والدش به رخداد کلیک متصل و اضافه بشه که همچین روالی قابل استفاده و منطقی نمیشه.

نکته ی مهم اینه که Source ، همون شی ای هست که در حال پیمایش کردن هست . مثلا وقتی روی شی btnLevel_3 کلیک میکنیم ، چون این شی ، در حال پیمایش کردن به سمت والدین هست ، پس Source مون در این لحظه ، همین شی (btnLevel_3) هست . وقتی هم که روی شیِ btnLevel_4 کلیک میکنیم ، چون این شی در حال پیمایش کردن به سمت والدین اش هست ، پس Source مون در این لحظه ، همین شی (btnLevel_4) هست .
در این مثال هست ولی حالت کلی نداره، نه الزاما، Source صرفا مقداری است که قراره منبع بروز رخداد باشه. اینکه Source چی باشه بستگی به این داره که ایجاد کننده اش بخواد چه منبعی به عنوان Source معرفی بشه.
ممکنه کلاس ایجاد کننده رخداد بخواد به هر دلیلی شیء دیگری رو منبع معرفی کنه، دست خودشه.
Source مقداری است که ایجاد کننده رخداد ارسال کرده، کد برنامه اش ممکنه طوری باشه که بخواد شیء متفاوت دیگری رو منبع معرفی کنه.
اینکه چه مقداری داره به منطق کدی که رخداد رو ایجاد می کنه بستگی داره. توضیحات RoutedEventArgs.Source Property (System.Windows) رو بخونید.

اما sender ، اون شی ای هست که در Xaml (یا سی شارپ) ، اون رویداد را براش نوشتیم . یعنی در مثالی که گفته شد ، همیشه ، sender مون ، شی btnLevel_3 هست (چون رویداد ButtonBase.Click را در این شی نوشتیم) .
در واقع ، وقتی btnLevel_4 ، در مسیر پیمایش به سمت بالا ، به btnLevel_3 رسید ، به شی btnLevel_4 اطلاع میده که رویدادِ ButtonBase.Click اش را اجرا کنه (یعنی خودِ شیِ btnLevel_4 ، اجرا کننده ی رویدادِ ButtonBase.Click در شی btnLevel_3 نیست) و چون شیِ btnLevel_3 ، رویداد را اجرا میکنه ، پس sender مون ، همون شیِ btnLevel_3 هست اما Source مون ، شیِ btnLevel_4 هست .
بله.

بنابراین ، در کد زیر :

کد:
    <Button HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"  Name="btnLevel_1" Margin="20">
        <Button ButtonBase.Click="CommonClickHandler"  Name="btnLevel_2"  Background="DarkCyan" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="100">
            <Button ButtonBase.Click="CommonClickHandler"  Name="btnLevel_3" Margin="50,50" Width="550" Height="200" Background="DarkKhaki" HorizontalContentAlignment="Left" VerticalContentAlignment="Top">
                <Button  Name="btnLevel_4" Margin="70, 30" Width="400" Height="130" Background="DarkMagenta" HorizontalContentAlignment="Left" VerticalContentAlignment="Top">

                </Button>
            </Button>
        </Button>
    </Button>

وقتی روی دکمه ی btnLevel_3 کلیک میکنیم ، اول ، رویدادِ ButtonBase.Click ئه مربوط به خودش را اجرا میکنه و بعد ، والدش را پیمایش میکنه و میبینه توی والدِ btnLevel_2 اش هم یه رویدادِ ButtonBase.Click وجود داره که این را هم مجددا اجرا میکنه (در واقع ، 2 بار در کد بالا به این دلیل ، برای این شی و همینطور شی btnLevel_4 ، رویداد اجرا میشه) .

اینهایی که در بالا گفتم ، درستن؟
بله. مشکل تون با جهت پیمایش حل شد؟

و یک چیز دیگه اینکه وقتی ، هر رویدادِ مربوط به Routed Event در یک کنترل (و شی) اتفاق بیافته (صرف نظر از اینکه اون رویداد را برای اون کنترل در xaml یا سی شارپ نوشته باشیم یا ننوشته باشیم) ، باعث میشه اون شی و کنترل ، والدش را اجرا کنه؟
یعنی مثلا در کد اولی (نه کد بالا . بلکه کد اولی در پست 197) ، برای کنترل های btnLevel_1 و btnLevel_2 ، رویداد ButtonBase.Click را مقداردهی نکردیم . اما با این حال ، وقتی روی این دو کنترل کلیک کنیم ، باز باعث میشه که این کنترل ها ، والدشون را به سمت بالا (در صورت داشتنِ استراتژی حباب) پیمایش کنن؟ یا اینکه صرفا هر کنترلی را که روش کلیک کردیم و باز هم اگه رویدادِ Routed Event ده مربوط به اون کنترل را مقداردهی کرده باشیم (مثل رویداد ButtonBase.Click) ، در این صورت فقط پیمایش میکنن؟
پیمایش جهت دار همیشه انجام میشه، مگر اینکه در جایی دریافت کننده اعلام کنه که این رخداد توسط من مدیریت شده و دیگه پیمایش رو ادامه نده. مثل همون KeyEventArgs.Handled Property (System.Windows.Forms) در Windows Forms که میگه من رخداد این کلید رو در این مرحله مدیریت کردم و دیگه به عنوان کلید تایپ شده پردازش اش نکن.
در Windows Forms هم پیمایش رخداد ها انجام میشه، اگر غیر از این بود با کلیک روی یک کنترل فرزند، پنجره اصلی اش Focus نمی گرفت و Active نمیشد یا با تغییر Resolution صفحه نمایش هم کنترل ها از نو رسم نمی شدند یا با غیر فعال شدن یک کنترل فرزندانش غیر فعال نمی شدند.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
بله. منظور من اون چیزی است که می نویسم، اگه چیز دیگری بهش اضافه نکنید مشکل حل میشه. متصل و اضافه کردن رو خودتون اضافه کردید. طبعا اگر قرار باشه با چهار بار کلیک روی یک دکمه، چهار بار متد توصیف شده در والدش به رخداد کلیک متصل و اضافه بشه که همچین روالی قابل استفاده و منطقی نمیشه.


در این مثال هست ولی حالت کلی نداره، نه الزاما، Source صرفا مقداری است که قراره منبع بروز رخداد باشه. اینکه Source چی باشه بستگی به این داره که ایجاد کننده اش بخواد چه منبعی به عنوان Source معرفی بشه.
ممکنه کلاس ایجاد کننده رخداد بخواد به هر دلیلی شیء دیگری رو منبع معرفی کنه، دست خودشه.
Source مقداری است که ایجاد کننده رخداد ارسال کرده، کد برنامه اش ممکنه طوری باشه که بخواد شیء متفاوت دیگری رو منبع معرفی کنه.
اینکه چه مقداری داره به منطق کدی که رخداد رو ایجاد می کنه بستگی داره. توضیحات RoutedEventArgs.Source Property (System.Windows) رو بخونید.


بله.


بله. مشکل تون با جهت پیمایش حل شد؟

خیلی ممنون استاد :rose:
بله . مشکلم حل شد .
قبلا (که مشکل داشتم) ، فکر میکردم منظورشون از پیمایش به سمت بالا اینه که مثلا وقتی رویداد ButtonBase.Click را در کنترل btnLevel_3 نوشتیم ، روی هر کنترلی که از نوع ButtonBase که والدِ btnLevel_3 باشه ، کلیک کنیم (مثلا وقتی روی btnLevel_2 که والد btnLevel_3 هست ، کلیک کنیم) ، اون رویداد را باید اجرا (یا حتی اضافه) کنه .
الان دقیق متوجه شدم که قضیه چیه .

پیمایش جهت دار همیشه انجام میشه، مگر اینکه در جایی دریافت کننده اعلام کنه که این رخداد توسط من مدیریت شده و دیگه پیمایش رو ادامه نده. مثل همون KeyEventArgs.Handled Property (System.Windows.Forms) در Windows Forms که میگه من رخداد این کلید رو در این مرحله مدیریت کردم و دیگه به عنوان کلید تایپ شده پردازش اش نکن.
در Windows Forms هم پیمایش رخداد ها انجام میشه، اگر غیر از این بود با کلیک روی یک کنترل فرزند، پنجره اصلی اش Focus نمی گرفت و Active نمیشد یا با تغییر Resolution صفحه نمایش هم کنترل ها از نو رسم نمی شدند یا با غیر فعال شدن یک کنترل فرزندانش غیر فعال نمی شدند.

خیلی ممنون استاد :rose:
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
خیلی ممنون استاد .
استاد ، حالا استراتژیِ تونل (اون رویدادهایی که با نام Preview شروع میشن) ، مگه برعکسِ حالتِ استراتژیِ حباب نیست؟
اگه آره ، پس چرا در کد زیر ، شبیه استراتژی حباب عمل میکنه؟ :

کد:
    <Button Name="btnLevel_1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40">
        <Button Name="btnLevel_2" PreviewMouseDown="btnLevel_2_PreviewMouseDown"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkCyan">
            <Button Name="btnLevel_3"   HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkKhaki">
                <Button Name="btnLevel_4" Margin="40" Background="DarkOrange">

                </Button>
            </Button>
        </Button>
    </Button>

و رویداد سی شارپ :

کد:
        private void btnLevel_2_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement senderElement = sender as FrameworkElement;
            FrameworkElement sourceElement = e.Source as FrameworkElement;
            Debug.WriteLine("sender fired :  {0}  ....  Source : {1}", new string[] { senderElement.Name, sourceElement.Name });
        }

یعنی کدهای بالا ، مثل کد زیر (همون کد پست های قبل) عمل میکنه :

کد:
    <Button Name="btnLevel_1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40">
        <Button Name="btnLevel_2" ButtonBase.Click="CommonClickHandler"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkCyan">
            <Button Name="btnLevel_3"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkKhaki">
                <Button Name="btnLevel_4" Margin="40" Background="DarkOrange">

                </Button>
            </Button>
        </Button>
    </Button>

و رویداد سی شارپ :

کد:
        private void CommonClickHandler(object sender, RoutedEventArgs e)
        {
            FrameworkElement senderElement = sender as FrameworkElement;
            FrameworkElement sourceElement = e.Source as FrameworkElement;
            Debug.WriteLine("sender fired :  {0}  ....  Source : {1}", new string[] { senderElement.Name, sourceElement.Name });
        }

چرا این دو رویداد با دو استراتژی متفاوت ، مثل هم عمل میکنن؟ مگه بر عکسِ هم نیستند؟
 

the_king

مدیرکل انجمن
خیلی ممنون استاد .
استاد ، حالا استراتژیِ تونل (اون رویدادهایی که با نام Preview شروع میشن) ، مگه برعکسِ حالتِ استراتژیِ حباب نیست؟
برعکس ئه اما نمیدونم منظورتون از برعکس مشابه اون چیزی که من فکر می کنم یا نه. در مورد تفاوت Tunnel نسبت به Bubble اینطور میشه گفت که بجای اینکه از منبع بروز رخداد به سمت والدین اش و ریشه پیمایش کنه، از ریشه به سمت منبع بروز رخداد پیمایش می کنه. یعنی هم نقطه شروع و پایان اش برعکس ئه و هم جهت پیمایش. نه فقط جهت پیمایش. اگر منظورتون از برعکس همزمان هم عوض شدن جای نقطه شروع و پایان باشه و هم تغییر جهت پیمایش، بله، برعکس ئه.
اما اگه منظورتون از برعکس فقط این باشه که جهت پیمایش عوض بشه و از منبع بروز رخداد بجای اینکه به سمت بالا پیمایش کنه به سمت پایین و فرزندان اون المنت پیمایش کنه، نه Tunnel اینطور نیست.
در Tunnel علاوه بر اینکه جهت پیمایش برعکس Bubble ئه، جای نقطه شروع و پایان پیمایش اش هم عوض شده.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
برعکس ئه اما نمیدونم منظورتون از برعکس مشابه اون چیزی که من فکر می کنم یا نه. در مورد تفاوت Tunnel نسبت به Bubble اینطور میشه گفت که بجای اینکه از منبع بروز رخداد به سمت والدین اش و ریشه پیمایش کنه، از ریشه به سمت منبع بروز رخداد پیمایش می کنه. یعنی هم نقطه شروع و پایان اش برعکس ئه و هم جهت پیمایش. نه فقط جهت پیمایش. اگر منظورتون از برعکس همزمان هم عوض شدن جای نقطه شروع و پایان باشه و هم تغییر جهت پیمایش، بله، برعکس ئه.
در Tunnel علاوه بر اینکه جهت پیمایش برعکس Bubble ئه، جای نقطه شروع و پایان پیمایش اش هم عوض شده.

خیلی ممنون استاد
بله . منظورم از برعکس ، همینی هست که گفتید .
فکر کنم حالا یه کم متوجه شدم. الان در کد زیر (همون کد اول در پست 204) (پدر کنترل ، btnLevel_1 ، کنترل Window هست) :

کد:
    <Button Name="btnLevel_1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40">
        <Button Name="btnLevel_2" PreviewMouseDown="btnLevel_2_PreviewMouseDown"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkCyan">
            <Button Name="btnLevel_3"   HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkKhaki">
                <Button Name="btnLevel_4" Margin="40" Background="DarkOrange">

                </Button>
            </Button>
        </Button>
    </Button>

و در رویداد سی شارپ :

کد:
        private void btnLevel_2_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement senderElement = sender as FrameworkElement;
            FrameworkElement sourceElement = e.Source as FrameworkElement;
            Debug.WriteLine("sender fired :  {0}  ....  Source : {1}", new string[] { senderElement.Name, sourceElement.Name });
        }

الان کد بالا (که استراتژی تونل داره) ، این طور که در زیر میگم ، عمل کنه؟ :

وقتی دکمه ی btnLevel_1 را کلیک میکنیم ، رویداد کلیک از windows ، شروع میکنه به پیمایش تا به Source که کنترل btnLevel_1 هست ، برسه . اما چون در مسیر پیمایش اش (تا سطح btnLevel_1) ، به هیچ کنترلی که رویداد کلیک (ئه مربوط به ButtonBase) داشته باشن ، برخورد نمیکنه (منظورم اینه که به ست اش برنمیخوره) ، پس رویدادی را اجرا نمیکنه (این قضیه ، برای وقتی که روی کنترل Window هم کلیک میکنیم ، اتفاق میافته منتها پیمایشی انجام نمیگیره چون هم مبدا و هم مقصد ، خود window هست) .

وقتی دکمه ی btnLevel_2 را کلیک میکنیم ، رویداد کلیک از windows ، شروع میکنه به پیمایش تا به Source که کنترل btnLevel_2 هست ، برسه . تا وقتی که به btnLevel_1 برسه ، به این نوع رویداد برخورد نمیکنه . اما وقتی به مقصد برسه ، به رویداد کلیک که همون btnLevel_2_PreviewMouseDown باشه برمیخوره و این رویداد را اجرا میکنه .

وقتی دکمه ی btnLevel_3 را کلیک میکنیم ، رویداد کلیک از windows ، شروع میکنه به پیمایش تا به Source که کنترل btnLevel_3 هست ، برسه . تا وقتی که به btnLevel_1 برسه ، به این نوع رویداد برخورد نمیکنه . اما وقتی به btnLevel_2 برسه ، به رویداد کلیک که همون btnLevel_2_PreviewMouseDown باشه برمیخوره و این رویداد را اجرا میکنه . بعد هم که به مقصد که همون btnLevel_3 باشه ، برسه ، دیگه چیزی برای اجرای رویداد کلیک دیگه ای ، پیدا نمیکنه . پس وقتی روی دکمه ی btnLevel_3 کلیک میکنیم ، همین یه بار اجرا میشه .

وقتی دکمه ی btnLevel_4 را کلیک میکنیم ، رویداد کلیک از windows ، شروع میکنه به پیمایش تا به Source که کنترل btnLevel_4 هست ، برسه . تا وقتی که به btnLevel_1 برسه ، به این نوع رویداد برخورد نمیکنه . اما وقتی به btnLevel_2 برسه ، به رویداد کلیک که همون btnLevel_2_PreviewMouseDown باشه برمیخوره و این رویداد را اجرا میکنه . بعد هم که به btnLevel_3 و بعد هم که به مقصد که همون btnLevel_4 باشه ، برسه ، دیگه چیزی برای اجرای رویداد کلیک دیگه ای ، پیدا نمیکنه . پس وقتی روی دکمه ی btnLevel_4 هم کلیک میکنیم ، همین یه بار اجرا میشه .

اینها درستن؟

و بنابراین ، کد زیر (که استراتژی تونل داره) :

کد:
    <Button Name="btnLevel_1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40">
        <Button Name="btnLevel_2" PreviewMouseDown="btnLevel_2_PreviewMouseDown"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkCyan">
            <Button Name="btnLevel_3" PreviewMouseDown="btnLevel_2_PreviewMouseDown"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkKhaki">
                <Button Name="btnLevel_4" Margin="40" Background="DarkOrange">

                </Button>
            </Button>
        </Button>
    </Button>

با کد زیر که استراتژی حباب داره :

کد:
    <Button Name="btnLevel_1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40">
        <Button Name="btnLevel_2" ButtonBase.Click="CommonClickHandler"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkCyan">
            <Button Name="btnLevel_3" ButtonBase.Click="CommonClickHandler"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkKhaki">
                <Button Name="btnLevel_4" Margin="40" Background="DarkOrange">

                </Button>
            </Button>
        </Button>
    </Button>

از لحاظ ترتیب اجرای رویداد (اینکه رویداد در کنترل btnLevel_2 زودتر اجرا بشه یا در کنترل btnLevel_3 زودتر اجرا بشه) ، مخالف هم هستند . درسته؟

من قبلا فکر میکردم مخالف هم بودن به این معناست که مثلا وقتی 4 سطح کنترل داریم و وقتی برای btnLevel_3 ، استراتژی حباب (برای کلیک) مشخص کردیم و در این استراتژی ، وقتی روی دکمه های btnLevel_3 یا دکمه ی btnLevel_4 که کلیک میکنیم (یعنی خود کنترل btnLevel_3 و والدهاش)، رویداد اجرا میشه ، پس در استراتژی تونل ، وقتی روی کنترل های Window یا کنترل btnLevel_1 و کنترل btnLevel_2 و کنترل btnLevel_3 که کلیک کنیم (یعنی خود کنترل btnLevel_3 و جدهای اون) ، رویداد اجرا میشه . که این چیزی که فکر میکردم ، غلط هست و انگار دلیل اشتباهم را متوجه شدم (اگه توضیحاتی که در بالا دادم ، درست باشن) .

خیلی ممنون استاد
 

the_king

مدیرکل انجمن
خیلی ممنون استاد
بله . منظورم از برعکس ، همینی هست که گفتید .
فکر کنم حالا یه کم متوجه شدم. الان در کد زیر (همون کد اول در پست 204) (پدر کنترل ، btnLevel_1 ، کنترل Window هست) :

کد:
    <Button Name="btnLevel_1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40">
        <Button Name="btnLevel_2" PreviewMouseDown="btnLevel_2_PreviewMouseDown"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkCyan">
            <Button Name="btnLevel_3"   HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkKhaki">
                <Button Name="btnLevel_4" Margin="40" Background="DarkOrange">

                </Button>
            </Button>
        </Button>
    </Button>

و در رویداد سی شارپ :

کد:
        private void btnLevel_2_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement senderElement = sender as FrameworkElement;
            FrameworkElement sourceElement = e.Source as FrameworkElement;
            Debug.WriteLine("sender fired :  {0}  ....  Source : {1}", new string[] { senderElement.Name, sourceElement.Name });
        }

الان کد بالا (که استراتژی تونل داره) ، این طور که در زیر میگم ، عمل کنه؟ :

وقتی دکمه ی btnLevel_1 را کلیک میکنیم ، رویداد کلیک از windows ، شروع میکنه به پیمایش تا به Source که کنترل btnLevel_1 هست ، برسه . اما چون در مسیر پیمایش اش (تا سطح btnLevel_1) ، به هیچ کنترلی که رویداد کلیک (ئه مربوط به ButtonBase) داشته باشن ، برخورد نمیکنه (منظورم اینه که به ست اش برنمیخوره) ، پس رویدادی را اجرا نمیکنه (این قضیه ، برای وقتی که روی کنترل Window هم کلیک میکنیم ، اتفاق میافته منتها پیمایشی انجام نمیگیره چون هم مبدا و هم مقصد ، خود window هست) .

وقتی دکمه ی btnLevel_2 را کلیک میکنیم ، رویداد کلیک از windows ، شروع میکنه به پیمایش تا به Source که کنترل btnLevel_2 هست ، برسه . تا وقتی که به btnLevel_1 برسه ، به این نوع رویداد برخورد نمیکنه . اما وقتی به مقصد برسه ، به رویداد کلیک که همون btnLevel_2_PreviewMouseDown باشه برمیخوره و این رویداد را اجرا میکنه .

وقتی دکمه ی btnLevel_3 را کلیک میکنیم ، رویداد کلیک از windows ، شروع میکنه به پیمایش تا به Source که کنترل btnLevel_3 هست ، برسه . تا وقتی که به btnLevel_1 برسه ، به این نوع رویداد برخورد نمیکنه . اما وقتی به btnLevel_2 برسه ، به رویداد کلیک که همون btnLevel_2_PreviewMouseDown باشه برمیخوره و این رویداد را اجرا میکنه . بعد هم که به مقصد که همون btnLevel_3 باشه ، برسه ، دیگه چیزی برای اجرای رویداد کلیک دیگه ای ، پیدا نمیکنه . پس وقتی روی دکمه ی btnLevel_3 کلیک میکنیم ، همین یه بار اجرا میشه .

وقتی دکمه ی btnLevel_4 را کلیک میکنیم ، رویداد کلیک از windows ، شروع میکنه به پیمایش تا به Source که کنترل btnLevel_4 هست ، برسه . تا وقتی که به btnLevel_1 برسه ، به این نوع رویداد برخورد نمیکنه . اما وقتی به btnLevel_2 برسه ، به رویداد کلیک که همون btnLevel_2_PreviewMouseDown باشه برمیخوره و این رویداد را اجرا میکنه . بعد هم که به btnLevel_3 و بعد هم که به مقصد که همون btnLevel_4 باشه ، برسه ، دیگه چیزی برای اجرای رویداد کلیک دیگه ای ، پیدا نمیکنه . پس وقتی روی دکمه ی btnLevel_4 هم کلیک میکنیم ، همین یه بار اجرا میشه .
بله.

و بنابراین ، کد زیر (که استراتژی تونل داره) :

کد:
    <Button Name="btnLevel_1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40">
        <Button Name="btnLevel_2" PreviewMouseDown="btnLevel_2_PreviewMouseDown"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkCyan">
            <Button Name="btnLevel_3" PreviewMouseDown="btnLevel_2_PreviewMouseDown"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkKhaki">
                <Button Name="btnLevel_4" Margin="40" Background="DarkOrange">

                </Button>
            </Button>
        </Button>
    </Button>

با کد زیر که استراتژی حباب داره :

کد:
    <Button Name="btnLevel_1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40">
        <Button Name="btnLevel_2" ButtonBase.Click="CommonClickHandler"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkCyan">
            <Button Name="btnLevel_3" ButtonBase.Click="CommonClickHandler"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="40" Background="DarkKhaki">
                <Button Name="btnLevel_4" Margin="40" Background="DarkOrange">

                </Button>
            </Button>
        </Button>
    </Button>

از لحاظ ترتیب اجرای رویداد (اینکه رویداد در کنترل btnLevel_2 زودتر اجرا بشه یا در کنترل btnLevel_3 زودتر اجرا بشه) ، مخالف هم هستند . درسته؟
بله.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f

خیلی ممنون استاد .
پس میتونیم نتیجه بگیریم که هیچ وقت در Routed Event ها (هر 3 استراتژی شون) ، رویدادِ مربوط به والد کنترل Source ، اجرا نمیشه . بلکه رویداد کنترل فرزندانشون اجرا میشه . و تفاوت استراتژی های مختلف Routed Event ، فقط توی ترتیب اجرای رویدادهای کنترلهاست پدر و فرزند (کلا سلسله مراتب ارث بری) هست . درسته؟

یعنی وقتی در کنترل btnLevel_2 ، رویدادی از نوع Routed Event را نوشتیم ، این طور نیست که رویداد کنترل btnLevel_1 (که والدش هست) اجرا بشه . بلکه رویداد کنترل btnLevel_2 و رویدادد کنترل فرزندانش (که کنترل btnLevel_3 و btnLevel_4 باشن) ، اجرا میشن و توی استراتژی های مختلف ، ترتیب اجرای رویداد این کنترل های پدر و فرزند فقط فرق میکنه .
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
استاد ، کلاس String که در فضای نام System هست رو میگه در اسمبلیِ System.Runtime.dll قرار داره .
پس چرا در کدهای xaml ، وقتی اسمبلیِ mscorlib را تعیین میکنیم ، به این کلاس String ، دسترسی داریم؟

کد:
xmlns:System="clr-namespace:System;assembly=mscorlib"
 

the_king

مدیرکل انجمن
خیلی ممنون استاد .
پس میتونیم نتیجه بگیریم که هیچ وقت در Routed Event ها (هر 3 استراتژی شون) ، رویدادِ مربوط به والد کنترل Source ، اجرا نمیشه . بلکه رویداد کنترل فرزندانشون اجرا میشه.
متوجه نمیشم چی میگید، اگر منظورتون از اجرا شدن رویداد فلان چیز بروز رویداد در اون چیز ئه، رویداد مربوط به والد کنترل Source در هر پیمایش ای که از اون المنت والد گذر کنه میتونه اجرا بشه، مستقل از اینکه جهت چیه و با چه ترتیبی به این المنت رسیده. Bubble و Tunnel هم که هر دوشون در پیمایش از المنت والد Source گذر می کنند، پس چطور رویداد والد کنترل Source هیچوقت اجرا نمیشه وقتی در پیمایش شامل بوده؟
اگر منبع بروز رخداد Source ئه، رخداد در Source اتفاق افتاده، حالا یا والدین اش از رویداد با خبر می شوند یا نمی شوند، اما به فرزندان Source که رخداد در اونها رخ نداده چه ربطی داره؟

و تفاوت استراتژی های مختلف Routed Event ، فقط توی ترتیب اجرای رویدادهای کنترلهاست پدر و فرزند (کلا سلسله مراتب ارث بری) هست . درسته؟
در حالت کلی خیر در مورد تفاوت Bubble و Tunnel حرف تون درست ئه، اما در استراتژی Direct که اصلا سلسله مراتبی در کار نیست.
و البته در رخداد های الحاقی هم که منبع بروز رخداد جدا از المنت های توصیف شده است و Source جزو المنت ها نیست که پدر و فرزند اش مطرح باشه، ترتیب وقوع بر اساس سلسله مراتب توصیفی در XAML ئه، نه رابطه پدر و فرزندی. رابطه پدر و فرزندی در جایی معنی داره که Source یک المنت و کنترل باشه، نه یک سرویس یا جزئی از سیستم مثل ماوس که منحصر بفرد ئه.

یعنی وقتی در کنترل btnLevel_2 ، رویدادی از نوع Routed Event را نوشتیم ، این طور نیست که رویداد کنترل btnLevel_1 (که والدش هست) اجرا بشه . بلکه رویداد کنترل btnLevel_2 و رویدادد کنترل فرزندانش (که کنترل btnLevel_3 و btnLevel_4 باشن) ، اجرا میشن و توی استراتژی های مختلف ، ترتیب اجرای رویداد این کنترل های پدر و فرزند فقط فرق میکنه .
حرفی که میزنید بی معنی ئه، شما در کنترل btnLevel_2 یک رویداد رو Route می کنید و به یک متد متصل کردید، تا اینجا قبول. اما اینکار شما چی بوده؟ صرفا هدایت یک رویداد به یک متد.
که با اینکار کدتون، برنامه تون، روتین تون، از بروز اون رخداد با خبر بشه. با اینکار شما متدی اجرا میشه؟ نه. هنوز رخدادی در جایی بروز پیدا نکرده که پیمایشی انجام بشه.
شما با اینکار نه یک زنجیره پیمایشی جدید ایجاد کرده اید، نه جلوی یک پیمایشی رو گرفته اید و نه بروز رخدادی رو فعال کرده اید و نه جلوی بروز رخدادی رو گرفته اید.
آیا قبل از اینکار شما رویداد در کنترل btnLevel_1 میتونسته اجرا بشه؟ بله. کار شما تاثیری در بروز رخداد داره؟ نه. هیج جا، نه در فرزندان و نه در والدین. تاثیری نداره.
بعد متصل کردنش روال پیمایش فرقی کرد؟ خیر. آیا قبل از اینکه اون رویداد رو در btnLevel_2 به متدی هدایت کنید در btnLevel_3 و btnLevel_4 اون رویداد می توانست بروز پیدا کنه؟ بله. ربطی به کار شما نداشت.
شما یک متد اضافه کرده اید که باهاش از بروز رخدادی با خبر بشوید و کاری انجام بدید، اگر حذفش کنید شما با خبر نمی شوید، نه اینکه اون رخداد دیگه رخ نده یا پیمایش انجام نده.
سلسله مراتب پیمایش کاری به این نداره که شما متدی متصل کرده اید یا نکرده اید. فقط اگر متد رو متصل کنید در کدش این اختیار رو دارید که پیمایش رو همونجا متوقف کنید تا ادامه پیدا نکنه.
btnLevel_1 میتونه از بروز رخداد کلیک در btnLevel_4 با خبر بشه، چه در btnLevel_2 متدی مشخص بکنید و چه نکنید.
اینکه در btnLevel_3 و btnLevel_4 رخدادی بروز پیدا کنه ربطی به این نداشت که در btnLevel_2 متدی متصل شده یا نشده.
 

the_king

مدیرکل انجمن
استاد ، کلاس String که در فضای نام System هست رو میگه در اسمبلیِ System.Runtime.dll قرار داره .
پس چرا در کدهای xaml ، وقتی اسمبلیِ mscorlib را تعیین میکنیم ، به این کلاس String ، دسترسی داریم؟

کد:
xmlns:System="clr-namespace:System;assembly=mscorlib"
شما در XAML به dll که رفرنس نمی دهید، دارید url مشخص می کنید، اون clr-namespace:System;assembly=mscorlib برای XAML شناخته شده است.
Built-in Types for Common XAML Language Primitives - WPF
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
خیلی ممنون استاد . :rose:
استاد ، در کد زیر :

کد:
                <Rectangle Name="myRectangle" MouseDown="myRectangle_MouseDown" Width="100" Height="100" Fill="DarkKhaki" Margin="425,316,1067,453">
                    <Rectangle.Triggers>
                        <Trigger SourceName="myRectangle" Property="Opacity" Value="1">
                            <Trigger.Setters>
                                <Setter TargetName="myRectangle" Property="Width" Value="10"/>
                            </Trigger.Setters>
                        </Trigger>
                    </Rectangle.Triggers>
                </Rectangle>

چرا ارور میده که :

کد:
Triggers collection members must be of type EventTrigger.

پروپرتیِ Rectangle.Triggers که از نوع TriggerCollection هست و TriggerCollection هم که از Collection<TriggerBase> ارث بری میکنه و متد GetEnumerator (که از کلاس Collection<TriggerBase> ارث بری میکنه) ، مقدار IEnumerator<T> را برمیگردونه و T هم در اینجا از نوع TriggerBase هست .
پس باید بشه در پروپرتیِ Rectangle.Triggers (که از نوع TriggerCollection هست) ، هر شی ای از فرزندانِ TriggerBase را بدیم (یعنی مثلا هم بتونیم نوع Trigger و یا نوع EventTrigger و یا نوع DataTrigger و کلا هر نوع از فرزندان TriggerBase را بدیم) . اما ارور میگه فقط میتونین مقدار EventTrigger را بدین . چرا؟

اگه در این پروپرتی Rectangle.Triggers (که از نوع TriggerCollection هست) و فقط محدود به دادن نوع EventTrigger هستیم ، خوب چرا خروجی اش را از نوع TriggerCollection گرفتن ؟ (که بصورت استاندارد این کلاس TriggerCollection طراحی شده تا هر نوع از TriggerBase را بدیم) . چرا این پروپرتی را از نوع List<EventTrigger > ها نگرفتن؟

---------------------------------------------------------------------------------------------

ویرایش :
طبق لینک زیر :

Triggers collection members must be of type EventTrigger

دلیل ارور را متوجه شدم . برای استفاده از نوعی بجز EventTrigger (مثلا استفاده از Trigger یا DataTrigger یا کلا هر نوع TriggerBase ای) ، باید ای از Style یا ControlTemplate یا DataTemplate استفاده کنیم .
بنابراین با کد زیر(و استفاده از Style) ، این ارور حل میشه :

کد:
                <Rectangle Name="myRectangle" MouseDown="myRectangle_MouseDown" Width="100" Height="100" Fill="DarkKhaki" Margin="425,316,1067,453">
                    <Rectangle.Style>
                        <Style TargetType="Rectangle">
                            <Style.Triggers>
                                <Trigger Property="Opacity" Value="0.5">
                                    <Setter Property="Width" Value="10"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Rectangle.Style>
                </Rectangle>

اما هنوز دلیل این کارشون را متوجه نشدم که برای چیه و چرا برای استفاده از انواع مختلف TriggerBase ، باید از Style و این چیزها استفاده بشه و مستقیما نمیشه استفاده کرد؟

اما باز یه مشکل دیگه . اینکه کد بالا چرا کار نمیکنه؟ :green:
یعنی وقتی مقدار Opacity ئه Rectangle را در زمان اجرا (با کد سی شارپ) برابر 0.5 میکنم ، Width ئه مربوط به Rectangle ام برابر 10 نمیشه و تغییری نمیکنه . چرا؟
و چی کار باید کنم تا کد بالا کار کنه؟ یعنی زمانی که Opacity ئه Rectangle به 0.5 تغییر کرد ، Width ئه Rectangle به 10 تغییر کنه ؟
 
آخرین ویرایش:

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
استاد ، بی زحمت پست بالا (3 خط آخر) را جواب میدین؟ :

" اما باز یه مشکل دیگه . اینکه کد بالا چرا کار نمیکنه؟ :green:
یعنی وقتی مقدار Opacity ئه Rectangle را در زمان اجرا (با کد سی شارپ) برابر 0.5 میکنم ، Width ئه مربوط به Rectangle ام برابر 10 نمیشه و تغییری نمیکنه . چرا؟
و چی کار باید کنم تا کد بالا کار کنه؟ یعنی زمانی که Opacity ئه Rectangle به 0.5 تغییر کرد ، Width ئه Rectangle به 10 تغییر کنه ؟ "

--------------------------------------------------------------------------------------

و همچنین استاد ، از مثالی که مایکروسافت در لینک زیر زد :

Animation Overview - WPF

یعنی مثال زیر :

کد:
            <Rectangle
                Name="MyRectangle"
                Width="100"
                Height="100"
                Fill="Blue">
                <Rectangle.Triggers>
                    <!-- Animates the rectangle's opacity. -->
                    <EventTrigger RoutedEvent="Rectangle.Loaded">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetName="MyRectangle"
                                    Storyboard.TargetProperty="Opacity"
                                    From="1.0" To="0.0" Duration="0:0:5"
                                    AutoReverse="True" RepeatBehavior="Forever" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Rectangle.Triggers>
            </Rectangle>

در خط :

کد:
<EventTrigger RoutedEvent="Rectangle.Loaded">

سینتکسِ پروپرتیِ RoutedEvent چجوری هه که مقدارش را Rectangle.Loaded داد؟
یا به عبارت دیگه ، چه کدی در xaml بنویسیم ، جوری که با مقداردهی پروپرتی های کلاس RoutedEvent (مثل پروپرتیِ HandlerType و OwnerType و ...) ، برابر با مقداردهی ای که در بالا انجام داد (مقدار Rectangle.Loaded) بشه؟
البته این پروپرتی های کلاس RoutedEvent ، همه get شدنی هستند و قابلیت set ندارند . کلا من ، منطق مقداردهیِ پروپرتی RoutedEvent در کد بالا را متوجه نشدم .

تشکر
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
استاد ، بی زحمت پست بالا (3 خط آخر) را جواب میدین؟ :

" اما باز یه مشکل دیگه . اینکه کد بالا چرا کار نمیکنه؟ :green:
یعنی وقتی مقدار Opacity ئه Rectangle را در زمان اجرا (با کد سی شارپ) برابر 0.5 میکنم ، Width ئه مربوط به Rectangle ام برابر 10 نمیشه و تغییری نمیکنه . چرا؟
و چی کار باید کنم تا کد بالا کار کنه؟ یعنی زمانی که Opacity ئه Rectangle به 0.5 تغییر کرد ، Width ئه Rectangle به 10 تغییر کنه ؟ "

سلامی مجدد استاد .
استاد ، جواب اینها را تا حدودی پیدا کردم (اما نه خیلی دقیق) .
مشکل این کد ، بخاطر این بود که (علاوه بر Margin) ، پروپرتی های Width و Height را هم در تگ Rectangle (به عنوان attributes) مشخص کردم . اما هنوز نمیدونم مشکل مشخص کردن اش ، چیه و چرا وقتی مشخص میکنیم ، کار نمیکنه؟
در واقع ، کد زیر ، کار میکنه و مشکلی نداره :

کد:
                <Rectangle Name="myRectangle" MouseDown="myRectangle_MouseDown" Fill="DarkKhaki" Margin="425,316,1067,453">
                    <Rectangle.Style>
                        <Style TargetType="Rectangle">
                            <Style.Triggers>
                                <Trigger Property="Opacity" Value="0.5">
                                    <Setter Property="Width" Value="50"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Rectangle.Style>
                </Rectangle>

و در کد سی شارپ :

کد:
        private void myRectangle_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.myRectangle.Opacity = 0.5;
        }

--------------------------------------------------------------------------------------
و همچنین استاد ، از مثالی که مایکروسافت در لینک زیر زد :

Animation Overview - WPF

یعنی مثال زیر :

کد:
            <Rectangle
                Name="MyRectangle"
                Width="100"
                Height="100"
                Fill="Blue">
                <Rectangle.Triggers>
                    <!-- Animates the rectangle's opacity. -->
                    <EventTrigger RoutedEvent="Rectangle.Loaded">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetName="MyRectangle"
                                    Storyboard.TargetProperty="Opacity"
                                    From="1.0" To="0.0" Duration="0:0:5"
                                    AutoReverse="True" RepeatBehavior="Forever" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Rectangle.Triggers>
            </Rectangle>

در خط :

کد:
<EventTrigger RoutedEvent="Rectangle.Loaded">

سینتکسِ پروپرتیِ RoutedEvent چجوری هه که مقدارش را Rectangle.Loaded داد؟
یا به عبارت دیگه ، چه کدی در xaml بنویسیم ، جوری که با مقداردهی پروپرتی های کلاس RoutedEvent (مثل پروپرتیِ HandlerType و OwnerType و ...) ، برابر با مقداردهی ای که در بالا انجام داد (مقدار Rectangle.Loaded) بشه؟
البته این پروپرتی های کلاس RoutedEvent ، همه get شدنی هستند و قابلیت set ندارند . کلا من ، منطق مقداردهیِ پروپرتی RoutedEvent در کد بالا را متوجه نشدم .

تشکر

طبق لینک زیر :

EventTrigger.RoutedEvent Property (System.Windows)

سینتکس پروپرتیِ EventTrigger.RoutedEvent ، بصورت ClassName.EventName هست (بنابراین برای متد Loaded ئه Rectangle ، میشه Rectangle.Loaded) .
در xaml ، از پروپرتی یا اعضایی که کلاس RoutedEvent را برمیگردونن ، موقع مقداردهیِ این پروپرتی ها ، فقط بصورت سینکسِ بالا (ClassName.EventName) میشه استفاده کرد (اگه اشتباه نکنم و درست متوجه شده باشم) .
به عبارت دیگه ، نمیشه برای مقداردهیِ پروپرتی های از نوع RoutedEvent ، از content property ها (پروپرتی هایی که در تگ مجزا ، بهشون محتوا و مقدار میدیم) استفاده کرد .

یه سئوال دیگه که داشتم و جواب گرفتم اینکه در کد زیر :

کد:
<Storyboard>
    <DoubleAnimation
        From="1.0" To="0.0" Duration="0:0:1"
        AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>

content property ئه Storyboard ، در کلاس والدش که TimelineGroup باشه مشخص شد و اون هم پروپرتیِ Children در این کلاس هست .
پس در کد بالا ، مقدار DoubleAnimation ، در پروپرتیِ Children ئه Storyboard ریخته میشه . این پروپرتیِ Children ، از نوع Timeline هست (اون DoubleAnimation هم از نوع Timeline هست) .

تشکر استاد .
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
استاد ، کلاس DoubleAnimation و Storyboard ، خیلی پروپرتی های مشترک با هم دارن (چون هر دو بصورت مشترک از کلاس TimeLine ارث بری میکنن) .
حالا میشه وقتی میخوایم پروپرتی های مشترک را تعیین کنیم ، در هر کدوم شی هایی که از این دو کلاس که ساختیم ، بصورت دلخواه در یکی از این دو کلاس ، مشخص کنیم؟
یعنی در کد زیر :

کد:
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetName="MyRectangle"
                                    Storyboard.TargetProperty="Opacity"
                                    From="1.0" To="0.0" Duration="0:0:5"
                                    AutoReverse="True" RepeatBehavior="Forever" />
                            </Storyboard>

پروپرتی های Duration و AutoReverse و RepeatBehavior و حتی attached property هایی مثل Storyboard.TargetName و Storyboard.TargetProperty را درون شیِ کلاس Storyboard مشخص کنیم؟
یعنی مثلا به عنوان نمونه ، کد بالا را میتونیم بصورت زیر هم بنویسیم؟ :

کد:
                <Storyboard Duration="0:0:5"
                                    AutoReverse="True" RepeatBehavior="Forever"
                                    Storyboard.TargetName="MyRectangle"
                                    Storyboard.TargetProperty="Opacity">
                    <DoubleAnimation From="1.0" To="0.0" />
                </Storyboard>
 

the_king

مدیرکل انجمن
پروپرتیِ Rectangle.Triggers که از نوع TriggerCollection هست و TriggerCollection هم که از Collection<TriggerBase> ارث بری میکنه و متد GetEnumerator (که از کلاس Collection<TriggerBase> ارث بری میکنه) ، مقدار IEnumerator<T> را برمیگردونه و T هم در اینجا از نوع TriggerBase هست .
پس باید بشه در پروپرتیِ Rectangle.Triggers (که از نوع TriggerCollection هست) ، هر شی ای از فرزندانِ TriggerBase را بدیم (یعنی مثلا هم بتونیم نوع Trigger و یا نوع EventTrigger و یا نوع DataTrigger و کلا هر نوع از فرزندان TriggerBase را بدیم) . اما ارور میگه فقط میتونین مقدار EventTrigger را بدین . چرا؟
یکسری خطاها عمومی هستند، مثلا اینکه نوع داده نامعتبری رو بکار ببرید که بخاطر عدم تطابق نوع داده خطا بده، اما این خطا برای عدم تطابق نوع داده بروز پیدا نکرده، برای اینه که شیء مورد نظر Trigger برای مشخصه ها رو پشتیبانی نمی کنه. نه اینکه مجموعه <Collection<TriggerBase نتونه Trigger رو بپذیره. در FrameworkElement ها مثل Rectangle مشخصه Triggers که TriggerCollection ئه از طریق کلاس EventTrigger تامین و مدیریت میشه، برای همین امکان اینکه از و DataTrigger و Trigger استفاده کنید نیست، پشتیانی اش نمی کنه.
کد:
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public TriggerCollection Triggers
        {
            get
            {
                TriggerCollection triggerCollection = EventTrigger.TriggerCollectionField.GetValue(this);
                if (triggerCollection == null)
                {
                    triggerCollection = new TriggerCollection(this);
                    EventTrigger.TriggerCollectionField.SetValue(this, triggerCollection);
                }
                return triggerCollection;
            }
        }

اگه در این پروپرتی Rectangle.Triggers (که از نوع TriggerCollection هست) و فقط محدود به دادن نوع EventTrigger هستیم ، خوب چرا خروجی اش را از نوع TriggerCollection گرفتن ؟ (که بصورت استاندارد این کلاس TriggerCollection طراحی شده تا هر نوع از TriggerBase را بدیم) . چرا این پروپرتی را از نوع List<EventTrigger > ها نگرفتن؟
List که ابدا مناسب اینجور موارد نیست، صرف از نظر نوع داده اش. List برای مجموعه ای مناسب ئه که فقط خودتون باهاش کار می کنید، نمی خواهید روی تغییرات و درج و حذف نظارتی انجام بدید و کار خاصی در عملیات انجام بشه. در List میشه یک عضو رو بارها تکراری درج کرد، حذف کرد، لیست رو خالی کرد و ...، بدون اینکه نظارتی روی این موارد باشه.
از <Collection<TriggerBase استفاده میشه چون ICollection و IList نظارت روی مجموعه رو ارائه می کنند. TriggerBase ئه چون در Style هم باید از همین TriggerCollection استفاده بشه و اونجا محدود به EventTrigger نیست :
کد:
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public TriggerCollection Triggers
        {
            get
            {
                base.VerifyAccess();
                if (this._visualTriggers == null)
                {
                    this._visualTriggers = new TriggerCollection();
                    if (this._sealed)
                    {
                        this._visualTriggers.Seal();
                    }
                }
                return this._visualTriggers;
            }
        }

اما هنوز دلیل این کارشون را متوجه نشدم که برای چیه و چرا برای استفاده از انواع مختلف TriggerBase ، باید از Style و این چیزها استفاده بشه و مستقیما نمیشه استفاده کرد؟
در Style برای یکسری المنت ها یک روال کلی مشخص می کنید که اگر فلان شد بهمان بشه، نه مشخص ئه که کی فلان میشه و نه مشخص ئه که چند تا مورد از فلان هست، یک روال ئه که وقوع اش معلوم نیست کی و کجا است.
اما در المنت ای که غیر از خودتون هیچکسی نمیتونه Opacity رو تغییر بده برای چه چیزی Trigger تعیین می کنید؟ خودتون در جای مشخصی صریحا Opacity رو تغییر می دهید، جایی که Opacity رو تغییر می دهید نا معلوم نیست که برایش Trigger تعریف کنید.

اما باز یه مشکل دیگه . اینکه کد بالا چرا کار نمیکنه؟ :green:
یعنی وقتی مقدار Opacity ئه Rectangle را در زمان اجرا (با کد سی شارپ) برابر 0.5 میکنم ، Width ئه مربوط به Rectangle ام برابر 10 نمیشه و تغییری نمیکنه . چرا؟
و چی کار باید کنم تا کد بالا کار کنه؟ یعنی زمانی که Opacity ئه Rectangle به 0.5 تغییر کرد ، Width ئه Rectangle به 10 تغییر کنه ؟
بررسی کنید ببینید کجا رو اشتباه کرده اید، ایرادی در اصل کار نیست و مشکلی نداره و کار میکنه. فقط اینکه برابری اعداد اعشاری رو بررسی می کنید کلا درست نیست، 0.5 با 0.5000001 و 0.4999999 یکسان نیستند.
 

the_king

مدیرکل انجمن
در خط :

کد:
<EventTrigger RoutedEvent="Rectangle.Loaded">

سینتکسِ پروپرتیِ RoutedEvent چجوری هه که مقدارش را Rectangle.Loaded داد؟
یا به عبارت دیگه ، چه کدی در xaml بنویسیم ، جوری که با مقداردهی پروپرتی های کلاس RoutedEvent (مثل پروپرتیِ HandlerType و OwnerType و ...) ، برابر با مقداردهی ای که در بالا انجام داد (مقدار Rectangle.Loaded) بشه؟
البته این پروپرتی های کلاس RoutedEvent ، همه get شدنی هستند و قابلیت set ندارند . کلا من ، منطق مقداردهیِ پروپرتی RoutedEvent در کد بالا را متوجه نشدم .

تشکر
داره میگه وقتی رخداد فلان بروز کرد فلان کار رو انجام بده، مثلا وقتی Rectangle.Loaded در اون المنت رخ داد فلان Storyboard اجرا بشه.
به HandlerType و OwnerType کاری نداره.
 

the_king

مدیرکل انجمن
سلامی مجدد استاد .
استاد ، جواب اینها را تا حدودی پیدا کردم (اما نه خیلی دقیق) .
مشکل این کد ، بخاطر این بود که (علاوه بر Margin) ، پروپرتی های Width و Height را هم در تگ Rectangle (به عنوان attributes) مشخص کردم . اما هنوز نمیدونم مشکل مشخص کردن اش ، چیه و چرا وقتی مشخص میکنیم ، کار نمیکنه؟
وقتی مشخصه ای رو صریحا مقدار دهی می کنید، دیگه امکان تغییر در Style ازش گرفته میشه.
مثل اینه که بگید Style فلان رو روی این المنت اعمال کن ولی صریحا طول المنت ئه 50 باشه. دیگه کاری نداره که Style میخواد طول رو تغییر بده.
 

the_king

مدیرکل انجمن
استاد ، کلاس DoubleAnimation و Storyboard ، خیلی پروپرتی های مشترک با هم دارن (چون هر دو بصورت مشترک از کلاس TimeLine ارث بری میکنن) .
حالا میشه وقتی میخوایم پروپرتی های مشترک را تعیین کنیم ، در هر کدوم شی هایی که از این دو کلاس که ساختیم ، بصورت دلخواه در یکی از این دو کلاس ، مشخص کنیم؟
یعنی در کد زیر :

کد:
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetName="MyRectangle"
                                    Storyboard.TargetProperty="Opacity"
                                    From="1.0" To="0.0" Duration="0:0:5"
                                    AutoReverse="True" RepeatBehavior="Forever" />
                            </Storyboard>

پروپرتی های Duration و AutoReverse و RepeatBehavior و حتی attached property هایی مثل Storyboard.TargetName و Storyboard.TargetProperty را درون شیِ کلاس Storyboard مشخص کنیم؟
یعنی مثلا به عنوان نمونه ، کد بالا را میتونیم بصورت زیر هم بنویسیم؟ :

کد:
                <Storyboard Duration="0:0:5"
                                    AutoReverse="True" RepeatBehavior="Forever"
                                    Storyboard.TargetName="MyRectangle"
                                    Storyboard.TargetProperty="Opacity">
                    <DoubleAnimation From="1.0" To="0.0" />
                </Storyboard>
در مورد Storyboard.TargetName و Storyboard.TargetProperty که بله.
در مورد مشخصه های مشترک هم در این مثال بله، چون شما فقط یک DoubleAnimation دارید که هم Duration اش کل زمان Duration اون Storyboard محسوب میشه و هم فرضا AutoReverse بودنش به معنی AutoReverse بودن Storyboard ئه. ولی کلا این مشخصه ها در Storyboard و DoubleAnimation ئه یکسان نیستند، در Storyboard یک وضعیت رو برای نتیجه کل اجزاء Storyboard در نظر می گیرید، نه اینکه تک تک اعضاء اش اون وضعیت یکسان رو داشته باشند، اگر زمان Storyboard ئه 5 ثانیه باشه تمامی انیمیشن های داخلش الزاما 5 ثانیه ای نیستند. ممکنه مدت زمان Storyboard کمتر یا بیشتر از مدت زمان DoubleAnimation ئه باشه، ممکنه یک DoubleAnimation ئه AutoReverse اش متفاوت از خود Storyboard ئه باشه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
وقتی مشخصه ای رو صریحا مقدار دهی می کنید، دیگه امکان تغییر در Style ازش گرفته میشه.
مثل اینه که بگید Style فلان رو روی این المنت اعمال کن ولی صریحا طول المنت ئه 50 باشه. دیگه کاری نداره که Style میخواد طول رو تغییر بده.

خیلی ممنون استاد از جواب هاتون .
آره . همینطوره .
چرا؟
بر عکس اش هم که کردم ، یعنی پروپرتی opacity را هم در attributes (در xaml) و هم در style اش مشخص کردم ، اون مقدارِ opacity ای که در style مشخص کرده بودم ، کار نکرد :

کد:
                <Rectangle Name="myRectangle" Opacity="0.95" MouseDown="myRectangle_MouseDown" Fill="DarkKhaki" Margin="425,316,1067,453">
                    <Rectangle.Style>
                        <Style TargetType="Rectangle">
                            <Style.Triggers>
                                <Trigger Property="Width" Value="50">
                                    <Setter Property="Opacity" Value="0.1"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Rectangle.Style>
                </Rectangle>

و در سی شارپ :

کد:
        private void myRectangle_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.myRectangle.Width = 50;
        }

ارتباط شون به هم چیه؟
خوب در attributes اش که مشخص کرده بودیم ، این تعیین مقدارش ، برای زمان طراحی استفاده بشه اما مقداری از اون پروپرتی ای که در style مشخص کردیم ، زمانی که اون trigger اتفاق افتاد ، همون مقدار بشه .
دقیقا تناقض این دو را متوجه نشدم که برای چی هستن . علی الظاهر نباید تناقضی با هم داشته باشن .


در مورد Storyboard.TargetName و Storyboard.TargetProperty که بله.
در مورد مشخصه های مشترک هم در این مثال بله، چون شما فقط یک DoubleAnimation دارید که هم Duration اش کل زمان Duration اون Storyboard محسوب میشه و هم فرضا AutoReverse بودنش به معنی AutoReverse بودن Storyboard ئه. ولی کلا این مشخصه ها در Storyboard و DoubleAnimation ئه یکسان نیستند، در Storyboard یک وضعیت رو برای نتیجه کل اجزاء Storyboard در نظر می گیرید، نه اینکه تک تک اعضاء اش اون وضعیت یکسان رو داشته باشند، اگر زمان Storyboard ئه 5 ثانیه باشه تمامی انیمیشن های داخلش الزاما 5 ثانیه ای نیستند. ممکنه مدت زمان Storyboard کمتر یا بیشتر از مدت زمان DoubleAnimation ئه باشه، ممکنه یک DoubleAnimation ئه AutoReverse اش متفاوت از خود Storyboard ئه باشه.

آها خیلی ممنون (همچنین تشکر بابت بقیه ی پست ها و جواب ها) .
پس اگه اشتباه نکنم ، شبیه انیمیشن سازی هست .
مثلا در افترافکت ، همون اول ، باید یه composation اصلی ای تعریف کنیم که این کامپوزیشن اصلی ، بستر اصلیِ تمام کلیپ ها یا فایل های صوتی یا هر لایه ی دیگه ای هست (لایه ها میتونن از جنس solid یا حتی کمپوزیشن های فرعی و ... باشن) که توی این کامپوزیشن اصلی میذاریم .

برای خودِ این کامپوزیشن اصلی هم باید ویژگی هایی که یکیش ، مدت زمان هست را براش تعیین کنیم (که تعیین کننده ی مدت زمان کل پروژه هست) . لایه هایی که در کمپوزیشن ایجاد میکنیم هم هر کدوم میتونن بصورت مجزا این ویژگی ها را هم داشته باشن (مثل مدت زمان و ...) اما اگه مدت زمان این لایه ها ، از مدت زمان کمپوزیشن اصلی ، بیشتر باشه ، زمان اضافی ، نادیده گرفته میشه و از اون لحظه به بعد (اگه از مدت زمانی که در کمپوزیشن اصلی تعیین کرده بودیم ، رد بشه) ، دیگه هیچ یک از اون لایه هایی که درون کمپوزیشن اصلی بودن ، نمایش داده نمیشه و در واقع ، با اتمام زمان کمپوزیشن ، همه چیز تمام میشه (هر چند لایه های درون کمپوزیشن ، زمان و حتی ویژگی های دیگه ی مربوط به خودشون را داشته باشن . در واقع لایه های درون کمپوزیشن اصلی ، تابع ویژگی های کمپوزیشن اصلی اند) .

آیا در اینجا میشه گفت که شی Storyboard ، شبیه کمپوزیشن اصلی عمل میکنه (یعنی بستر اصلی انیمیشن هست) و اشیاء های TimeLine ای که در پروپرتیِ Children ئه Storyboard قرار میدیم (مثل DoubleAnimation و StringAnimation و Int32Animation و ...) ، شبیه لایه هایی که درون کمپوزیشن اصلی در افترافکت قرار میدیم ، هستن؟

خیلی ممنون استاد .:rose:
 

جدیدترین ارسال ها

بالا