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

the_king

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

خروجی Uno platform در مرورگر اینترنتی اجرا میشه ؟
یعنی مثل نرم افزارهایی مثل wpf و اینها ، بصورت فایل exe اجرا نمیشه؟
پلتفرم uwp چطور؟
میشه با .net 5 و توسط پلتفرم uwp ، برنامه را در بقیه ی سیستم عامل ها اجرا کرد؟
جستجو کنید. جواب همه این سوالات در پاراگراف های ابتدایی مطالبی که در موردشون نوشته میشه هست.

اگه اشتباه نکنم ، قبلا گفته بودید که همه ی این کلاس ها (مثل control و ...) که باهاشون کار میکنیم ، به چهارچوب کاری (که در حال حاضر توسط .net framework استفاده میکنیم) ربط داره .
صد البته که ربط داره، شما هر کلاسی که بسازید یا بکار ببرید، چه MyClass و چه Control، با object هایی سر و کار دارید که تحت NET. ئه، مگر میشه کدی در NET. نوشته و اجرا بشه که به NET. ربطی نداشته باشه. اما NET. مگه در WPF و Windows Forms دو تا NET. متفاوت ئه که کنترل هاشون اینقدر با هم فرق داره؟

خوب چهارچوب .net 5 و .net core میتونه کلا متفاوت از .net framework باشه و شاید اصلا کنترل های بصری (مثل control و windows و form و ...) را اصلا ارائه نداده باشن . حالا من نمیدونم . چون با .net 5 و .net core آشنا نیستم .
این کنترل های بصری که میگید در NET. ارائه شده؟ شما در برنامه Windows Forms تون که تحت NET. ئه مثلا کنترل Grid دارید؟
در برنامه WPF تون که تحت NET. ئه DataGridView دارید؟ در برنامه کنسولی تون که تحت NET. کدوم یکی از اینها ارائه شده که میگید شاید در NET Core. و NET 5. ارائه نشده باشند؟
غیر از اینه که شما در NET. واسط کاربری رو در WPF یا Windows Forms طراحی کرده اید؟
غیر از اینه که DataGridView در Windows Forms ارائه شده و Grid در WPF؟ غیر از اینه که System.Windows.Controls.Control با System.Windows.Forms.Control دو کلاس کاملا متفاوت و ناسازگار هستند که یکی به WPF تعلق داره و دیگری به Windows Forms و هیچکدوم در برنامه های کنسولی NET. نیستند؟ اون برنامه کنسولی تحت NET. محسوب نمیشه که هیچکدوم از این کنترل ها داخلش ارائه نشده؟

اگر قبول دارید که Control و Window نه در معماری NET. بلکه در پلتفرم های Windows Forms و WPF با ساختار خاص خودشون ارائه شده اند و میدونید که WPF و Windows Forms هم در NET Framework. و هم در NET Core. پشتیبانی شده، دیگه ابهام در چیه؟
قبلا هم گفتم که مستندات هر کدوم از اون کنترل ها در سایت مایکروسافت سمت چپ تنظیم Version داره، در لیستش NET Framework. هست، NET 5. هست، NET Core. هم هست. در هر کدوم که نباشه بالای صفحه پیام میده که این کلاس در این نسخه موجود نیست.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
در حالت کلی من مزیت خاصی برای هیچ روشی قائل نیستم، علاوه بر اینکه به سلیقه برنامه نویس بستگی داره، هر کدوم ممکنه بر اساس شرایط پروژه شما گزینه مطلوبی باشه. یک مثال ساده اش اینه که اگر تعداد مشخصه هایی که null نبودنشون رو بررسی می کنید زیاد باشه، یک بار پیاده سازی IValueConverter کد کمتری لازم داره و شسته رفته تر میشه تا تعریف کردن تعداد زیادی مشخصه که همگی کار مشابهی رو انجام می دهند. نهایتا یک انتخاب شخصی ئه.
خیلی ممنون استاد .
استاد ، در این فایلی که ضمیمه میکنم (که کنترل ShapeTextButton هست (عملکرد همون کنترل TransparentControl در wpf را داره)) ، در این کنترل ، اینترفیس IValueConverter را هم پیاده سازی کردم . و بعد هم در کد زیر که میخوام ازش Template بسازم :

XML:
    <CustomControls:ShapeTextButton x:Key="Test1"/>


    <!--  تمپلیت برای ShapeTextButton  -->
    
    <ControlTemplate x:Key="ShapeTextButtonTemplate" TargetType="{x:Type CustomControls:ShapeTextButton}">
        <Grid Name="MainGrid" Background="Transparent">
            <Path Name="Shape"
                    Data="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Data}"
                    Margin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Margin}" 
                    HorizontalAlignment="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.HorizontalAlignment}"
                    VerticalAlignment="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.VerticalAlignment}"
                    Stroke="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Stroke}"
                    StrokeThickness="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeThickness}"
                    Fill="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Fill}"
                    StrokeStartLineCap="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeStartLineCap}"
                    StrokeEndLineCap="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeEndLineCap}"
                    StrokeLineJoin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeLineJoin}"
                    StrokeDashOffset="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeDashOffset}"
                    StrokeDashCap="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeDashCap}"
                    Stretch="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Stretch}"
                    Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Width}"
                    Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Height}"
                    MinWidth="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.MinWidth}"
                    MinHeight="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.MinHeight}"
                    LayoutTransform="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.LayoutTransform}"
                    RenderTransform="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.RenderTransform}"
                    RenderTransformOrigin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.RenderTransformOrigin}"
                    ContextMenu="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.ContextMenu}"
                    ToolTip="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.ToolTip}"
                    Cursor="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Cursor}"
                    ForceCursor="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.ForceCursor}"
                    FlowDirection="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.FlowDirection}"
                    Style="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Style}"
                    FocusVisualStyle="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.FocusVisualStyle}"/>

            <ContentPresenter x:Name="ContentPresenter" TextBlock.Foreground="{TemplateBinding Foreground}"/>
        </Grid>

        <ControlTemplate.Triggers>

            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true">
                <Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Data}"/>
            </DataTrigger>
            
        </ControlTemplate.Triggers>
    </ControlTemplate>

    <!--#endregion-->
و همچنین در پنجره ی اصلی که میخوام ازش استفاده کنم :

XML:
        <DockPanel Name="ToolBarDocPanel" LastChildFill="False" VerticalAlignment="Top" Height="40" Background="#FF0A0A0A" MouseLeftButtonDown="ToolBarDocPanel_MouseLeftButtonDown">

            <CustomControls:ShapeTextButton Template="{StaticResource ShapeTextButtonTemplate}" x:Name="CloseButton" DockPanel.Dock="Right" Margin="0,0,10,0" Width="24" Height="24">
                <CustomControls:ShapeTextButton.DefaultButtonShape>
                    <Path Margin="0" Stroke="White" StrokeThickness="1" Data="M1,1    23,23"/>
                </CustomControls:ShapeTextButton.DefaultButtonShape>
                <CustomControls:ShapeTextButton.MouseEnterButtonShape>
                    <Path Margin="0" Stroke="White" StrokeThickness="1" Data="M23,1    1,23"/>
                </CustomControls:ShapeTextButton.MouseEnterButtonShape>

                abc
            </CustomControls:ShapeTextButton>
            
        </DockPanel>
این converter کار نمیکنه . اشکالش کجاست؟
البته شرط کلیک کردن را در trigger در template نذاشتم (چون فعلا میخوام تست بگیرم فقط) .


اگر قبول دارید که Control و Window نه در معماری NET. بلکه در پلتفرم های Windows Forms و WPF با ساختار خاص خودشون ارائه شده اند و میدونید که WPF و Windows Forms هم در NET Framework. و هم در NET Core. پشتیبانی شده، دیگه ابهام در چیه؟
قبلا هم گفتم که مستندات هر کدوم از اون کنترل ها در سایت مایکروسافت سمت چپ تنظیم Version داره، در لیستش NET Framework. هست، NET 5. هست، NET Core. هم هست. در هر کدوم که نباشه بالای صفحه پیام میده که این کلاس در این نسخه موجود نیست.
 

پیوست ها

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
خیلی ممنون استاد .
استاد ، در این فایلی که ضمیمه میکنم (که کنترل ShapeTextButton هست (عملکرد همون کنترل TransparentControl در wpf را داره)) ، در این کنترل ، اینترفیس IValueConverter را هم پیاده سازی کردم . و بعد هم در کد زیر که میخوام ازش Template بسازم :
استاد ، تمپلیت زیر هم ارور میده و هم کلا مثل قبلی عمل نمیکنه (MultiDataTrigger گذاشتم) :

XML:
    <CustomControls:ShapeTextButton x:Key="Test1"/>


    <!--  تمپلیت برای ShapeTextButton  -->
    
    <ControlTemplate x:Key="ShapeTextButtonTemplate" TargetType="{x:Type CustomControls:ShapeTextButton}">
        <Grid Name="MainGrid" Background="Transparent">
            <Path Name="Shape"
                    Data="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Data}"
                    Margin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Margin}" 
                    HorizontalAlignment="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.HorizontalAlignment}"
                    VerticalAlignment="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.VerticalAlignment}"
                    Stroke="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Stroke}"
                    StrokeThickness="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeThickness}"
                    Fill="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Fill}"
                    StrokeStartLineCap="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeStartLineCap}"
                    StrokeEndLineCap="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeEndLineCap}"
                    StrokeLineJoin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeLineJoin}"
                    StrokeDashOffset="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeDashOffset}"
                    StrokeDashCap="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.StrokeDashCap}"
                    Stretch="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Stretch}"
                    Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Width}"
                    Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Height}"
                    MinWidth="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.MinWidth}"
                    MinHeight="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.MinHeight}"
                    LayoutTransform="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.LayoutTransform}"
                    RenderTransform="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.RenderTransform}"
                    RenderTransformOrigin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.RenderTransformOrigin}"
                    ContextMenu="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.ContextMenu}"
                    ToolTip="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.ToolTip}"
                    Cursor="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Cursor}"
                    ForceCursor="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.ForceCursor}"
                    FlowDirection="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.FlowDirection}"
                    Style="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Style}"
                    FocusVisualStyle="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.FocusVisualStyle}"/>

            <ContentPresenter x:Name="ContentPresenter" TextBlock.Foreground="{TemplateBinding Foreground}"/>
        </Grid>

        <ControlTemplate.Triggers>

            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Property="IsMouseOver" Value="true"/>
                    <Condition Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true"/>
                </MultiDataTrigger.Conditions>

                <MultiDataTrigger.Setters>
                    <Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape.Data}"/>
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>

        </ControlTemplate.Triggers>
    </ControlTemplate>

    <!--#endregion-->
ارور زیر را میده :

کد:
Must have non-null value for 'Binding'.
استاد ، راهی نداره که کدهای xaml را راحت تر trace کنیم؟
مثلا وقتی کدهای xaml بالا اجرا میشه ، راهی نداره که بفهمم کد درون متد Convert (که این متد را در کلاس ShapeTextButton نوشتم که در پست قبلی پیوست کرده بودم) ، اجرا میشه یا نه و کلا بتونم trace اش کنم؟

اگر قبول دارید که Control و Window نه در معماری NET. بلکه در پلتفرم های Windows Forms و WPF با ساختار خاص خودشون ارائه شده اند و میدونید که WPF و Windows Forms هم در NET Framework. و هم در NET Core. پشتیبانی شده، دیگه ابهام در چیه؟
قبلا هم گفتم که مستندات هر کدوم از اون کنترل ها در سایت مایکروسافت سمت چپ تنظیم Version داره، در لیستش NET Framework. هست، NET 5. هست، NET Core. هم هست. در هر کدوم که نباشه بالای صفحه پیام میده که این کلاس در این نسخه موجود نیست.
متوجه شدم استاد . ممنون

اما یه نکته ای که هست اینه که ما وقتی توی توضیحات کلاس System.Windows.Window (در wpf) میریم ، آخرش نوشته که در فلان نسخه از .net framework و فلان نسخه از .net core و همچنین در .net 5 وجود داره .

هر کدوم از این کلاس های کنترل و کمپوننت که بریم و مثلا نوشته نباشه که فلان نسخه از .net core ازش پشتیبانی میکنه ، به این معناست که اگه اون نسخه از .net core (که این کنترل مون ازش پشتیبانی نمیکنه) را در هر پلتفرمی انتخاب کنیم ، نمیتونیم صرفا از همون کنترل استفاده کنیم؟
تشکر استاد .
 

the_king

مدیرکل انجمن
خیلی ممنون استاد .
استاد ، در این فایلی که ضمیمه میکنم (که کنترل ShapeTextButton هست (عملکرد همون کنترل TransparentControl در wpf را داره)) ، در این کنترل ، اینترفیس IValueConverter را هم پیاده سازی کردم . و بعد هم در کد زیر که میخوام ازش Template بسازم :

این converter کار نمیکنه . اشکالش کجاست؟
البته شرط کلیک کردن را در trigger در template نذاشتم (چون فعلا میخوام تست بگیرم فقط) .
متوجه نمیشم باید چیکار بکنه؟ اونقدر کد رو بزرگ کرده اید که مناسب بررسی نیست. ثانیا شما یک کنترل ShapeTextButton کامل رو داخل Resources قرار می دهید فقط به این خاطر که از IValueConverter اش استفاده کنید؟ فقط یک کلاس کوچک لازم داشت.

اگر میخواهید کار با IValueConverter رو یاد بگیرید از یک مثال کوچک و ساده شروع کنید که رفع اشکالش راحت باشه، قبل از اینکه قاطی یک کلاس بزرگش کنید.
در ضمن Debug.WriteLine(value.ToString()) رو نوشته اید که وقتی value مقدار null داره قابل اجرا نیست، و در نظر بگیرید که شما برای DependencyProperty های reference-value که با PropertyMetadata مقدار پیشفرض غیر null در نظر نگرفته اید مقدار پیشفرض null رو دارید که مناسب ارجاع به مقدار نیست. وقتی میگه Must have non-null value for 'Binding' یعنی با مقدار null روبرو شده.


استاد ، راهی نداره که کدهای xaml را راحت تر trace کنیم؟
مثلا وقتی کدهای xaml بالا اجرا میشه ، راهی نداره که بفهمم کد درون متد Convert (که این متد را در کلاس ShapeTextButton نوشتم که در پست قبلی پیوست کرده بودم) ، اجرا میشه یا نه و کلا بتونم trace اش کنم؟
کد XAML اجرا نمیشه که بخواهید موقع اجرا trace اش کنید، XAML موقع کامپایل ترجمه میشه به BAML که باینری ئه و اونه که موقع اجرای پروژه اجرا میشه، نه کد XAML شما.

هر کدوم از این کلاس های کنترل و کمپوننت که بریم و مثلا نوشته نباشه که فلان نسخه از .net core ازش پشتیبانی میکنه ، به این معناست که اگه اون نسخه از .net core (که این کنترل مون ازش پشتیبانی نمیکنه) را در هر پلتفرمی انتخاب کنیم ، نمیتونیم صرفا از همون کنترل استفاده کنیم؟
یک صفحه مثال بزنید، لینک به یک صفحه بدهید که همچین شرایطی داره.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
متوجه نمیشم باید چیکار بکنه؟ اونقدر کد رو بزرگ کرده اید که مناسب بررسی نیست. ثانیا شما یک کنترل ShapeTextButton کامل رو داخل Resources قرار می دهید فقط به این خاطر که از IValueConverter اش استفاده کنید؟ فقط یک کلاس کوچک لازم داشت.

اگر میخواهید کار با IValueConverter رو یاد بگیرید از یک مثال کوچک و ساده شروع کنید که رفع اشکالش راحت باشه، قبل از اینکه قاطی یک کلاس بزرگش کنید.
در ضمن Debug.WriteLine(value.ToString()) رو نوشته اید که وقتی value مقدار null داره قابل اجرا نیست، و در نظر بگیرید که شما برای DependencyProperty های reference-value که با PropertyMetadata مقدار پیشفرض غیر null در نظر نگرفته اید مقدار پیشفرض null رو دارید که مناسب ارجاع به مقدار نیست. وقتی میگه Must have non-null value for 'Binding' یعنی با مقدار null روبرو شده.
خیلی ممنون استاد :rose:
بله . ShapeTextButton را توی Resources قرار دادم بخواطر استفاده ی IValueConverter ازش (البته میشه بصورت مستقیم هم استفاده کنم که یه کم دردسرش بیشتر هه) .

برای اینترفیس IValueConverter ، کلاسی مجزا ساختم :

C#:
    [ValueConversion(typeof(Path), typeof(bool))]
    public class ButtonShapePropertyValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Path buttonShape = value as Path;
            if (buttonShape == null)
                return false;
            else
                return true;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return  null;
        }
    }
}
اما اون ارورِ Must have non-null value for 'Binding' ، بخاطر این بود که انگار وقتی از MultiDataTrigger استفاده میکنیم ، انگار وقتی در یکی از Condition ها از Binding استفاده کردیم ، در بقیه ی Condition ها هم باید از Binding استفاده کنیم (ولی من برای یکی از Property استفاده کرده بودم و برای یکی دیگه از Binding استفاده کرده بودم) .

یعنی کد زیر ، دیگه ارور نمیده :

XML:
    <CustomControls:ButtonShapePropertyValueConverter x:Key="Test1"/>

    .
    .
    .

            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="true"/>
                    <Condition Binding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true"/>
                </MultiDataTrigger.Conditions>

                <MultiDataTrigger.Setters>
                    <Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape.Data}"/>
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
ارور نمیده اما کاری که من میخوام را هم انجام نمیده . یعنی با اونکه پروپرتیِ MouseEnterButtonShape (در کلاس ShapeTextButton) ، بهش مقدار دادم و null نیست (در کد زیر) اما شکلی که مشخص کرده بودم در تمپلیت در صورتی که موس روی المنت ام بیاد (همین کد بالا) ، رسم نمیشه .

XML:
            <CustomControls:ShapeTextButton  Template="{StaticResource ShapeTextButtonTemplate}" x:Name="CloseButton" DockPanel.Dock="Right" Margin="0,0,10,0" Width="24" Height="24">
                <CustomControls:ShapeTextButton.DefaultButtonShape>
                    <Path Margin="0" Stroke="White" StrokeThickness="1" Data="M1,1    23,23"/>
                </CustomControls:ShapeTextButton.DefaultButtonShape>
             
                <CustomControls:ShapeTextButton.MouseEnterButtonShape>
                    <Path Margin="0" Stroke="White" StrokeThickness="1" Data="M23,1    1,23"/>
                </CustomControls:ShapeTextButton.MouseEnterButtonShape>

                abc
            </CustomControls:ShapeTextButton>
چرا استاد کار نمیکنه؟


یک صفحه مثال بزنید، لینک به یک صفحه بدهید که همچین شرایطی داره.
مثلا کلاس Window :


یا کلاس Form :


یا خیلی (یا شایدم همه ی) کلاس های built-in در مایکروسافت (در دات نت) که آخر اون صفحه ، مشخص میکنن که اون کلاس یا استراکچر یا اینترفیس و ... ، در چه نسخه از دات نت (چه .net framework یا .net core) وجود دارن .

تشکر استاد . :rose:
 
آخرین ویرایش:

the_king

مدیرکل انجمن
سر از کاری که می کنید در نمیارم. در هر صورت ایراد از Converter نیست :
ValueConverterSample.rar

مثلا کلاس Window :


یا کلاس Form :


یا خیلی (یا شایدم همه ی) کلاس های built-in در مایکروسافت (در دات نت) که آخر اون صفحه ، مشخص میکنن که اون کلاس یا استراکچر یا اینترفیس و ... ، در چه نسخه از دات نت (چه .net framework یا .net core) وجود دارن .
وقتی میگید صرفا از اون کنترل نمی توانیم استفاده کنیم باید معنی اش باشه که از x و y که کنترل های دیگری هستند بتوانید استفاده کنید، خوب توضیحات اون x و y رو بخونید، مشخص شده که می توانید ازشون استفاده کنید یا نه.

فرضا System.Windows.Window که کنترلی از WPF ئه طبعا در NET Framework 2.0 نیست، برای همین از نسخه 3.0 به بعد موجود ئه.
اما نه به این خاطر که در NET Framework 2.0. واسط های کاربری WPF پنجره و کلاس Window ندارند، به این خاطر که WPF با NET Framework 3.0 معرفی شده، قبلش اصلا WPF ای در کار نبوده، هیچ جزء دیگری از WPF رو پیدا نمی کنید که در NET Framework 2.0. باشه.
همونطور که WPF با NET Core 3.0. معرفی شده، قبل از اون NET Core. اصلا WPF رو پشتیبانی نمی کرد، نه اینکه فقط کلاس Window این شرایط رو داشته باشه، کل مجموعه WPF این شرایط رو داره.
اما بعضی کلاس ها هستند که در نسخه های بعدی به پلتفرم اضافه شدن، مثلا DatePicker در WPF همراه NET Framework 4.0. معرفی شد، در NET Framework 3.0 هم WPF بوده ولی DatePicker نبوده.
 

پیوست ها

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سر از کاری که می کنید در نمیارم. در هر صورت ایراد از Converter نیست :
ValueConverterSample.rar
خیلی ممنون استاد . :rose:
مشکل از خط :

XML:
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true"/>
بود که در کدی که دادین ، متوجه شدم .
در واقع در RelativeSource ، اون Mode را باید روی Self میذاشتم نه روی TemplatedParent .

در واقع ، در اینجا که درون یه شیِ Condition ، اون Mode را روی Self تنظیم کردین ، باعث نمیشه که اون Self ، به شیِ Condition اشاره کنه . بلکه باعث میشه به شیِ ControlTemplate ای که از اون نوع هست (همون شیِ ShapeTextButton) اشاره کنه . درسته؟
اما اگه یه کنترلی یا چیز دیگه ای بجای اون Condition میبود (مثلا یه المنت ای مثل ComboBox میبود) ، باعث میشد به همون المنت (کمبوباکس) اشاره کنه (که در این صورت اگه به نوع ControlTemplate مون میخواستیم اشاره کنیم ، بجای Self باید از TemplatedParent استفاده میکردیم) . درست میگم؟

یه سئوال دیگه اینکه چرا همین IValueConverter را که در کلاس ButtonShapePropertyValueConverter پیاده سازی کرده بودم را بجاش در همون کلاس ShapeTextButton (که از کلاس Button ارث بری میکرد) ، پیاده سازی کنم (و در اون resource ای که کلیدش Test1 بود را از نوع ShapeTextButton که اون اینترفیس را پیاده سازی کرده بودم ، بدم) ، در این صورت چرا convertor ام باز کار نمیکنه؟
کلا نمیتونیم این اینترفیس را در همون کلاسِ کنترل مون پیاده سازی کنیم؟


وقتی میگید صرفا از اون کنترل نمی توانیم استفاده کنیم باید معنی اش باشه که از x و y که کنترل های دیگری هستند بتوانید استفاده کنید، خوب توضیحات اون x و y رو بخونید، مشخص شده که می توانید ازشون استفاده کنید یا نه.

فرضا System.Windows.Window که کنترلی از WPF ئه طبعا در NET Framework 2.0 نیست، برای همین از نسخه 3.0 به بعد موجود ئه.
اما نه به این خاطر که در NET Framework 2.0. واسط های کاربری WPF پنجره و کلاس Window ندارند، به این خاطر که WPF با NET Framework 3.0 معرفی شده، قبلش اصلا WPF ای در کار نبوده، هیچ جزء دیگری از WPF رو پیدا نمی کنید که در NET Framework 2.0. باشه.
همونطور که WPF با NET Core 3.0. معرفی شده، قبل از اون NET Core. اصلا WPF رو پشتیبانی نمی کرد، نه اینکه فقط کلاس Window این شرایط رو داشته باشه، کل مجموعه WPF این شرایط رو داره.
اما بعضی کلاس ها هستند که در نسخه های بعدی به پلتفرم اضافه شدن، مثلا DatePicker در WPF همراه NET Framework 4.0. معرفی شد، در NET Framework 3.0 هم WPF بوده ولی DatePicker نبوده.
بله . اینها را میدونستم که wpf از نسخه ی 3 در .net framework اضافه شد (البته قضیه ی DataPicker را نمیدونم) .
اما میدونم بعضی کلاس ها در نسخه های بعد اضافه شدن (مثل WindowChrome) .

الان پس جواب سئوالی که در پست قبل پرسیده بودم ، مثبت هست . درسته؟ :
هر کدوم از این کلاس های کنترل و کمپوننت که بریم و مثلا نوشته نباشه که فلان نسخه از .net core ازش پشتیبانی میکنه ، به این معناست که اگه اون نسخه از .net core (که این کنترل مون ازش پشتیبانی نمیکنه) را در هر پلتفرمی انتخاب کنیم ، نمیتونیم صرفا از همون کنترل استفاده کنیم؟

تشکر استاد .
 

the_king

مدیرکل انجمن
خیلی ممنون استاد . :rose:
مشکل از خط :

XML:
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true"/>
بود که در کدی که دادین ، متوجه شدم .
در واقع در RelativeSource ، اون Mode را باید روی Self میذاشتم نه روی TemplatedParent .

در واقع ، در اینجا که درون یه شیِ Condition ، اون Mode را روی Self تنظیم کردین ، باعث نمیشه که اون Self ، به شیِ Condition اشاره کنه . بلکه باعث میشه به شیِ ControlTemplate ای که از اون نوع هست (همون شیِ ShapeTextButton) اشاره کنه . درسته؟
Self کاری به این نداره که داخل کدوم تگ نوشته شده، با المنتی کار داره که Binding رویش اعمال میشه.

اما اگه یه کنترلی یا چیز دیگه ای بجای اون Condition میبود (مثلا یه المنت ای مثل ComboBox میبود) ، باعث میشد به همون المنت (کمبوباکس) اشاره کنه (که در این صورت اگه به نوع ControlTemplate مون میخواستیم اشاره کنیم ، بجای Self باید از TemplatedParent استفاده میکردیم) . درست میگم؟
تصوری از چیزی که میگید ندارم. توضیحات TemplatedParent رو بخونید، برای امتحان روی دکمه تون رخداد Click اضافه کنید و ببینید مقدارش چیه :
C#:
MessageBox.Show((((FrameworkElement)sender).TemplatedParent == null) ? "null" : "not null");
یه سئوال دیگه اینکه چرا همین IValueConverter را که در کلاس ButtonShapePropertyValueConverter پیاده سازی کرده بودم را بجاش در همون کلاس ShapeTextButton (که از کلاس Button ارث بری میکرد) ، پیاده سازی کنم (و در اون resource ای که کلیدش Test1 بود را از نوع ShapeTextButton که اون اینترفیس را پیاده سازی کرده بودم ، بدم) ، در این صورت چرا convertor ام باز کار نمیکنه؟
کلا نمیتونیم این اینترفیس را در همون کلاسِ کنترل مون پیاده سازی کنیم؟
حتما یک جایی اشتباه کرده اید وگرنه همچین محدودیتی وجود نداره، در همون ShapeTextButton هم می توانستید IValueConverter داشته باشید.
من صرفا از نظر منطقی نبودن کاری که کردید گفتم، چون قرار دادن یک کلاس کنترل در Resources بخاطر صرفا IValueConverter اش، هدر دادن منابع ئه، نه اینکه حتما لازم باشه کلاس جداگانه ای رو بهش اختصاص بدهید.

الان پس جواب سئوالی که در پست قبل پرسیده بودم ، مثبت هست . درسته؟ :
هر کدوم از این کلاس های کنترل و کمپوننت که بریم و مثلا نوشته نباشه که فلان نسخه از .net core ازش پشتیبانی میکنه ، به این معناست که اگه اون نسخه از .net core (که این کنترل مون ازش پشتیبانی نمیکنه) را در هر پلتفرمی انتخاب کنیم ، نمیتونیم صرفا از همون کنترل استفاده کنیم؟
خیر. استقراء در استدلال تون طبق معمول اشتباه ئه. صرفا نداره. مثال NET Framework 2.0. رو هم که زدم. شما به کسی می توانید بگویید اگر NET Framework 2.0. رو انتخاب کنی در WPF صرفا از کنترل ListBox نمی توانی استفاده کنی. صرفا از Button نمی توانی استفاده کنی. صرفا از Label و Menu و ... نمی توانی استفاده کنی؟ بپرسه از کدوم کنترل ها می توانم استفاده کنم میخواهید بگید هیچ کنترلی؟ قطعا خیال می کنه سر کار گذاشتیدش. صرفا از همون کنترل یعنی قاعده ای رو منحصر می کنید به کنترل فلان. صرفا از فلان کنترل ها یعنی قاعده ای رو منحصر کنید به فلان کنترل ها. در حالی که در اطلاعات اون صفحه قاعده منحصر به چیزی نشده. صرفا نداره، خیلی ساده اینه که "نمیتونیم از اون کنترل استفاده کنیم". در مورد سایر کنترل ها هیچ حکمی صادر نمیشه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
Self کاری به این نداره که داخل کدوم تگ نوشته شده، با المنتی کار داره که Binding رویش اعمال میشه.


تصوری از چیزی که میگید ندارم. توضیحات TemplatedParent رو بخونید، برای امتحان روی دکمه تون رخداد Click اضافه کنید و ببینید مقدارش چیه :
C#:
MessageBox.Show((((FrameworkElement)sender).TemplatedParent == null) ? "null" : "not null");
خیلی ممنون استاد :rose:
استاد ، من قاتی کردم .
الان شما توضیحاتی که برای Self در لینک زیر داده را ببینید :


در توضیحات Self گفته :

کد:
to bind a property of a given object to another property of the object itself
"برای بایند کردن یه پروپرتی از یه شی ، به پروپرتیِ دیگه از همون شی" .
(البته فکر کنم منظورش از شی ، همون element هست) .
منظور از element هم کنترل ها هستند دیگه . درسته؟

و مثالش را هم این گفته :

XML:
<Rectangle Fill="Red" Height="100" Stroke="Black"
 Width="{Binding RelativeSource={RelativeSource Self}, Path=Height}"/>
خوب ، من هم یه تصویری به همین صورت در ذهنم از عملکرد Self هست که وقتی برای پروپرتی ای (در مثال بالا ، پروپرتیِ Width) اون Self را میذاریم ، در واقع Self ، اشاره به شی (یا المنت) ای که اون پروپرتی ، متعلق به المنت هست (در اینجا المنت Rectangle که پروپرتیِ Width ، متعلق به المنت Rectangle هست) ، اشاره میکنه . در واقع Self ، المنت Rectangle را به عنوان Source Object تعیین میکنه .

اما مثال ما این طور بود :

XML:
<Condition Binding="{Binding RelativeSource={RelativeSource  Mode=Self}, Path=IsMouseOver}" Value="true"/>
اگه به همون روال در توضیحات بالا پیش بریم ، در این مثال ، Self ، مقدار Source Object مون را شیِ Condition در نظر میگیره دیگه . درست نیست؟
یا این طوره که چون Condition ، یک المنت (یا کنترل یا کلا شی بصری) نیست ، همونطور به تگ های پدر اش مراجعه میکنه تا اولین المنت اش را درنظر میگیره و چون در این مثال مون ، ControlTemplate ، اولین شی جد بصری اش بود ، واسه ی همین در مثال Self مون ، نوعی که در ControlTemplate (که همون نوع ShapeTextButton بود) را به عنوان Source Object تعیین کرد؟

کلا من متوجه نشدم چرا در این مثال ، وقتی Self را بکار بردیم ، Source Object را ShapeTextButton تعیین کرد؟
ما در مثال های قبلی (اگه یادتون باشه) ، وقتی که درون ControlTemplate ای میخواستیم بایندینگ ای انجام بدیم ، از Mode ئه TemplatedParent استفاده میکردیم که در این صورت ، Source Object مون را همون المنت ای که در ControlTemplate مشخص کرده بودیم ، مد نظر قرار میداد و اشاره میکرد .
من کلا گیج شدم .


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

همچنین استاد ، پروپرتیِ IsPressed ، حس میکنم که یه کم دیر عمل میکنه (به نظرم میاد که انگار حدود 200 میلی ثانیه تاخیر در اجرا داره) .
درسته؟
متد OnMouseLeftButtonDown هم انگار تاخیر داره اما اندکی بهتر از IsPressed هست .
تشکر استاد .
 

the_king

مدیرکل انجمن
الان شما توضیحاتی که برای Self در لینک زیر داده را ببینید :


در توضیحات Self گفته :

کد:
to bind a property of a given object to another property of the object itself
"برای بایند کردن یه پروپرتی از یه شی ، به پروپرتیِ دیگه از همون شی" .
(البته فکر کنم منظورش از شی ، همون element هست) .
منظور از element هم کنترل ها هستند دیگه . درسته؟
وقتی توضیحات Self در سایت مایکروسافت هست توضیحات یک سایت دیگه رو چرا بررسی کنیم؟
خیر. منظور از element المنت ئه، نه control. قبلا در این مورد توضیح داده بودم.

و مثالش را هم این گفته :

XML:
<Rectangle Fill="Red" Height="100" Stroke="Black"
Width="{Binding RelativeSource={RelativeSource Self}, Path=Height}"/>
خوب ، من هم یه تصویری به همین صورت در ذهنم از عملکرد Self هست که وقتی برای پروپرتی ای (در مثال بالا ، پروپرتیِ Width) اون Self را میذاریم ، در واقع Self ، اشاره به شی (یا المنت) ای که اون پروپرتی ، متعلق به المنت هست (در اینجا المنت Rectangle که پروپرتیِ Width ، متعلق به المنت Rectangle هست) ، اشاره میکنه . در واقع Self ، المنت Rectangle را به عنوان Source Object تعیین میکنه .

اما مثال ما این طور بود :

XML:
<Condition Binding="{Binding RelativeSource={RelativeSource  Mode=Self}, Path=IsMouseOver}" Value="true"/>
اگه به همون روال در توضیحات بالا پیش بریم ، در این مثال ، Self ، مقدار Source Object مون را شیِ Condition در نظر میگیره دیگه . درست نیست؟
شما که طبق همون روال پیش نرفتید، در مثال اول چی دارید؟ Width ای که از نوع double ئه و متعلق به Rectangle ئه و Height ای رو که از نوع double ئه رویش Bind می کنید.
حالا فرض کنید </"Condition Property="IsMouseOver" Value="true> رو داریم، این IsMouseOver باید مشخصه ای در Condition باشه؟ نه.
Condition رو Source Object ای برای اون IsMouseOver به حساب میاورید؟ طبعا نه.
یا به نظرتون Binding یک شیء از نوع boolean ئه که بشه مقدار IsMouseOver رو بهش Bind کرد؟ دو مثال شبیه هم نیستند.

یا این طوره که چون Condition ، یک المنت (یا کنترل یا کلا شی بصری) نیست ، همونطور به تگ های پدر اش مراجعه میکنه تا اولین المنت اش را درنظر میگیره و چون در این مثال مون ، ControlTemplate ، اولین شی جد بصری اش بود ، واسه ی همین در مثال Self مون ، نوعی که در ControlTemplate (که همون نوع ShapeTextButton بود) را به عنوان Source Object تعیین کرد؟
دارید برای خودتون قاعده اختراع می کنید، جایی نوشته که Self در تگ و والد تگ دنبال المنت میگرده؟ اگر جایی در مستنداتش ننوشته، پس دارید حدس می زنید، با حدس میشه حکم صادر کرد؟
اینکه مربوط به المنت نبودن Condition ئه که کاملا درست ئه چون Condition خودش رو به عنوان Source معرفی نمی کنه، اما اولا این وظیفه Condition نیست که برای {... Binding} کاری انجام بده که حالا جستجویی انجام بده یا نده و ثانیا Self سلسله مراتبی برای جستجو نداره و مثلا همچین کدی درست نیست و جواب نمیده :
XML:
        <Border BorderBrush="Red">
            <Rectangle Fill="{Binding Path=BorderBrush, RelativeSource={RelativeSource Self}}"/>
        </Border>
کلا من متوجه نشدم چرا در این مثال ، وقتی Self را بکار بردیم ، Source Object را ShapeTextButton تعیین کرد؟
من میدونم اشکال کار تون کجاست، عادت دارید که سریع از روی یکی دو مثال یک قاعده کلی تو ذهنتون بسازید که چون فلان مورد بهمان شد پس کلا همه موارد بهمان میشن. قبلا هم بارها گفتم که استقراء از جزء به کل از اساس کار اشتباهیه. از اونجایی که این قاعده ای نیست که از راهنمای اون مورد خونده باشید و صرفا قاعده نادرستی ساخته اید، بعدا به مثال هایی بر میخورید که با قاعده در ذهنتون مطابقت نداره.

مثلا الان قاعده رو بر این قرار داده اید که اگر جایی Self بود، Self یعنی شیء اون تگ ای است که داخلش نوشته شده. ممکنه در مثال هایی جواب درستی بده، اما این قاعده به هر حال اشتباه ئه و میتونه در مثال هایی نتیجه اش نادرست باشه. در حالی که اگر بخواهید ببینید Self چه قاعده ای داره یا باید راهنما شو بخونید یا رفتار کد برنامه نویسی اش رو تحلیل کنید. رفتار Self بر اساس کد RelativeSource تفسیر میشه، نه قاعده ای که شما تو ذهنتون میسازید.

ما در مثال های قبلی (اگه یادتون باشه) ، وقتی که درون ControlTemplate ای میخواستیم بایندینگ ای انجام بدیم ، از Mode ئه TemplatedParent استفاده میکردیم که در این صورت ، Source Object مون را همون المنت ای که در ControlTemplate مشخص کرده بودیم ، مد نظر قرار میداد و اشاره میکرد .
ذهنی تفسیر نکنید، یک مثال کوچک واقعی رو تحلیل کنید، اگر دیدید متوجه نشدید مطرحش کنید تا با هم تحلیلش کنیم.

همچنین استاد ، پروپرتیِ IsPressed ، حس میکنم که یه کم دیر عمل میکنه (به نظرم میاد که انگار حدود 200 میلی ثانیه تاخیر در اجرا داره) .
درسته؟
متد OnMouseLeftButtonDown هم انگار تاخیر داره اما اندکی بهتر از IsPressed هست .
بصورت کلی دیر عمل می کنه؟ همچین موردی ندیدم، اما اگر منظورتون RepeatButton باشه RepeatButton.Delay داره.
 

SajjadKhati

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


شما که طبق همون روال پیش نرفتید، در مثال اول چی دارید؟ Width ای که از نوع double ئه و متعلق به Rectangle ئه و Height ای رو که از نوع double ئه رویش Bind می کنید.
حالا فرض کنید </"Condition Property="IsMouseOver" Value="true> رو داریم، این IsMouseOver باید مشخصه ای در Condition باشه؟ نه.
Condition رو Source Object ای برای اون IsMouseOver به حساب میاورید؟ طبعا نه.

یا به نظرتون Binding یک شیء از نوع boolean ئه که بشه مقدار IsMouseOver رو بهش Bind کرد؟ دو مثال شبیه هم نیستند.


دارید برای خودتون قاعده اختراع می کنید، جایی نوشته که Self در تگ و والد تگ دنبال المنت میگرده؟ اگر جایی در مستنداتش ننوشته، پس دارید حدس می زنید، با حدس میشه حکم صادر کرد؟
اینکه مربوط به المنت نبودن Condition ئه که کاملا درست ئه چون Condition خودش رو به عنوان Source معرفی نمی کنه، اما اولا این وظیفه Condition نیست که برای {... Binding} کاری انجام بده که حالا جستجویی انجام بده یا نده و ثانیا Self سلسله مراتبی برای جستجو نداره و مثلا همچین کدی درست نیست و جواب نمیده :
XML:
        <Border BorderBrush="Red">
            <Rectangle Fill="{Binding Path=BorderBrush, RelativeSource={RelativeSource Self}}"/>
        </Border>
آها . خیلی ممنون استاد . :rose:
پس ، همونطور که در template مینویسیم :

XML:
<Condition Property="IsMouseOver" Value="true"/>
و منظور از پروپرتیِ IsMouseOver ، پروپرتی ای عضوِ کلاس Condition نیست ، بلکه پروپرتیِ عضوِ نوع المنت ControlTemplate هست ، به همین ترتیب ، وقتی Self را به عنوان binding مشخص میکنیم ، منظور ، عضو پروپرتیِ (همون کلاس که) Condition باشه ، نیست بلکه پروپرتی ای عضوِ نوع المنت ControlTemplate هست .
درسته؟

درباره ی سایت اصلی هم تقریبا همین توضیح را داده که اون سایت داده بود .

من میدونم اشکال کار تون کجاست، عادت دارید که سریع از روی یکی دو مثال یک قاعده کلی تو ذهنتون بسازید که چون فلان مورد بهمان شد پس کلا همه موارد بهمان میشن. قبلا هم بارها گفتم که استقراء از جزء به کل از اساس کار اشتباهیه. از اونجایی که این قاعده ای نیست که از راهنمای اون مورد خونده باشید و صرفا قاعده نادرستی ساخته اید، بعدا به مثال هایی بر میخورید که با قاعده در ذهنتون مطابقت نداره.

مثلا الان قاعده رو بر این قرار داده اید که اگر جایی Self بود، Self یعنی شیء اون تگ ای است که داخلش نوشته شده. ممکنه در مثال هایی جواب درستی بده، اما این قاعده به هر حال اشتباه ئه و میتونه در مثال هایی نتیجه اش نادرست باشه. در حالی که اگر بخواهید ببینید Self چه قاعده ای داره یا باید راهنما شو بخونید یا رفتار کد برنامه نویسی اش رو تحلیل کنید. رفتار Self بر اساس کد RelativeSource تفسیر میشه، نه قاعده ای که شما تو ذهنتون میسازید.
بر اساس نوشته ی متن اون سایت گفتم دیگه استاد .

ذهنی تفسیر نکنید، یک مثال کوچک واقعی رو تحلیل کنید، اگر دیدید متوجه نشدید مطرحش کنید تا با هم تحلیلش کنیم.
مثلا استاد در مثال زیر :

XML:
    <CustomControls:ShapeTextButton x:Key="Test1"/>


    <!--  تمپلیت برای ShapeTextButton  -->
    
    <ControlTemplate x:Key="ShapeTextButtonTemplate" TargetType="{x:Type CustomControls:ShapeTextButton}">
        <Grid Name="MainGrid" Background="Transparent">
            <Path Name="Shape"
                    Data="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Data}"
                    Margin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Margin}" 
            <ContentPresenter x:Name="ContentPresenter" TextBlock.Foreground="{TemplateBinding Foreground}"/>
        </Grid>

        <ControlTemplate.Triggers>

            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding RelativeSource={RelativeSource  Mode=Self}, Path=IsMouseLeftButtonDown}" Value="true"/>
                    <Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true"/>
                </MultiDataTrigger.Conditions>

                <MultiDataTrigger.Setters>
                    <Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape.Data}"/>
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>



        </ControlTemplate.Triggers>
    </ControlTemplate>
در خط :

XML:
<Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape.Data}"/>
وقتی میخوایم به پروپرتیِ MouseEnterButtonShape که در کنترل ShapeTextButton (و این کنترل هم در نوع ControlTemplate) مشخص شده ، اشاره کنیم ، از Mode ئه TemplatedParent استفاده میکنیم (از Self استفاده نمیکنیم) .

در خیلی جاهای قبلی هم همینطور بوده که وقتی به نوعی که در ControlTemplate میخواستیم اشاره کنیم ، از Mode ئه TemplatedParent استفاده میکردیم . نمیدونم چرا توی این مثال ، برای Condition ، از Self باید استفاده کرد و TemplatedParent جواب نمیده؟

و یه سئوال دیگه اینکه اگه در Condition ، برای اشاره به نوع ControlTemplate ، از Self استفاده کردیم ، در همین تگ Setter ، نمیتونیم برای اشاره به همون نوع ControlTemplate ، از Self استفاده کنیم و باید از TemplatedParent استفاده کنیم (اما در Condition ، برعکس هه)؟

بصورت کلی دیر عمل می کنه؟ همچین موردی ندیدم، اما اگر منظورتون RepeatButton باشه RepeatButton.Delay داره.
نه .
حالا نمیدونم کلا رسم دیر انجام میشه یا این رویداد دیرتر اجرا میشه و تاخیر داره .
ولی رسم ، زمانی که پروپرتیِ IsMouseOver ، مقدار true میگیره ، انگار نسبت به زمانی که موس روی کنترل میره ، تاخیر خاصی نداره و خوب اجرا و رسم میشه .
اما برای IsPressed یا MouseLeftButtonDown ، حس میکنم اندکی تاخیر در رسم نسبت به زمانی که کلیک میکنیم و رها میکنیم ، هست . شاید خطای دید ام باشه . شاید هم به همون قضیه ی دیرتر رسم شدن در wpf که قبلا گفتید و مثال زدید ربط داشته باشه . نمیدونم .

تشکر استاد :rose:
 

the_king

مدیرکل انجمن
آها . خیلی ممنون استاد . :rose:
پس ، همونطور که در template مینویسیم :

XML:
<Condition Property="IsMouseOver" Value="true"/>
و منظور از پروپرتیِ IsMouseOver ، پروپرتی ای عضوِ کلاس Condition نیست ، بلکه پروپرتیِ عضوِ نوع المنت ControlTemplate هست
بله.

، به همین ترتیب ، وقتی Self را به عنوان binding مشخص میکنیم ، منظور ، عضو پروپرتیِ (همون کلاس که) Condition باشه ، نیست بلکه پروپرتی ای عضوِ نوع المنت ControlTemplate هست .
درسته؟
بله.

مثلا استاد در مثال زیر :

XML:
    <CustomControls:ShapeTextButton x:Key="Test1"/>


    <!--  تمپلیت برای ShapeTextButton  -->
   
    <ControlTemplate x:Key="ShapeTextButtonTemplate" TargetType="{x:Type CustomControls:ShapeTextButton}">
        <Grid Name="MainGrid" Background="Transparent">
            <Path Name="Shape"
                    Data="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Data}"
                    Margin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DefaultButtonShape.Margin}"
            <ContentPresenter x:Name="ContentPresenter" TextBlock.Foreground="{TemplateBinding Foreground}"/>
        </Grid>

        <ControlTemplate.Triggers>

            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding RelativeSource={RelativeSource  Mode=Self}, Path=IsMouseLeftButtonDown}" Value="true"/>
                    <Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true"/>
                </MultiDataTrigger.Conditions>

                <MultiDataTrigger.Setters>
                    <Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape.Data}"/>
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>



        </ControlTemplate.Triggers>
    </ControlTemplate>
در خط :

XML:
<Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape.Data}"/>
وقتی میخوایم به پروپرتیِ MouseEnterButtonShape که در کنترل ShapeTextButton (و این کنترل هم در نوع ControlTemplate) مشخص شده ، اشاره کنیم ، از Mode ئه TemplatedParent استفاده میکنیم (از Self استفاده نمیکنیم) .

در خیلی جاهای قبلی هم همینطور بوده که وقتی به نوعی که در ControlTemplate میخواستیم اشاره کنیم ، از Mode ئه TemplatedParent استفاده میکردیم . نمیدونم چرا توی این مثال ، برای Condition ، از Self باید استفاده کرد و TemplatedParent جواب نمیده؟

و یه سئوال دیگه اینکه اگه در Condition ، برای اشاره به نوع ControlTemplate ، از Self استفاده کردیم ، در همین تگ Setter ، نمیتونیم برای اشاره به همون نوع ControlTemplate ، از Self استفاده کنیم و باید از TemplatedParent استفاده کنیم (اما در Condition ، برعکس هه)؟
خیلی ساده است، MouseEnterButtonShape در کجا تعریف شده؟ در شیء ShapeTextButton. در داخل ControlTemplate که تعریف نشده. در ControlTemplate مشخصه ای به نام MouseEnterButtonShape یا IsMouseOver هست؟ اگر نیست، منبع MouseEnterButtonShape یا IsMouseOver ئه نمیتونه ControlTemplate باشه.

Shape کجا تعریف شده؟ در شیء ShapeTextButton همچین چیزی هست؟ یعنی در رخداد Click اون شیء همچین "Shape" ای هست؟
C#:
            var shape = (Shape)((ShapeTextButton)sender).FindName("Shape");
            MessageBox.Show((shape == null) ? "null" : "not null");
غیر از اینه که Shape در ControlTemplate تعریف شده؟ جایی که Shape تعریف شده با جایی که MouseEnterButtonShape تعریف شده مشترک ئه؟ اگر مشترک نیست Self (خود) نمیتونه Shape و MouseEnterButtonShape رو جای مشترکی پیدا کنه.
 

SajjadKhati

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


بله.


خیلی ساده است، MouseEnterButtonShape در کجا تعریف شده؟ در شیء ShapeTextButton. در داخل ControlTemplate که تعریف نشده. در ControlTemplate مشخصه ای به نام MouseEnterButtonShape یا IsMouseOver هست؟ اگر نیست، منبع MouseEnterButtonShape یا IsMouseOver ئه نمیتونه ControlTemplate باشه.

Shape کجا تعریف شده؟ در شیء ShapeTextButton همچین چیزی هست؟ یعنی در رخداد Click اون شیء همچین "Shape" ای هست؟
C#:
            var shape = (Shape)((ShapeTextButton)sender).FindName("Shape");
            MessageBox.Show((shape == null) ? "null" : "not null");
غیر از اینه که Shape در ControlTemplate تعریف شده؟ جایی که Shape تعریف شده با جایی که MouseEnterButtonShape تعریف شده مشترک ئه؟ اگر مشترک نیست Self (خود) نمیتونه Shape و MouseEnterButtonShape رو جای مشترکی پیدا کنه.
خیلی ممنون استاد .
آها ، یعنی میگید که در کدِ :

XML:
<Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape.Data}"/>
چون پروپرتیِ MouseEnterButtonShape ، در کلاس ShapeTextButton هست ، پس در اینجا ، کلاس ShapeTextButton ، به عنوان Source Object مشخص میشه و چون مقدار TargetName ، همون Target Object مون هست و مقدارش هم نام شی Shape (که همون شی کلاس Path هست) ، پس در اینجا کلاس Path مون به عنوان Target Type مشخص میشه (که تا اینجا معلوم هست) و چون نوع Source Object مون با نوع Target Object مون فرق داره ، پس نمیشه از Mode ئه Self استفاده کرد (چون Mode ئه Self را فقط زمانی که هر دوی Source Object با نوع Target Object برابر باشه ، میشه استفاده کرد) .
درست متوجه شدم؟


در کد زیر هم :

XML:
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true"/>
اون Condition اصلا پروپرتیِ TargetName نداره تا Target Object مون را باهاش مشخص کنیم .
بنابراین وقتی Target Object مون را مشخص نکنیم ، کد بالا هم همونطور که قبلا گفتید ، مثل کد زیر :

XML:
<Condition Property="IsMouseOver" Value="true"/>
باعث میشه که Target Object مون را همون شیِ نوع ControlTemplate (که در اینجا شی کلاس ShapeTextButton بود) ، در نظر بگیره .
درسته؟

و چون پروپرتی MouseEnterButtonShape هم در کلاس ShapeTextButton مون هست و بنابراین در اینجا ، Source Object مون هم برابر شی ShapeTextButton میشه و بنابراین چون Source Object با Target Object مون در اینجا (در کد دو خط بالاتر) برابر میشه ، پس باید از Mode ئه Self استفاده کنیم .
درسته؟

تشکر استاد .
 

the_king

مدیرکل انجمن
خیلی ممنون استاد .
آها ، یعنی میگید که در کدِ :

XML:
<Setter TargetName="Shape" Property="Data" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseEnterButtonShape.Data}"/>
چون پروپرتیِ MouseEnterButtonShape ، در کلاس ShapeTextButton هست ، پس در اینجا ، کلاس ShapeTextButton ، به عنوان Source Object مشخص میشه و چون مقدار TargetName ، همون Target Object مون هست و مقدارش هم نام شی Shape (که همون شی کلاس Path هست) ، پس در اینجا کلاس Path مون به عنوان Target Type مشخص میشه (که تا اینجا معلوم هست) و چون نوع Source Object مون با نوع Target Object مون فرق داره ، پس نمیشه از Mode ئه Self استفاده کرد (چون Mode ئه Self را فقط زمانی که هر دوی Source Object با نوع Target Object برابر باشه ، میشه استفاده کرد) .
درست متوجه شدم؟
نه. اصلا کاری به مقایسه نوع Source Object یا Target Object نداریم، شما دارید نوع یک TemplateParent رو با نوع Shape مقایسه می کنید، اصلا فرض کنید نوع شون یکی هم بود، فرقی نمی کرد، اهمیتی نداشت، هیچ تاثیری در انتخاب Mode نداشت.
مقایسه نوع ایندو بی ربط ئه. Self یعنی خود، Shape عضوی از شیء x ئه، MouseEnterButtonShape هم عضوی از شیء y ئه، اگر x = y باشه و یک شیء باشند، Self ئه، وگرنه نیست، اصلا ربطی به این نداره که Shape از چه نوعی است یا y از چه نوعی.
Shape عضوی از اون ShapeTextButton هست؟ نیست. در حالی که MouseEnterButtonShape عضوی از ShapeTextButton ئه، پس نمیتونه Self باشه، x و y دو تا شیء متفاوت هستند، یکی شون Template کنترل ئه و دیگری خود کنترل.

در کد زیر هم :

XML:
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=MouseEnterButtonShape, Converter={StaticResource Test1}}" Value="true"/>
اون Condition اصلا پروپرتیِ TargetName نداره تا Target Object مون را باهاش مشخص کنیم .
بنابراین وقتی Target Object مون را مشخص نکنیم ، کد بالا هم همونطور که قبلا گفتید ، مثل کد زیر :

XML:
<Condition Property="IsMouseOver" Value="true"/>
باعث میشه که Target Object مون را همون شیِ نوع ControlTemplate (که در اینجا شی کلاس ShapeTextButton بود) ، در نظر بگیره .
درسته؟
وقتی TargetName ای وجود نداره و Condition هم کاری با Target ای نداره، چرا باید برایش مقداری در نظر بگیره؟
مثل اینه که بگید Line برای Background چه مقداری در نظر بگیره، وقتی Background برای Line معنی نداره مقداری هم برایش وجود نداره.
هیچ مقداری برای چیزی که در Condition معنی نداره در نظر نمی گیره. Self همون شیء ShapeTextButton ئه.

و چون پروپرتی MouseEnterButtonShape هم در کلاس ShapeTextButton مون هست و بنابراین در اینجا ، Source Object مون هم برابر شی ShapeTextButton میشه و بنابراین چون Source Object با Target Object مون در اینجا (در کد دو خط بالاتر) برابر میشه ، پس باید از Mode ئه Self استفاده کنیم .
درسته؟
خیر. Target Object در Condition مفهومی نداره. چون Source Object ئه همون شیء ShapeTextButton ای است که برایش Condition و MultiTrigger و Template می نویسید Self ئه. وقتی Self نیست که MouseEnterButtonShape ئه عضوی از هر شیء ای بجز اون شیء ShapeTextButton باشه که برایش Condition می نویسید، حتی اگر نوع اش ShapeTextButton باشه ولی شی دیگری باشه Self نیست.
 

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

بالا