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

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
این شیء ای که از نوع <Enumerable<UIElement بر میگردونه، چی به foreach تحویل میده؟ طبق توضیحات foreach با GetEnumerator اش یک IEnumerator تحویل میده. حالا این IEnumerator منبع اش کدوم مجموعه است؟ خودش مستقلا یک مجموعه داخلش داره و اعضاء خودش رو بر میگردونه؟ یا <Enumerable<UIElement ئه یک مجموعه کپی از روی NewControls برای خودش ایجاد کرده و اعضاء اون رو بر می گردونه؟ یا مستقیما اعضاء NewControls رو بر میگردونه؟
امتحان کنید و ببینید :
C#:
            var userControl = new UserControl();
            var newControls = new UIElementCollection(userControl, userControl);
            var enumerable = newControls.Cast<UIElement>();
            var enumerator = enumerable.GetEnumerator();
            var fieldInfo = enumerator.GetType().GetField("source", BindingFlags.NonPublic | BindingFlags.Instance);
            var source = fieldInfo?.GetValue(enumerator);
            if (source == newControls)
            {
                MessageBox.Show("yes");
            }


اینکه نوع دو شیء متفاوت از هم باشند به رفتار Enumerable هاشون ربطی نداره.
C#:
        public class A : IEnumerable<int>
        {
            public static readonly List<int> Source = new List<int>(new[] { 1, 2, 3 });

            public IEnumerator<int> GetEnumerator()
            {
                return Source.GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }

        public class B : IEnumerable<int>
        {
            public IEnumerator<int> GetEnumerator()
            {
                return A.Source.GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
C#:
            var b = new B();
            foreach (var i in b)
            {
                A.Source.Sort();
            }

سلامی مجدد
خیلی ممنون استاد .
یه چیزهایی متوجه شدم .
اما ان شاء ا... بعدا قضیه ی Enumerator و Enumerable را باید از اول مرور کنم تا کامل متوجه شم .
تشکر استاد .

اولا شما دارید یک Model ای رو توصیف می کنید که واسط پیچیده ای داره، طراحی بدی داره. ساختار داده داخلش به خودش مربوط ئه، ممکنه هزار تا کلاس داخل خودش استفاده کنه که private باشند، ایرادی نداره، اما واسطی که به محیط خارج از خودش ارائه میکنه نباید دردسر ساز باشه، نباید زیادی پیچیده باشه. اگر Model طراحی خوبی داشته باشه نباید برای تعامل با ViewModel بار زیادی روی دوش ViewModel بندازه.

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

این Model طراحی بدی داره.

من هم برای پروژه هام یه کلاس پایه تعریف میکنم . مثلا کلاس InteropWithUI و اعضایی را تعریف میکنم که به درد کل پروژه های دیگه بخوره و بشه از این کلاس در پروژه های دیگه ستفاده کرد و همچنین یه کلاسِ فرزند این کلاس تعریف میکنم بنام PoshtibangirToloUI که از InteropWithUI ارث بری میکنه و شامل اعضایی هست که فقط در پروژه ی PoshtibangirTolo ام کاربرد داره .

این برای قضیه ی View (در واقع بخشی از View) بود .
برای Model هم یه همچین کاری هم میکنم که یه کلاس پایه داشته باشه تا بعدا در هر پروژه ای بشه ازش استفاده کرد . یه کلاسی که اولِ نامش هم با PoshtibangirTolo شروع میشه رو میذارم که برای همین قضیه ی انتقال اطلاعات به ViewModel مخصوص پروژه ی PoshtibangirTolo و کلا اعضایی که فقط در این پروژه بکار میره ، در این کلاس باشه .
همچین چیزی ، خوبه؟

==========================

- بعد اینکه ، فرضا یه پروپرتی ای از نوع کلاسِ Student (از نوع Model) را درون کلاسِ ViewModel مون تعریف کردیم .
مقداردهی این پروپرتی (که درون ViewModel تعریف کردیم) ، فقط باید از درون خودِ کلاسِ ViewModel انجام بگیره و مقدار اولیه اش هم شیِ جدیدی از Student باید بهش بدیم دیگه . درسته؟

یعنی نمیتونیم در پارامتر متدی (یا در پارامتر متد سازنده) ، مقدار پروپرتیِ Student را از View بگیریم . چون در MVVM ، در یم View ، نمیتونیم از Model مون شی ای (حداقل ، بصورت صریح) ایجاد کنیم . درسته؟
پس مقداردهی اولیه ی اون پروپرتیِ نوع Student ، فقط و فقط باید در کلاسِ ViewModel انجام بشه (نه اینکه در کلاس های View انجام بشه و به ViewModel ، پاس داده بشه) و چون نمیتونه مقدارش را از View دریافت کنه ، پس برای مقداردهی اولیه ، حتما باید شیِ جدیدی از این پروپرتی ، در ViewModel ایجاد بشه .
همونطور که در اون پروژه بود .
درست میگم؟


- اگه این طور باشه ، پس با هر بار ساختنِ شی جدید از ViewModel ، در واقع با شیِ جدیدی از Model مون کار میکنیم (چون معمولا مقداردهی اولیه ی پروپرتی های ViewModel ، در متد سازنده انجام میشه) .
پس ، هر وقت که در کلاس های View بخوایم فقط با یک شی از Model (توسط ViewModel) کار کنیم ، پس باید با یک شی از ViewModel کار کنیم . درست میگم؟

معمولا در کلاس های مختلف View (مثل کلاس های Window ها و UserControl ها و Page ها و ...) که همه شون ، یک ظاهرِ نرم افزار را تشکیل میدن ، لازم داریم که با یک شی از Model (بصورت غیر مستقیم) کار کنیم . پس تا زمانی که این طور باشه ، پس با یک شی از ViewModel در View مون باید کار کنیم . درست میگم؟

و اگه این طور باشه ، لازمه که بین کلاس های View مون (مثل کلاس های Window ها و UserControl ها و Page ها و ...) ، اول فرضا در کلاس MainWindow ، شی ای از ViewModel که ساختیم ، به بقیه ی کلاس ها ، فقط همون شی (از ViewModel) را پاس بدیم . تا به این صورت ، فقط با یک شی از ViewModel کار کنیم .
یعنی در هر کلاسی از View مون (مثل کلاس های Window ها و UserControl ها و Page ها و ...) ، یک شیِ جدیدی از ViewModel نسازیم . وگرنه با اون کار ، ViewModel ، شیِ جدیدی از Model مون میسازه (البته تا مادامی که به شیِ جدیدی از Model در View ، توسط ViewModel نیازی نداشته باشیم) .
درست میگم؟



در مطالب قبلی صحبتش شده. وظیفه هر جزء رو در حد مسئولیت خودش در نظر بگیرید، یعنی فرضا اگر Model قراره موقع تغییر اطلاع بده، مسئولیتش در همین حد ئه که امکانی به ViewModel یا کلا هر موجودیت خارج از خودش بده که اگر خواست، بتونه از تغییر مطلع بشه، فقط همین. اینکه ViewModel به تغییر اهمیت میده یا نمیده، به View خبر میده یا نمیده یا چطور خبر میده به Model ربطی نداره.

همچین Model ای با INotifyPropertyChanged که میگه مقدار فلان مشخصه تغییر کرد، یا یک event خاص خودش میتونه به ViewModel تغییرات رو اطلاع بده. حالا اینکه ViewModel ای که ممکنه شخص دیگری نویسنده اش باشه به این تغییرات اهمیت میده یا نه یا در مقابل این تغییرات با View چه تعاملی بکنه دیگه به Model ربطی نداره.

بله .
حواسم تازه به INotifyPropertyChanged جمع شد .
با وجود INotifyPropertyChanged ، نیازی به اینکه پروپرتی ای از نوع View در ViewModel تعریف کنیم ، نداریم .
فقط کافی هه که رویداد PropertyChanged را در ViewModel متصل کنیم .

اون WinForm ای که میگید اسم پلتفرم داخل NET. ئه، طبعا وقتی خود NET Framework. سال 2002 ارائه شده، WinForm هم نمیتونسته زودتر از 2002 باشه، با خود NET. معرفی شده. اما قدمت Windows Forms که ربطی به NET. نداره، قبل از اینکه NET. ارائه بشه برنامه های تحت Windows Forms و زبان های برنامه نویسی ویژوال با پلتفرم Windows Forms بودند. یعنی از همون موقع که ویندوز این واسط کاربری رو داشته زبان های برنامه نویسی ویژوال اون دوران هم همچین پلتفرمی داشته اند.

احتمالا در مورد Windows Forms صحبت می کردم، Windows Forms یک پلتفرم خاص NET. نیست، فرضا در Visual Basic 5 (سال 1997) هم پلتفرم همون Windows Forms ئه.

بله.

WinForm و Windows Forms مگه فرق دارن؟!
در ویکی پدیا که هر دو را یکی زده .
من سندی از تاریخچه ی مجزایی از هر کدوم پیدا نکردم .
فقط توی ویکی پدیا ، هر دو را یکی میدونه و نسخه ی اولیه را وابسته به .net framework و انتشار اولیه شون را هم 2002 زده .

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

the_king

مدیرکل انجمن
من هم برای پروژه هام یه کلاس پایه تعریف میکنم . مثلا کلاس InteropWithUI و اعضایی را تعریف میکنم که به درد کل پروژه های دیگه بخوره و بشه از این کلاس در پروژه های دیگه ستفاده کرد و همچنین یه کلاسِ فرزند این کلاس تعریف میکنم بنام PoshtibangirToloUI که از InteropWithUI ارث بری میکنه و شامل اعضایی هست که فقط در پروژه ی PoshtibangirTolo ام کاربرد داره .

این برای قضیه ی View (در واقع بخشی از View) بود .
برای Model هم یه همچین کاری هم میکنم که یه کلاس پایه داشته باشه تا بعدا در هر پروژه ای بشه ازش استفاده کرد . یه کلاسی که اولِ نامش هم با PoshtibangirTolo شروع میشه رو میذارم که برای همین قضیه ی انتقال اطلاعات به ViewModel مخصوص پروژه ی PoshtibangirTolo و کلا اعضایی که فقط در این پروژه بکار میره ، در این کلاس باشه .
همچین چیزی ، خوبه؟
من خوب برای شما رو نمی تونم مشخص کنم. خوب و بد اش رو شخص دیگری نمی تونه برای شما مشخص کنه، خوب و بد شخصی ئه. مثلا من اگر n پروژه مختلف رو شروع کنم n شروع متفاوت دارند که حاصل ارزیابی و مدل سازی خاص اون پروژه است، کلاس پایه و کلاس خاص پروژه از پیش مشخص شده ای ندارم. این مساله کاملا شخصی ئه.
- بعد اینکه ، فرضا یه پروپرتی ای از نوع کلاسِ Student (از نوع Model) را درون کلاسِ ViewModel مون تعریف کردیم .
مقداردهی این پروپرتی (که درون ViewModel تعریف کردیم) ، فقط باید از درون خودِ کلاسِ ViewModel انجام بگیره و مقدار اولیه اش هم شیِ جدیدی از Student باید بهش بدیم دیگه . درسته؟
نه الزاما، نمیدونم این باید ها رو بر اساس چه محدودیت هایی تعیین می کنید.
شما یک مشخصه دارید که وابسته به Model و ViewModel ئه، چون نوع اش در Model مشخص شده و تعریف اش در ViewModel ئه. بر اساس معماری MVVM چه محدودیت هایی حاصل این وابستگی ئه؟ که فقط در Model یا ViewModel میتونه مورد دسترسی مستقیم قرار بگیره و نباید در View صریحا کاری باهاش داشت که وابستگی به Model ایجاد کنه. MVVM برای مقدار دهی یا تعیین مقدار اولیه محدودیت دیگری ایجاد می کنه؟ فرضا اگر در Model بهش مقداری داده بشه چه چیزی نقض میشه که نباید انجام بشه؟
یعنی نمیتونیم در پارامتر متدی (یا در پارامتر متد سازنده) ، مقدار پروپرتیِ Student را از View بگیریم .
قبلا در موردش صحبت کردیم، اصلا نمی توانیم یا می توانیم مطرح نیست، به صورت هایی می توانیم و به صورت هایی نمی توانیم. مساله گرفتنش نیست، مساله وابستگی ئه.
شما به قاعده ای که نباید نقض بشه باید توجه کنید، نه لیست هزاران کاری که میشه کرد یا نمیشه کرد.
فرض کنید که در ViewModel یک مشخصه داریم از نوع object که یک Student بر میگردونه و در View مقدار این مشخصه رو به عنوان یک object بگیرید و با ToString یا با GetType().GetField یا هر روش غیر مستقیم دیگری از مقدارش استفاده کنید، بدون اینکه هیچ کاری با نوع شیء Student داشته باشید، مشابه شیوه ای که Binding ها می کنند. کاری که قبلا هم در کد ها کردیم. اگر همچین کاری بکنید View تون وابستگی به Student نداره، چون اولا بدون توجه به Model و اون Student کامپایل میشه و ثانیا هر نوع ساختار سازگاری که اون فیلد با نام فلان رو داره میتونه جایگزین Student بشه، یعنی کاری با Student نداره، در object ئه دنبال فیلد فلان میگرده، شبیه Binding.
"نمیتونیم در پارامتر متدی (یا در پارامتر متد سازنده) ، مقدار پروپرتیِ Student را از View بگیریم ." ممکنه در هزاران کد درست باشه و در هزاران کد دیگه نادرست. چی درستی اش رو تعیین می کنه؟ وابستگی. اینکه View به Model وابسته شد یا نشد. باید به اون قاعده عدم وابستگی توجه کنید، به اصول معماری MVVM توجه کنید، نه اینکه کدوم کد رو بنویسیم نقض میشه یا نمیشه.

چون در MVVM ، در یم View ، نمیتونیم از Model مون شی ای (حداقل ، بصورت صریح) ایجاد کنیم . درسته؟
قاعده ای که برای حالت صریح مشخص می کنید درسته، اما نتیجه ای که قبلا ازش گرفتید فراتر از محدودیت این قاعده است.
پس مقداردهی اولیه ی اون پروپرتیِ نوع Student ، فقط و فقط باید در کلاسِ ViewModel انجام بشه (نه اینکه در کلاس های View انجام بشه و به ViewModel ، پاس داده بشه) و چون نمیتونه مقدارش را از View دریافت کنه ، پس برای مقداردهی اولیه ، حتما باید شیِ جدیدی از این پروپرتی ، در ViewModel ایجاد بشه .
اینقدر باید و نباید و حتما و قطعا نسازید، از یک قاعده ساده هزاران حالت در میاد، اینطوری نمی توانید از قاعده لیست باید و نباید بسازید. قاعده رو ول می کنید می چسبید به یکسری باید و نبایدی که میتونه در هزاران کد درست باشه در هزاران کد دیگه نادرست.
همونطور که در اون پروژه بود .
درست میگم؟
بارها در مورد استقراء صحبت کردم و توضیح دادم، البته بی فایده. هر چقدر توضیح دادم که اشتباه ئه توجهی نکردید و دائم تکرارش می کنید. از مثال های x و y و z نتیجه می گیرید که قاعده فلانی هست و بعد می پرسید درسته؟
WinForm و Windows Forms مگه فرق دارن؟!
در پاسخ هایی که من می دهم Windows Forms اسم واسط کاربری ویندوز ئه.
اولی اسم یک پلتفرم در NET. ئه و دومی اسم واسط کاربری گرافیکی یک سیستم عامل.
در NET. اون WinForm یک اسم دیگه داره که همون Windows Forms ئه، پس در NET. هر دوشون اسم یک پلتفرم ئه. اما جدا از محیط NET. یک Windows Forms هست که ربطی به NET. نداره. Windows Forms واسط کاربری گرافیکی سیستم عامل ویندوز ئه، قبل از اینکه NET. ای بوجود بیاد بوده. اون WinForm بر اساس همون واسط کاربری طراحی شده و داره از همین Windows Forms ویندوز استفاده می کنه.
در ویکی پدیا که هر دو را یکی زده .

من سندی از تاریخچه ی مجزایی از هر کدوم پیدا نکردم .
فقط توی ویکی پدیا ، هر دو را یکی میدونه و نسخه ی اولیه را وابسته به .net framework و انتشار اولیه شون را هم 2002 زده .
دلیل خیلی ساده ای داره، چون مدت ها اسم خاصی نداشته. بعد از NET. بود که اسمش شد Windows Forms. سالها ویندوز فقط یک واسط کاربری گرافیکی داشته که نیازی به تفکیک اش از واسط های کاربری دیگه نبوده. مثلا قبل از NET. خود مایکروسافت در ++Microsoft Visual C برای توصیف پلتفرم های کنسولی و گرافیکی دو تا اسم شبیه به هم بکار برده بود :
Win32 Application
Win32 Console Application
برای همین در مستندات قدیمی چه مایکروسافت و چه شرکت های دیگه با عبارات متفاوتی برای مشخص کردن واسط کاربری ویندوز یا پلتفرمی که به اون واسط کاربری گرافیکی مربوطه استفاده کرده اند، نه یک اسم خاص که مایکروسافت تعیین کرده باشه.
همانطور که قبل از پیدایش NET. محیطی که برنامه ها اجرا می شدند همون Native بود ولی اسمی از Native نبود، کسی نمی دونست Native یعنی چی، چون محیط Managed ای نبود که برای اون قبلی اسم خاصی در نظر بگیرند.
 

SajjadKhati

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

نه الزاما، نمیدونم این باید ها رو بر اساس چه محدودیت هایی تعیین می کنید.
شما یک مشخصه دارید که وابسته به Model و ViewModel ئه، چون نوع اش در Model مشخص شده و تعریف اش در ViewModel ئه. بر اساس معماری MVVM چه محدودیت هایی حاصل این وابستگی ئه؟ که فقط در Model یا ViewModel میتونه مورد دسترسی مستقیم قرار بگیره و نباید در View صریحا کاری باهاش داشت که وابستگی به Model ایجاد کنه. MVVM برای مقدار دهی یا تعیین مقدار اولیه محدودیت دیگری ایجاد می کنه؟ فرضا اگر در Model بهش مقداری داده بشه چه چیزی نقض میشه که نباید انجام بشه؟

قبلا در موردش صحبت کردیم، اصلا نمی توانیم یا می توانیم مطرح نیست، به صورت هایی می توانیم و به صورت هایی نمی توانیم. مساله گرفتنش نیست، مساله وابستگی ئه.
شما به قاعده ای که نباید نقض بشه باید توجه کنید، نه لیست هزاران کاری که میشه کرد یا نمیشه کرد.
فرض کنید که در ViewModel یک مشخصه داریم از نوع object که یک Student بر میگردونه و در View مقدار این مشخصه رو به عنوان یک object بگیرید و با ToString یا با GetType().GetField یا هر روش غیر مستقیم دیگری از مقدارش استفاده کنید، بدون اینکه هیچ کاری با نوع شیء Student داشته باشید، مشابه شیوه ای که Binding ها می کنند. کاری که قبلا هم در کد ها کردیم. اگر همچین کاری بکنید View تون وابستگی به Student نداره، چون اولا بدون توجه به Model و اون Student کامپایل میشه و ثانیا هر نوع ساختار سازگاری که اون فیلد با نام فلان رو داره میتونه جایگزین Student بشه، یعنی کاری با Student نداره، در object ئه دنبال فیلد فلان میگرده، شبیه Binding.
"نمیتونیم در پارامتر متدی (یا در پارامتر متد سازنده) ، مقدار پروپرتیِ Student را از View بگیریم ." ممکنه در هزاران کد درست باشه و در هزاران کد دیگه نادرست. چی درستی اش رو تعیین می کنه؟ وابستگی. اینکه View به Model وابسته شد یا نشد. باید به اون قاعده عدم وابستگی توجه کنید، به اصول معماری MVVM توجه کنید، نه اینکه کدوم کد رو بنویسیم نقض میشه یا نمیشه.


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

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

بارها در مورد استقراء صحبت کردم و توضیح دادم، البته بی فایده. هر چقدر توضیح دادم که اشتباه ئه توجهی نکردید و دائم تکرارش می کنید. از مثال های x و y و z نتیجه می گیرید که قاعده فلانی هست و بعد می پرسید درسته؟

خیلی ممنون استاد از جواب کامل تون .
بله این قضیه را گفتید . ولی من هم سئوال پرسیدم که این طور هست یا نه؟
نظرم نبود .
ضمن اینکه تجربه ی کم من را هم در نظر بگیرید .


درباره ی قضیه ی Reflection ها که یادآوری کردید درباره ی این قضیه ، خیلی به جا بود .
تشکر .

در پاسخ هایی که من می دهم Windows Forms اسم واسط کاربری ویندوز ئه.
اولی اسم یک پلتفرم در NET. ئه و دومی اسم واسط کاربری گرافیکی یک سیستم عامل.
در NET. اون WinForm یک اسم دیگه داره که همون Windows Forms ئه، پس در NET. هر دوشون اسم یک پلتفرم ئه. اما جدا از محیط NET. یک Windows Forms هست که ربطی به NET. نداره. Windows Forms واسط کاربری گرافیکی سیستم عامل ویندوز ئه، قبل از اینکه NET. ای بوجود بیاد بوده. اون WinForm بر اساس همون واسط کاربری طراحی شده و داره از همین Windows Forms ویندوز استفاده می کنه.

دلیل خیلی ساده ای داره، چون مدت ها اسم خاصی نداشته. بعد از NET. بود که اسمش شد Windows Forms. سالها ویندوز فقط یک واسط کاربری گرافیکی داشته که نیازی به تفکیک اش از واسط های کاربری دیگه نبوده. مثلا قبل از NET. خود مایکروسافت در ++Microsoft Visual C برای توصیف پلتفرم های کنسولی و گرافیکی دو تا اسم شبیه به هم بکار برده بود :
Win32 Application
Win32 Console Application
برای همین در مستندات قدیمی چه مایکروسافت و چه شرکت های دیگه با عبارات متفاوتی برای مشخص کردن واسط کاربری ویندوز یا پلتفرمی که به اون واسط کاربری گرافیکی مربوطه استفاده کرده اند، نه یک اسم خاص که مایکروسافت تعیین کرده باشه.
همانطور که قبل از پیدایش NET. محیطی که برنامه ها اجرا می شدند همون Native بود ولی اسمی از Native نبود، کسی نمی دونست Native یعنی چی، چون محیط Managed ای نبود که برای اون قبلی اسم خاصی در نظر بگیرند.

آها خیلی ممنون استاد .
قدمت این Windows Form (که به دات نت فریم وورک ربطی نداره) ، چقدر هست؟
یعنی اولین انتشارش کِی بوده؟

میگم ها .
من فکر میکردم که قدمت WinForm ئه دات نت فریم وورک ، به سال 2002 برمیگرده .
همیشه میگفتم یعنی توی 4 سال ، این همه اختلاف بین WinForm و WPF میتونستن بوجود بیارن؟!
میگفتم اگه این طور بود ، چرا چهار سالِ بعدِ WPF ، این همه اختلاف با پلتفرم دیگه نتونستن بوجود بیارن؟ یا چهار سال قبل از سال 2002 (که WinForm اومد) ، چیز خاصی ارائه نکردن .


==============================


استاد ، من یه کلاس ViewModel دارم به کد زیر :

C#:
    public class VssBackupViewModel : ViewModelBase
    {
        private string test;

        public string Test
        {
            get
            {
                return this.test;
            }
            set
            {
                this.test = value;
                this.RaisePropertyChanged();
            }
        }


        public VssBackupViewModel()
        {
            this.Test = "salam";
        }

    }

یه کلاس برای پایه ی ویندوزهای پروژه ام دارم :

C#:
    public class PoshtibangirToloWindowBase : Window
    {
            public VssBackupViewModel VssBackupViewModel { get; set; }
            public ExtensionMethodViewModel ExtensionMethodViewModel { get; set; }
            public PoshtibangirToloUI UI { get; set; }

    }

که ویندوزِ MainWindow ام از این ارث بری میکنه .
در بخشی کدهای xaml ئه MainWindow هم :

XML:
<local:PoshtibangirToloWindowBase
        x:Class="PoshtibangirTolo.View.Windows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:PoshtibangirTolo.View.Windows"
         xmlns:ViewModel="clr-namespace:PoshtibangirTolo.ViewModel"
        x:Name="PoshtibangirToloMainWindow"
        .
        .
        .
        >
    
        <local:PoshtibangirToloWindowBase.VssBackupViewModel>
        <ViewModel:VssBackupViewModel/>
    </local:PoshtibangirToloWindowBase.VssBackupViewModel>
    <local:PoshtibangirToloWindowBase.ExtensionMethodViewModel>
        <ViewModel:ExtensionMethodViewModel/>
    </local:PoshtibangirToloWindowBase.ExtensionMethodViewModel>

            <WrapPanel Name="ContentWrapPanel" Grid.Row="1">
            <Button Content="{Binding ElementName=PoshtibangirToloMainWindow, Path=VssBackupViewModel.Test}" Padding="10, 5" Margin="10"/>
        </WrapPanel>
    
</local:PoshtibangirToloWindowBase>

اینها ، بخشی از کد که به این سئوالی که میپرسم ، مربوط میشه را گذاشتم . اون سه تا نقطه که گذاشتم ، یعنی کدهای دیگه ای که بی ربط به این قضیه بودن ، هستن .

اما با این کد ، مشکل اینه که این دکمه ای که Content اش را Binding کردم ، فقط زمانی که برنامه را اجرا کنم ، Content اش (که همون مقدارِ VssBackupViewModel.Test هست) ، میاد .
راهی هست که زمان طراحی (designer) ، این مقدارش (که salam هست) را نشون بده؟

نمیدونم مشکل از کجاست . پروپرتی VssBackupViewModel که در خط بالاترش هست . حتی این پروپرتی را در کدهای سی شارپ ، قبل از اینکه متد InitializeComponent اجرا بشه ، مقداردهی کردم ، باز هم نتیجه همینه .

پس در خیلی از Binding هایی که انجام میدیم و نتیجه اش زمان طراحی هم نشون داده میشه ، فرق اون Binding ها با این Binding چیه؟!
 

the_king

مدیرکل انجمن
خیلی ممنون استاد از جواب کامل تون .
بله این قضیه را گفتید . ولی من هم سئوال پرسیدم که این طور هست یا نه؟
نظرم نبود .
ضمن اینکه تجربه ی کم من را هم در نظر بگیرید .
اصلا به تجربه ربطی نداره، بزرگترین دانشمند معاصر هم که باشید وقتی روشی غلط ئه، نباید انجامش بدهید. تجربه شما زیاد بشه روشی که غلط ئه درست میشه؟ شما از یک طرف میگید این قضیه رو گفتید و من به حرف های شما اهمیتی نمی دهم و از طرف دیگر با همان روشی که اشتباه ئه نتیجه ای می گیرید و از من میپرسید نتیجه درسته یا نه؟ اگر حرف من اهمیتی نداره برای چی از من می پرسید؟
آها خیلی ممنون استاد .
قدمت این Windows Form (که به دات نت فریم وورک ربطی نداره) ، چقدر هست؟
یعنی اولین انتشارش کِی بوده؟
تاریخ انتشارش در تاپیک سئوالات و مباحث WPF چه اهمیتی داره؟ به شکل امروزیش با Windows 3 ارائه شد.
میگم ها .
من فکر میکردم که قدمت WinForm ئه دات نت فریم وورک ، به سال 2002 برمیگرده .
مگه غیر از اینه؟
همیشه میگفتم یعنی توی 4 سال ، این همه اختلاف بین WinForm و WPF میتونستن بوجود بیارن؟!
میگفتم اگه این طور بود ، چرا چهار سالِ بعدِ WPF ، این همه اختلاف با پلتفرم دیگه نتونستن بوجود بیارن؟ یا چهار سال قبل از سال 2002 (که WinForm اومد) ، چیز خاصی ارائه نکردن .
اولا اختلاف بوجود آوردن هنر نیست. ثانیا در تاریخ ارائه که طراحی نشده، یک محصول قبل از ارائه عمومی مدت ها در فرایند طراحی و توسعه بوده. ثالثا تمایلی ندارند که مدام یک پلتفرم جدید ارائه کنند.
استاد ، من یه کلاس ViewModel دارم به کد زیر :

C#:
    public class VssBackupViewModel : ViewModelBase
    {
        private string test;

        public string Test
        {
            get
            {
                return this.test;
            }
            set
            {
                this.test = value;
                this.RaisePropertyChanged();
            }
        }


        public VssBackupViewModel()
        {
            this.Test = "salam";
        }

    }

یه کلاس برای پایه ی ویندوزهای پروژه ام دارم :

C#:
    public class PoshtibangirToloWindowBase : Window
    {
            public VssBackupViewModel VssBackupViewModel { get; set; }
            public ExtensionMethodViewModel ExtensionMethodViewModel { get; set; }
            public PoshtibangirToloUI UI { get; set; }

    }

که ویندوزِ MainWindow ام از این ارث بری میکنه .
در بخشی کدهای xaml ئه MainWindow هم :

XML:
<local:PoshtibangirToloWindowBase
        x:Class="PoshtibangirTolo.View.Windows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:PoshtibangirTolo.View.Windows"
         xmlns:ViewModel="clr-namespace:PoshtibangirTolo.ViewModel"
        x:Name="PoshtibangirToloMainWindow"
        .
        .
        .
        >
  
        <local:PoshtibangirToloWindowBase.VssBackupViewModel>
        <ViewModel:VssBackupViewModel/>
    </local:PoshtibangirToloWindowBase.VssBackupViewModel>
    <local:PoshtibangirToloWindowBase.ExtensionMethodViewModel>
        <ViewModel:ExtensionMethodViewModel/>
    </local:PoshtibangirToloWindowBase.ExtensionMethodViewModel>

            <WrapPanel Name="ContentWrapPanel" Grid.Row="1">
            <Button Content="{Binding ElementName=PoshtibangirToloMainWindow, Path=VssBackupViewModel.Test}" Padding="10, 5" Margin="10"/>
        </WrapPanel>
  
</local:PoshtibangirToloWindowBase>

اینها ، بخشی از کد که به این سئوالی که میپرسم ، مربوط میشه را گذاشتم . اون سه تا نقطه که گذاشتم ، یعنی کدهای دیگه ای که بی ربط به این قضیه بودن ، هستن .

اما با این کد ، مشکل اینه که این دکمه ای که Content اش را Binding کردم ، فقط زمانی که برنامه را اجرا کنم ، Content اش (که همون مقدارِ VssBackupViewModel.Test هست) ، میاد .
راهی هست که زمان طراحی (designer) ، این مقدارش (که salam هست) را نشون بده؟

نمیدونم مشکل از کجاست . پروپرتی VssBackupViewModel که در خط بالاترش هست . حتی این پروپرتی را در کدهای سی شارپ ، قبل از اینکه متد InitializeComponent اجرا بشه ، مقداردهی کردم ، باز هم نتیجه همینه .

پس در خیلی از Binding هایی که انجام میدیم و نتیجه اش زمان طراحی هم نشون داده میشه ، فرق اون Binding ها با این Binding چیه؟!
تفاوت در Binding ها نیست، در رفتار Designer ویژوال استدیو است.
خوشایند نیست ولی فرق اش در اینه که Designer ویژوال استدیو نمیدونه که میتونه در زمان طراحی از اون کلاس MainWindow شیء بسازه و برای همین در Designer اون Binding طوری که میخواهید عمل نمی کنه. Designer تلاش می کنه بر اساس اون برداشتی که از توصیف داره ساختاری بسازه و ظاهر رو پیش از اجرا نشون بده، برای همین الزاما چیزی که نشون میده اون چیزی که در اجرا می بینید نیست.
اگر کمک اش کنید کار اش رو بهتر انجام میده.
به d:IsDesignTimeCreatable و d:DesignInstance توجه کنید :
و مثال هایی از این دست :

فرضا بر اساس xaml شما، به اون d:DesignInstance و DataContext که نوشتم توجه کنید :
XML:
<local:PoshtibangirToloWindowBase x:Class="WpfApp.MainWindow"
                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
.
.
.
                            x:Name="PoshtibangirToloMainWindow"
                            Title="MainWindow" Height="450" Width="800"
.
.
.
                            d:DataContext ="{d:DesignInstance {x:Type local:MainWindow}, IsDesignTimeCreatable=True}"
                            DataContext="{Binding RelativeSource={RelativeSource Self}}"
                            >
    <local:PoshtibangirToloWindowBase.VssBackupViewModel>
        <local:VssBackupViewModel/>
    </local:PoshtibangirToloWindowBase.VssBackupViewModel>
    <Grid>
        <Button Content="{Binding Path=VssBackupViewModel.Test}" Padding="10, 5" Margin="10"/>
    </Grid>
</local:PoshtibangirToloWindowBase>
 

SajjadKhati

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

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

چی شده؟
من کی گفتم (یا حتی نتیجه گرفتم) که به حرف های شما اهمیتی نمیدم؟!!
نمیدونم چی باعث ناراحتی تون شد (اگه چیزی باعث ناراحتی تون شد ، معذرت میخوام) . ولی سئوالم را پرسیده بودم دیگه .


تفاوت در Binding ها نیست، در رفتار Designer ویژوال استدیو است.
خوشایند نیست ولی فرق اش در اینه که Designer ویژوال استدیو نمیدونه که میتونه در زمان طراحی از اون کلاس MainWindow شیء بسازه و برای همین در Designer اون Binding طوری که میخواهید عمل نمی کنه. Designer تلاش می کنه بر اساس اون برداشتی که از توصیف داره ساختاری بسازه و ظاهر رو پیش از اجرا نشون بده، برای همین الزاما چیزی که نشون میده اون چیزی که در اجرا می بینید نیست.
اگر کمک اش کنید کار اش رو بهتر انجام میده.
به d:IsDesignTimeCreatable و d:DesignInstance توجه کنید :
و مثال هایی از این دست :

فرضا بر اساس xaml شما، به اون d:DesignInstance و DataContext که نوشتم توجه کنید :
XML:
<local:PoshtibangirToloWindowBase x:Class="WpfApp.MainWindow"
                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
.
.
.
                            x:Name="PoshtibangirToloMainWindow"
                            Title="MainWindow" Height="450" Width="800"
.
.
.
                            d:DataContext ="{d:DesignInstance {x:Type local:MainWindow}, IsDesignTimeCreatable=True}"
                            DataContext="{Binding RelativeSource={RelativeSource Self}}"
                            >
    <local:PoshtibangirToloWindowBase.VssBackupViewModel>
        <local:VssBackupViewModel/>
    </local:PoshtibangirToloWindowBase.VssBackupViewModel>
    <Grid>
        <Button Content="{Binding Path=VssBackupViewModel.Test}" Padding="10, 5" Margin="10"/>
    </Grid>
</local:PoshtibangirToloWindowBase>

درباره ی بقیه ی قسمت های جواب تون هم ممنون .

بله . این کد رو که گفتید و گذاشتم ، درست شد .
اما d:dataContext با DataContext ئه Window مگه فرق نداره؟
پس چرا وقتی d:dataContext را ست میکنیم ، تا DataContext ئه Window را ست نکنیم ، جواب نمیده؟
نمیشه d:dataContext را ست کنیم اما DataContext ئه Window را ست نکنیم و جواب بده؟
یعنی نمیشه که در Binding ئه دکمه ام ، بجای اینکه از DataContext ئه Window استفاده کنه ، بصورت صریح از ElementName=PoshtibangirToloMainWindow استفاده کنه (با توجه به اینکه d:dataContext هم به همون مقدار تنظیم کنیم) اما همینطوری زمان طراحی کار کنه؟

تشکر استاد .
 

the_king

مدیرکل انجمن
بله . این کد رو که گفتید و گذاشتم ، درست شد .
اما d:dataContext با DataContext ئه Window مگه فرق نداره؟
فرق داره، دو تا کارکرد متفاوت دارند، همانطور که Source با d:Source در CollectionViewSource فرق داره.
You can even set both d:DataContext and DataContext in markup if you like. d:DataContext will override at design-time, and DataContext will override at run-time.​
نمیشه d:dataContext را ست کنیم اما DataContext ئه Window را ست نکنیم و جواب بده؟
هر کدوم رو برای هدف متفاوتی می نویسید، یکی اش برای زمان طراحی ئه، اون یکی برای زمان اجرا. مکمل هم هستند.
مجبور که نیستید اینها رو برای Window بنویسید، می توانید برای خود Button که میخواد ازشون استفاده کنه بنویسید.
XML:
        <Button
            d:DataContext ="{d:DesignInstance {x:Type local:MainWindow}, IsDesignTimeCreatable=True}"
            DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:MainWindow}}"
            Content="{Binding Path=VssBackupViewModel.Test}" Padding="10, 5" Margin="10"/>
یعنی نمیشه که در Binding ئه دکمه ام ، بجای اینکه از DataContext ئه Window استفاده کنه ، بصورت صریح از ElementName=PoshtibangirToloMainWindow استفاده کنه (با توجه به اینکه d:dataContext هم به همون مقدار تنظیم کنیم) اما همینطوری زمان طراحی کار کنه؟
ElementName و Source و RelativeSource رقیب هم هستند و مشخص کننده منبع داده ای بجز اون منبع داده پیشفرض، و هر کدوم رو که به کار ببرید منبع پیشفرضی که میتونست با DataContext مشخص بشه رو میذارید کنار و منبع دیگری رو مشخص می کنید. اگر در System.Windows.Data.Binding نگاه کنید منبع از چند طریق متفاوت مشخص میشه :
C#:
    private enum SourceProperties : byte
    {
        None,
        RelativeSource,
        ElementName,
        Source,
        StaticSource,
        InternalSource
    }
نمیشه که هم ElementName بکار برده بشه ولی منبع اون Element نباشه و منبع با DataContext مشخص بشه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
فرق داره، دو تا کارکرد متفاوت دارند، همانطور که Source با d:Source در CollectionViewSource فرق داره.


هر کدوم رو برای هدف متفاوتی می نویسید، یکی اش برای زمان طراحی ئه، اون یکی برای زمان اجرا. مکمل هم هستند.
مجبور که نیستید اینها رو برای Window بنویسید، می توانید برای خود Button که میخواد ازشون استفاده کنه بنویسید.
XML:
        <Button
            d:DataContext ="{d:DesignInstance {x:Type local:MainWindow}, IsDesignTimeCreatable=True}"
            DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:MainWindow}}"
            Content="{Binding Path=VssBackupViewModel.Test}" Padding="10, 5" Margin="10"/>

ElementName و Source و RelativeSource رقیب هم هستند و مشخص کننده منبع داده ای بجز اون منبع داده پیشفرض، و هر کدوم رو که به کار ببرید منبع پیشفرضی که میتونست با DataContext مشخص بشه رو میذارید کنار و منبع دیگری رو مشخص می کنید. اگر در System.Windows.Data.Binding نگاه کنید منبع از چند طریق متفاوت مشخص میشه :
C#:
    private enum SourceProperties : byte
    {
        None,
        RelativeSource,
        ElementName,
        Source,
        StaticSource,
        InternalSource
    }
نمیشه که هم ElementName بکار برده بشه ولی منبع اون Element نباشه و منبع با DataContext مشخص بشه.

خیلی ممنون استاد .
منم تعجبم همینه دیگه .
که چرا با اونکه d:dataContext با DataContext ئه ویندوز (یا کنترل) که ربطی به هم ندارن ، اما اگه d:dataContext را تعیین کنم اما DataContext ئه کنترل را تعیین نکنم (و بجاش از پروپرتیِ ElementName یا Source در Binding استفاده کنم) ، جواب نمیدن .

دلیلش را هنوز متوجه نشدم .
همچنین اینکه هنوز دقیق متوجه نشدم که چرا فرضا در Binding هایی که تا حالا انجام میدادم ، چرا نیاز نبود زمان طراحی ، از d:dataContext استفاده کنم اما فرقِ این Binding با بقیه چیه که در اینجا نیاز هست اما اونجا نیست؟
در واقع از کجا بفهمیم که در چه نوع Binding هایی ، به d:dataContext نیاز هست و در چه Binding هایی نیاز نیست؟

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

بعد اینکه استاد من در SettingWindow ام (که از کلاس PoshtibangirToloWindowBase و این هم از Window ارث بری میکنه) ، Binding ئه زیر را انجام دادم :

XML:
VssBackupViewModel="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Owner.VssBackupViewModel}"

پنجره ی SettingWindow هم بصورت کد سی شارپ باز میشه و قبل از Show کردن ، پروپرتیِ Owner ئه SettingWindow ، به شیِ MainWindow ، مقداردهی میشه .
ویندوزِ MainWindow هم از کلاس PoshtibangirToloWindowBase ارث بری میکنه .
پروپرتیِ VssBackupViewModel هم درون PoshtibangirToloWindowBase تعریف شده .

فعلا شراطی ندارم که این کد را زمان اجرا بررسی کنم (هر چند وقتی اجرا میکنم ، اروری نمیده) .
اما زمان طراحی ، چون Owner از نوع Window هست و پروپرتیِ VssBackupViewModel ، در فرزندش یعنی PoshtibangirToloWindowBase تعریف شد ، ارور زیر را میده :

کد:
The property 'VssBackupViewModel' was not found in type 'Window'.

بجز اینکه بتونیم با نوشتنِ Converter ، این ارور را رفع کنیم ، آیا راه دیگه ای هم هست؟

البته توی PoshtibangirToloWindowBase هم یه پروپرتی هم میتونم برای این کار بنویسم .
یا اینکه DataContext ئه SettingWindow را هم میشه this.Owner در کد سی شارپ در نظر گرفت .
البته این هم انگار میشه که ElementName را PoshtibangirToloSettingWindow.Owner در نظر بگیریم .

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

بعد اینکه ما وقتی در کد xaml ، عمل Binding ای انجام بدیم (با توجه به اینکه پروپرتیِ Source مون ، حتی وقتی که متدِ سازنده ی کلاسی که Binding Target در اون کلاس انجام میشه ، اجرا میشه ، از همون اول ، مقدار داشته باشه) ، در اولین رویدادی که بتونیم در Target Property مون ، مقدارش را دریافت کنیم (یعنی مقدار Target Property مون ، null نباشه) ، اون چه رویدادی هست؟

من یه کم که تست کردم ، در متد سازنده ی کلاس ، Target Property مون null بود .
در رویداد Loaded ، اون Target Property مون مقدار داشت .
حالا نمیدونم رویدادهایی قبل از رویداد Loaded وجود داره که Target Property مون در اون رویداد ، مقدار داشته باشه (که تست میکنم) .

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

the_king

مدیرکل انجمن
خیلی ممنون استاد .
منم تعجبم همینه دیگه .
که چرا با اونکه d:dataContext با DataContext ئه ویندوز (یا کنترل) که ربطی به هم ندارن ، اما اگه d:dataContext را تعیین کنم اما DataContext ئه کنترل را تعیین نکنم (و بجاش از پروپرتیِ ElementName یا Source در Binding استفاده کنم) ، جواب نمیدن .
تعجبی نداره، توضیح هر کدوم رو بخونید، رفتار عجیب و غریبی ندارند.
همچنین اینکه هنوز دقیق متوجه نشدم که چرا فرضا در Binding هایی که تا حالا انجام میدادم ، چرا نیاز نبود زمان طراحی ، از d:dataContext استفاده کنم اما فرقِ این Binding با بقیه چیه که در اینجا نیاز هست اما اونجا نیست؟
تایپ کردن من باعث نمیشه که شما دقیق متوجه بشوید، وقتی دقیق متوجه می شوید که پاسخ های من رو بخونید که نمیخونید. در پست قبلی نوشتم که "تفاوت در Binding ها نیست، در رفتار Designer ویژوال استدیو است."، و باز می گید فرق این Binding با اون Binding چیست، یعنی اصلا کاری به پاسخ های من ندارید، دارم برای خودم می نویسم.
در واقع از کجا بفهمیم که در چه نوع Binding هایی ، به d:dataContext نیاز هست و در چه Binding هایی نیاز نیست؟
به Binding ربطی نداره، به رفتار Designer مربوطه. اگر Designer برای منبع داده DataContext نوع مناسبی رو بکار نبرد (عملکرد Designer مربوط به زمان طراحی است، نه اجرا) و این مساله برای طراحی تون مشکل ایجاد کرد، با d:DataContext می توانید راهنمایی اش کنید. اگر مشکلی ایجاد نکرد طبعا نیازی به راهنمایی اش نیست. ممکنه اصلا در ارتقاء های بعدی ویژوال استدیو Designer رفتار متفاوتی داشته باشه.
بعد اینکه استاد من در SettingWindow ام (که از کلاس PoshtibangirToloWindowBase و این هم از Window ارث بری میکنه) ، Binding ئه زیر را انجام دادم :

XML:
VssBackupViewModel="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Owner.VssBackupViewModel}"

پنجره ی SettingWindow هم بصورت کد سی شارپ باز میشه و قبل از Show کردن ، پروپرتیِ Owner ئه SettingWindow ، به شیِ MainWindow ، مقداردهی میشه .
ویندوزِ MainWindow هم از کلاس PoshtibangirToloWindowBase ارث بری میکنه .
پروپرتیِ VssBackupViewModel هم درون PoshtibangirToloWindowBase تعریف شده .

فعلا شراطی ندارم که این کد را زمان اجرا بررسی کنم (هر چند وقتی اجرا میکنم ، اروری نمیده) .
اما زمان طراحی ، چون Owner از نوع Window هست و پروپرتیِ VssBackupViewModel ، در فرزندش یعنی PoshtibangirToloWindowBase تعریف شد ، ارور زیر را میده :

کد:
The property 'VssBackupViewModel' was not found in type 'Window'.

بجز اینکه بتونیم با نوشتنِ Converter ، این ارور را رفع کنیم ، آیا راه دیگه ای هم هست؟
ایده بدی داره. وقتی پنجره ایجاد میشه Owner داره؟ یا بعدا قبل از اینکه که نشونش بدهید صاحب Owner میشه؟ طبعا زمانی که میخواد پنجره ایجاد بشه Owner ای نداره. Owner رو بعدا زمانی که پنجره ایجاد شد مشخص می کنید. Binding رو نمیدونم کجا نوشته اید، Binding به این Owner کی انجام میشه؟ ظاهرا زمانی که میخواد پنجره ایجاد بشه، یعنی وقتی Owner نداره.
بجای اینکارا و Binding برای VssBackupViewModel، می توانید برای خود مشخصه در get اش شرط اضافه کنید که اگر مقداری که میخواد برگردونه null بود و Owner ای داشت که از قضا PoshtibangirToloWindowBase بود، اونوقت مقدار Owner.VssBackupViewModel رو به عنوان مقدار پیشفرض بجای null برگردونه. شبیه BackColor کنترل های ویندوز که رنگ پیشفرض رو از والدشون برمی گردونند، مگر اینکه مقداری بهشون بدهید.
بعد اینکه ما وقتی در کد xaml ، عمل Binding ای انجام بدیم (با توجه به اینکه پروپرتیِ Source مون ، حتی وقتی که متدِ سازنده ی کلاسی که Binding Target در اون کلاس انجام میشه ، اجرا میشه ، از همون اول ، مقدار داشته باشه) ، در اولین رویدادی که بتونیم در Target Property مون ، مقدارش را دریافت کنیم (یعنی مقدار Target Property مون ، null نباشه) ، اون چه رویدادی هست؟
جمله بندی تون یه جوری که متوجه نمی شوم مقدار Source رو می خواهید یا مقدار انتقالی از Binding رو. اگر منظورتون رخداد انتقال داده از Source به Target Property است، توضیحات مشخصه NotifyOnTargetUpdated و رخداد TargetUpdated رو بخونید.
طبعا میتونه به دلیل انتقال هر مقداری باشه، چه null و چه غیر null که می توانید بعدا بررسی اش کنید که هست یا نیست.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
تعجبی نداره، توضیح هر کدوم رو بخونید، رفتار عجیب و غریبی ندارند.

تایپ کردن من باعث نمیشه که شما دقیق متوجه بشوید، وقتی دقیق متوجه می شوید که پاسخ های من رو بخونید که نمیخونید. در پست قبلی نوشتم که "تفاوت در Binding ها نیست، در رفتار Designer ویژوال استدیو است."، و باز می گید فرق این Binding با اون Binding چیست، یعنی اصلا کاری به پاسخ های من ندارید، دارم برای خودم می نویسم.

به Binding ربطی نداره، به رفتار Designer مربوطه. اگر Designer برای منبع داده DataContext نوع مناسبی رو بکار نبرد (عملکرد Designer مربوط به زمان طراحی است، نه اجرا) و این مساله برای طراحی تون مشکل ایجاد کرد، با d:DataContext می توانید راهنمایی اش کنید. اگر مشکلی ایجاد نکرد طبعا نیازی به راهنمایی اش نیست. ممکنه اصلا در ارتقاء های بعدی ویژوال استدیو Designer رفتار متفاوتی داشته باشه.


خیلی ممنون استاد .
آها . یعنی Designer ، یه DataContext ئه مجزا از زمان اجرای برنامه داره .
ممکنه گاها DataContext ئه Designer (که همون d:dataContext هست) ، همون چیزی نباشه که DataContext ئه یه کنترل ، در زمان اجرای برنامه میشه . در این صورت ، نتیجه اش این میشه که در زمان طراحی ، نتیجه ی عمل Binding ، مثل نتیجه ی Binding در زمان اجرا نمیشه .
هر وقت همین مشکل پیش اومد (یعنی نتیجه ی Binding در زمان اجرا و زمان طراحی متفاوت بود) ، مقدار d:dataContext در کنترل مورد نظر را باید درست تنظیم کنیم .
درست متوجه شدم؟

اما سئوال پیش میاد که چرا گاها DataContext ئه Designer ، بصورت اتوماتیک ، همون چیزی تنظیم نمیشه که زمان اجرا (در کدهای xaml) هست؟ و چرا گاها هم درست تنظیم میشه؟


ایده بدی داره. وقتی پنجره ایجاد میشه Owner داره؟ یا بعدا قبل از اینکه که نشونش بدهید صاحب Owner میشه؟

قبل از اینکه نشون بده ، صاحب Owner میشه .

طبعا زمانی که میخواد پنجره ایجاد بشه Owner ای نداره. Owner رو بعدا زمانی که پنجره ایجاد شد مشخص می کنید. Binding رو نمیدونم کجا نوشته اید، Binding به این Owner کی انجام میشه؟

Binding ، در کدهای xaml و در کنترلِ UserControl انجام میدادم . الان با پیشنهادی که در زیر گفته بودید (مثل قضیه ی BackColor) ، دیگه میخوام اون سناریو را پیاده سازی کنم و Binding نمیکنم .

بجای اینکارا و Binding برای VssBackupViewModel، می توانید برای خود مشخصه در get اش شرط اضافه کنید که اگر مقداری که میخواد برگردونه null بود و Owner ای داشت که از قضا PoshtibangirToloWindowBase بود، اونوقت مقدار Owner.VssBackupViewModel رو به عنوان مقدار پیشفرض بجای null برگردونه. شبیه BackColor کنترل های ویندوز که رنگ پیشفرض رو از والدشون برمی گردونند، مگر اینکه مقداری بهشون بدهید.


بله . واقعا ایده ی قشنگی هه (همونطور که گفتید ، مثل BackColor کنترل های ویندوز که رنگ پیشفرض رو از والدشون برمی گردونند، مگر اینکه مقداری بهشون بدیم) .
لازم به Binding هم نداره .
خیلی ممنون .

جمله بندی تون یه جوری که متوجه نمی شوم مقدار Source رو می خواهید یا مقدار انتقالی از Binding رو. اگر منظورتون رخداد انتقال داده از Source به Target Property است، توضیحات مشخصه NotifyOnTargetUpdated و رخداد TargetUpdated رو بخونید.
طبعا میتونه به دلیل انتقال هر مقداری باشه، چه null و چه غیر null که می توانید بعدا بررسی اش کنید که هست یا نیست.

منظورم اینه که فرضا در کلاس SettingWindow ، یه پروپرتی درست کردیم (مثلا همین VssBackupViewModel در زیر) که این پروپرتی را ، به پروپرتی ای در کلاس MainWindow در کدهای xaml ، عمل Binding بهش انجام دادیم :

کد:
<local:PoshtibangirToloWindowBase
        x:Class="PoshtibangirTolo.View.Windows.SettingWindow"
        VssBackupViewModel="{Binding ElementName=PoshtibangirToloSettingWindow.Owner, Path=VssBackupViewModel}"
        />

در MainWindow هم به این صورت از SettingWindow ، در کلیک دکمه ای شی میسازیم :

C#:
            SettingWindow settingWindow = new SettingWindow();
            settingWindow.Owner = this;
            settingWindow.ShowDialog();

در کد xaml هم ، پروپرتیِ VssBackupViewModel ئه SettingWindow ، به پروپرتیِ VssBackupViewModel ئه MainWindow ، با عمل Binding متصل شده .
در کد سی شارپ هم ، پروپرتیِ VssBackupViewModel ئه MainWindow ، حتی قبل از اینکه رویداد این دکمه (که از پنجره ی SettingWindow شی میسازه و نمایش میده) اجرا بشه ، مقدار داره .

حالا در اولین رویدادی که در SettingWindow میتونیم مقدارِ VssBackupViewModel ئه SettingWindow را بگیریم که null نباشه ، کجاست؟
البته همونطور که گفتین ، من از این روش استفاده نمیکنم . از روشی که شما گفتین میخوام استفاده کنم .

تشکر استاد .
 

the_king

مدیرکل انجمن
خیلی ممنون استاد .
آها . یعنی Designer ، یه DataContext ئه مجزا از زمان اجرای برنامه داره .
ممکنه گاها DataContext ئه Designer (که همون d:dataContext هست) ، همون چیزی نباشه که DataContext ئه یه کنترل ، در زمان اجرای برنامه میشه . در این صورت ، نتیجه اش این میشه که در زمان طراحی ، نتیجه ی عمل Binding ، مثل نتیجه ی Binding در زمان اجرا نمیشه .
هر وقت همین مشکل پیش اومد (یعنی نتیجه ی Binding در زمان اجرا و زمان طراحی متفاوت بود) ، مقدار d:dataContext در کنترل مورد نظر را باید درست تنظیم کنیم .
درست متوجه شدم؟
بله.
اما سئوال پیش میاد که چرا گاها DataContext ئه Designer ، بصورت اتوماتیک ، همون چیزی تنظیم نمیشه که زمان اجرا (در کدهای xaml) هست؟ و چرا گاها هم درست تنظیم میشه؟
Designer اطلاعی از شرایط زمان اجرا نداره، نمیدونه از فلان کلاسی که شما ساختید در زمان اجرا با چه شرایطی و به چه شیوه ای شیء ساخته میشه. ممکنه یک پنجره ای داشته باشید، بلا استفاده، که اصلا در زمان اجرا شیء ای ازش ساخته نشه، اینکه در زمان اجرا ازش شیء ساخته نمیشه که ربطی به وظیفه Designer نداره، Designer میخواد امکان نمایش ظاهر اون پنجره رو در زمان طراحی بده، به شیوه ای که ممکنه اصلا در زمان اجرا رخ نده.
قبل از اینکه نشون بده ، صاحب Owner میشه .
قاعدتا همینه، چون مشخصه Owner برای Instance ئه، مستقل از شیء و Static که نیست، اول باید از پنجره شیء ای ساخته بشه. هر Binding ای که در زمان ایجاد و فراخوانی پنجره عمل کنه با یک Owner ئه null مواجه میشه.

منظورم اینه که فرضا در کلاس SettingWindow ، یه پروپرتی درست کردیم (مثلا همین VssBackupViewModel در زیر) که این پروپرتی را ، به پروپرتی ای در کلاس MainWindow در کدهای xaml ، عمل Binding بهش انجام دادیم :

کد:
<local:PoshtibangirToloWindowBase
        x:Class="PoshtibangirTolo.View.Windows.SettingWindow"
        VssBackupViewModel="{Binding ElementName=PoshtibangirToloSettingWindow.Owner, Path=VssBackupViewModel}"
        />

در MainWindow هم به این صورت از SettingWindow ، در کلیک دکمه ای شی میسازیم :

C#:
            SettingWindow settingWindow = new SettingWindow();
            settingWindow.Owner = this;
            settingWindow.ShowDialog();

در کد xaml هم ، پروپرتیِ VssBackupViewModel ئه SettingWindow ، به پروپرتیِ VssBackupViewModel ئه MainWindow ، با عمل Binding متصل شده .
در کد سی شارپ هم ، پروپرتیِ VssBackupViewModel ئه MainWindow ، حتی قبل از اینکه رویداد این دکمه (که از پنجره ی SettingWindow شی میسازه و نمایش میده) اجرا بشه ، مقدار داره .

حالا در اولین رویدادی که در SettingWindow میتونیم مقدارِ VssBackupViewModel ئه SettingWindow را بگیریم که null نباشه ، کجاست؟
البته همونطور که گفتین ، من از این روش استفاده نمیکنم . از روشی که شما گفتین میخوام استفاده کنم .
این Binding کار نمی کنه. شما از Binding میخواهید حواسش به تغییر مقدار VssBackupViewModel در شی null باشه و به محض اینکه مقدار VssBackupViewModel در شیء null تغییری کرد مقدار مشخصه VssBackupViewModel پنجره رو به روز کنه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
خیلی ممنون استاد .
یه یوزر کنترل تعریف کردم و توش پرورتیِ TitleBarIconButtonPadding از نوع Thickness تعریف کردم .

C#:
    public partial class TitleBarUserControl : PoshtibangirToloUserControlBase
    {
        public static readonly DependencyProperty TitleBarIconButtonPaddingProperty = DependencyProperty.Register("TitleBarIconButtonPadding", typeof(Thickness), typeof(TitleBarUserControl));


        public Thickness TitleBarIconButtonPadding
        {
            get
            {
                return (Thickness)this.GetValue(TitleBarIconButtonPaddingProperty);
            }
            set
            {
                this.SetValue(TitleBarIconButtonPaddingProperty, value);
            }
        }
    }


و در کد xaml هم یه کنترل ShapeTextButton ساختم و این پروپرتی را به پروپرتیِ Padding اش Binding کردم :

XML:
        <CustomControls:ShapeTextButton x:Name="PoshtibangirToloIconButton" DockPanel.Dock="Left"
                d:DataContext="{d:DesignInstance Type={x:Type local:TitleBarUserControl}, IsDesignTimeCreatable=True}"
                DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:TitleBarUserControl}}}"
                Padding="{Binding Path=TitleBarIconButtonPadding}"
                Margin="10, 4" Width="Auto" Height="32" FontSize="19"
                GetContentBrush_ForWhichButtonShapeProperty="Fill"
                DefaultButtonShape="{StaticResource PoshtibangirToloShape}"
                ControlDisabledButtonShape="{StaticResource PoshtibangirToloShape}"
                MouseEnterContentBrush="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Foreground}"
                MouseDownContentBrush="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Foreground}"
                Content="پشتیبانگیر طلوع"/>

و در کد xaml ئه MainWindow هم :

XML:
        <UserControl:TitleBarUserControl TitleBarIconButtonPadding="35, 0, 0, 0">
        </UserControl:TitleBarUserControl>

(اگه بعضی از تیکه های کد نیست ، چون میخواستم خلاصه بشه . فقط اصلی کدی که به این قضیه مربوطه را بذارم) .

زمان اجرا ، مقدار TitleBarIconButtonPadding درست هست و همون مقداری که ست کردم ، هست .
زمان طراحی ، در MainWindow هم درست هست .
زمانی که درست هست ، یعنی این طوری هست :

1.JPG

اما زمان طراحی در خودِ UserControl ، با اونکه در یوزر کنترل ، در کنترلِ ShapeTextButton، مقدار d:dataContext و همچنین DataContext ئه اون کنترل را تنظیم کردم ، مقدار TitleBarIconButtonPadding یا کلا مقدار Padding ئه ShapeTextButton ، اون چیزی نیست که ست کرده بودم و کلا صِفر هست.
یعنی این طوری هست :

2.JPG

میدونین مشکل از کجاست؟

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

the_king

مدیرکل انجمن
خیلی ممنون استاد .
یه یوزر کنترل تعریف کردم و توش پرورتیِ TitleBarIconButtonPadding از نوع Thickness تعریف کردم .

C#:
    public partial class TitleBarUserControl : PoshtibangirToloUserControlBase
    {
        public static readonly DependencyProperty TitleBarIconButtonPaddingProperty = DependencyProperty.Register("TitleBarIconButtonPadding", typeof(Thickness), typeof(TitleBarUserControl));


        public Thickness TitleBarIconButtonPadding
        {
            get
            {
                return (Thickness)this.GetValue(TitleBarIconButtonPaddingProperty);
            }
            set
            {
                this.SetValue(TitleBarIconButtonPaddingProperty, value);
            }
        }
    }


و در کد xaml هم یه کنترل ShapeTextButton ساختم و این پروپرتی را به پروپرتیِ Padding اش Binding کردم :

XML:
        <CustomControls:ShapeTextButton x:Name="PoshtibangirToloIconButton" DockPanel.Dock="Left"
                d:DataContext="{d:DesignInstance Type={x:Type local:TitleBarUserControl}, IsDesignTimeCreatable=True}"
                DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:TitleBarUserControl}}}"
                Padding="{Binding Path=TitleBarIconButtonPadding}"
                Margin="10, 4" Width="Auto" Height="32" FontSize="19"
                GetContentBrush_ForWhichButtonShapeProperty="Fill"
                DefaultButtonShape="{StaticResource PoshtibangirToloShape}"
                ControlDisabledButtonShape="{StaticResource PoshtibangirToloShape}"
                MouseEnterContentBrush="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Foreground}"
                MouseDownContentBrush="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Foreground}"
                Content="پشتیبانگیر طلوع"/>

و در کد xaml ئه MainWindow هم :

XML:
        <UserControl:TitleBarUserControl TitleBarIconButtonPadding="35, 0, 0, 0">
        </UserControl:TitleBarUserControl>

(اگه بعضی از تیکه های کد نیست ، چون میخواستم خلاصه بشه . فقط اصلی کدی که به این قضیه مربوطه را بذارم) .

زمان اجرا ، مقدار TitleBarIconButtonPadding درست هست و همون مقداری که ست کردم ، هست .
زمان طراحی ، در MainWindow هم درست هست .
زمانی که درست هست ، یعنی این طوری هست :

مشاهده پیوست 114426

اما زمان طراحی در خودِ UserControl ، با اونکه در یوزر کنترل ، در کنترلِ ShapeTextButton، مقدار d:dataContext و همچنین DataContext ئه اون کنترل را تنظیم کردم ، مقدار TitleBarIconButtonPadding یا کلا مقدار Padding ئه ShapeTextButton ، اون چیزی نیست که ست کرده بودم و کلا صِفر هست.
یعنی این طوری هست :

مشاهده پیوست 114427

میدونین مشکل از کجاست؟

تشکر استاد .
طبق کدی که نشون می دهید در مقدار دهی TitleBarIconButtonPaddingProperty مقدار پیشفرض TitleBarIconButtonPadding ئه مشخص نشده و درنتیجه Thickness صفر رو بکار میبره.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
طبق کدی که نشون می دهید در مقدار دهی TitleBarIconButtonPaddingProperty مقدار پیشفرض TitleBarIconButtonPadding ئه مشخص نشده و درنتیجه Thickness صفر رو بکار میبره.

خیلی ممنون استاد .
من از حالتِ DependencyProperty بودن هم خارجش کردم . یعنی بصورت یه پروپرتی معمولی تعریف کردم .
مگه حتما باید مقدار اولیه داشته باشه؟
مقدار اولیه هم بهش بدم هم اون مقدار را لازم ندارم .
مقداری که در MainWindow براش ست میکنم (مقداری که در کد xaml ئه آخری در پست قبلی ست شد) را میخوام اون طوری نشون بده .
تشکر استاد .
 

the_king

مدیرکل انجمن
من از حالتِ DependencyProperty بودن هم خارجش کردم . یعنی بصورت یه پروپرتی معمولی تعریف کردم .
با چه هدفی؟
مگه حتما باید مقدار اولیه داشته باشه؟
خیر. فرضا شما یک مشخصه میسازید برای عنوان یک کنترل که مقدار اولیه هم نداره و null ئه. وقتی هم که Designer میخواد نشونش بده با همون عنوان تهی نشونش میده، اگر این حالت رو می پسندید دلیلی نداره که مقدار پیشفرض null رو با مقدار اولیه دیگری تغییر بدهید. ولی دیگه نباید از اینکه Designer با Thickness صفر نشون میده گلایه ای داشته باشید. مقدار پیشفرض Thickness همون صفر ئه.
مقدار اولیه هم بهش بدم هم اون مقدار را لازم ندارم .
وقتی میگید مقدار اولیه لازم نداره، یعنی میخواهید در شیء جدیدی که شما یا بیل گیتس یا Designer میسازه از مقدار پیشفرض Thickness صفر استفاده کنه مگر اینکه بعدا مقدار مشخصه رو به هر طریقی تغییرش بدهید. Designer که به مقدار مشخصه دست نمیزنه که تغییرش بده، همون مقدار پیشفرض می مونه.
مقداری که در MainWindow براش ست میکنم (مقداری که در کد xaml ئه آخری در پست قبلی ست شد) را میخوام اون طوری نشون بده .
نمیدونم چرا موضوع ساده ای مثل این رو اینقدر مبهم می بینید. یک کلاس دارید از نوع X، که یک مشخصه داره با نام A و مقدار پیشفرض 0,0,0,0
در یک روالی به هر طریقی میایید از X شیء میسازید، مقدار A اش چیه؟ 0,0,0,0 پیشفرض و بعد به A مقدار 35,0,0,0 می دهید و از نتیجه راضی هستید.
و حالا از Designer ایراد می گیرید که چرا اون وقتی از X شی میسازه مقدار A ئه 0,0,0,0 ئه. خوب مقدار پیشفرض در مشخصه تون همونه دیگه. خودتونم وقتی از کلاس شیء میساختید مقدار مشخصه همون 0,0,0,0 بوده، بعدا تغییرش داده اید.
 

SajjadKhati

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

خیلی ممنون استاد .
با چه هدفی ، یعنی چه؟
کلا پروپرتی عادی تعریف کردم دیگه . DependencyProperty که قبلا بود ، الان نیست .
همچنین ممنون از جواب بقیه ی قسمت ها .

و حالا از Designer ایراد می گیرید که چرا اون وقتی از X شی میسازه مقدار A ئه 0,0,0,0 ئه. خوب مقدار پیشفرض در مشخصه تون همونه دیگه. خودتونم وقتی از کلاس شیء میساختید مقدار مشخصه همون 0,0,0,0 بوده، بعدا تغییرش داده اید.

آها ، یعنی Designer ، صرفا مقدار اولیه ی پروپرتی ها را که هر چی باشه ، از اون استفاده میکنه؟
آخه من Binding کردم .
وقتی توسط Binding ، مقدار پروپرتیِ هدف (Target Property Binding) که پروپرتیِ TitleBarIconButtonPadding هست ، تغییر کنه ، در اون زمان Designer نمیتونه متوجه بشه و ازش استفاده کنه؟
اگه این طوره ، پس چرا تا حالا Binding هایی که به همین طریق انجام میدادیم ، همون مقداری که در Binding Source بود ، Designer بهش واکنش نشون میداد و به همون مقدار تغییر میکرد؟
بعد ، در Designer ئه MainWindow (عکس اول در دو پست قبلی) پس چرا درست کار میکنه؟ (فقط Designer ئه UserControl این مشکل را داره) .
واسه ی d:dataContext نیست؟ یعنی با تنظیم d:dataContext ، درست نمیشه؟ انگار باید مربوط به d:dataContext باشه . نیست؟

کلا غیر از مقداردهی اولیه ، راهی نیست؟
تشکر استاد .
 
آخرین ویرایش:

the_king

مدیرکل انجمن
خیلی ممنون استاد .
با چه هدفی ، یعنی چه؟
یک کنترل داشتید که شبیه سایر کنترل های WPF از DependencyProperty برای تعریف کردن مشخصه استفاده کرده بود که روال عادی هم اینه. بعد گفتید از DependencyProperty خارجش کردید، برای همین پرسیدم که با چه هدفی DependencyProperty رو گذاشتید کنار و مشخصه عادی اش کردید؟ بالاخره بی دلیل که کاری رو انجام نمی دهید.
آها ، یعنی Designer ، صرفا مقدار اولیه ی پروپرتی ها را که هر چی باشه ، از اون استفاده میکنه؟
آخه من Binding کردم .
شما فرض کنید Designer نیست و یک کد دیگری است که از کلاس کنترل شی میسازه، فرقی می کنه؟ وقتی من A رو Binding کنم به B، داخل A مقدار B قرار میگیره، هر چی که باشه. حالا وقتی برای B مقدار جدیدی اعمال نشده مقدار B چیه؟ مقدار پیشفرض اش. چه در Designer چه در هر کدی که شی میسازه. شما Padding رو Binding کردید به مقدار مشخصه. خوب مقدار مشخصه چیه؟ همون مقدار پیشفرض 0,0,0,0 دیگه.
وقتی توسط Binding ، مقدار پروپرتیِ هدف (Target Property Binding) که پروپرتیِ TitleBarIconButtonPadding هست ، تغییر کنه ، در اون زمان Designer نمیتونه متوجه بشه و ازش استفاده کنه؟
چرا اتفاقا متوجه میشه. اما تغییر نکرده، کجا مقدار TitleBarIconButtonPadding تغییر کرده؟ کد کلاس TitleBarUserControl رو که دارید، کد xaml اش رو هم که دارید. یک کدی شبیه به ()var a = new ShapeTextButton رو در نظر بگیرید، تفسیر اش کنید و بگید کجا مقدار اون a.TitleBarIconButtonPadding تغییر کرده که توقع دارید Designer اعمالش کنه. تغییر نمی کنه، نه در کلاس اش به TitleBarIconButtonPadding مقداری داده اید و نه در xaml اش. Binding کردید به مشخصه ای که مقدارش همون پیشفرض مونده.
کلا غیر از مقداردهی اولیه ، راهی نیست؟
هست. فکر کنید و راه هایش رو پیدا کنید. از سه حالت که خارج نیست، مشخصه مقدار مطلوب رو یا همون اول در مقدار اولیه میگیره یا بعدا مقدارش به مقدار مطلوب تغییر می کنه، یا در get اش بجای مقدار پیشفرض اولیه مقدار مطلوب رو بر می گردونه. حالت چهارمی نداره.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
یک کنترل داشتید که شبیه سایر کنترل های WPF از DependencyProperty برای تعریف کردن مشخصه استفاده کرده بود که روال عادی هم اینه. بعد گفتید از DependencyProperty خارجش کردید، برای همین پرسیدم که با چه هدفی DependencyProperty رو گذاشتید کنار و مشخصه عادی اش کردید؟ بالاخره بی دلیل که کاری رو انجام نمی دهید.

خیلی ممنون استاد .
از DependencyProperty بودن ، خارج کردم چون دیدم حداقل فعلا از اون پروپرتی ای که تعریف کردم ، به عنوان Binding Target Property استفاده نمیکنم .
همچنین وقتی از اون پروپرتی ، به عنوان Binding Source Property استفاده میکنم ، در این حالت ، اون پروپرتی ، چه DependencyProperty باشه یا نباشه ، در عملکردش فرقی نداره (چه زمان اجرای برنامه و چه زمان طراحی) .

واسه ی همین گفتم انگار نیازی نیست ، پس به عنوان پروپرتیِ عادی تعریف کنم . اگه بعدا نیازی بود ، DependencyProperty اش میکنم .

چرا اتفاقا متوجه میشه. اما تغییر نکرده، کجا مقدار TitleBarIconButtonPadding تغییر کرده؟

در پست 831 ، در کد سومی که کد xaml هست ، مقدار TitleBarIconButtonPadding تغییر کرده دیگه :

XML:
        <UserControl:TitleBarUserControl TitleBarIconButtonPadding="35, 0, 0, 0">
        </UserControl:TitleBarUserControl>

که هم ، زمان اجرای برنامه ، و هم ، زمان طراحی در MainWindow ، همین مقدار را داره و درست کار میکنه .
اما زمان طراحی در UserControl ، مقدار پیش فرض اولیه ی خودش که صفر باشه را داره (و با این مقداری که تغییر کرد ، تغییر نمیکنه) .
عکس هر دو حالت را در همون پست گذاشتم .

کد کلاس TitleBarUserControl رو که دارید، کد xaml اش رو هم که دارید. یک کدی شبیه به ()var a = new ShapeTextButton رو در نظر بگیرید، تفسیر اش کنید و بگید کجا مقدار اون a.TitleBarIconButtonPadding تغییر کرده که توقع دارید Designer اعمالش کنه. تغییر نمی کنه، نه در کلاس اش به TitleBarIconButtonPadding مقداری داده اید و نه در xaml اش. Binding کردید به مشخصه ای که مقدارش همون پیشفرض مونده.

پروپرتیِ TitleBarIconButtonPadding ، عضو کلاس ShapeTextButton نیست .
عضو کلاس TitleBarUserControl هست . یه یوزر کنترل سفارشی برای ایجاد Title Bar در پروژه ام .

پروپرتیِ Padding ئه اون شی از ShapeTextButton را Binding کردم به پروپرتیِ TitleBarIconButtonPadding ئه TitleBarUserControl .

تشکر استاد .
 

the_king

مدیرکل انجمن
پروپرتیِ TitleBarIconButtonPadding ، عضو کلاس ShapeTextButton نیست .
عضو کلاس TitleBarUserControl هست . یه یوزر کنترل سفارشی برای ایجاد Title Bar در پروژه ام .
عذر میخوام، یک کدی شبیه به ()var a = new TitleBarUserControl رو در نظر بگیرید، تفسیر اش کنید و بگید کجا مقدار اون a.TitleBarIconButtonPadding تغییر کرده که توقع دارید Designer اعمالش کنه. به MainWindow ربطش ندهید، چون ساختن شیء از TitleBarUserControl که ربطی به MainWindow نداره. در کلاس یا توصیف TitleBarUserControl که MainWindow ای نیست.
در پست 831 ، در کد سومی که کد xaml هست ، مقدار TitleBarIconButtonPadding تغییر کرده دیگه :
فرض کنید در بیست تا پنجره تون UserControl رو استفاده کرده اید و مقدار TitleBarIconButtonPadding در هر بیست تا پنجره هم تغییر کرده، با مقادیر متفاوت. حالا وقتی UserControl رو طراحی می کنید توقع دارید برای اون مشخصه کدوم یکی از اون بیست مقدار رو ببینید؟ باید اون مقادیر در UserControl اعمال بشه؟ کدوم یکی شون؟ یک مشخصه که نمیتونه بیست تا مقدار داشته باشه. و چرا باید Designer مقداری رو از یک توصیف پنجره ای که ربطی به طراحی UserControl و ساختن شیء ازش نداره برداره و روی UserControl اعمال کنه؟
XML:
        <UserControl:TitleBarUserControl TitleBarIconButtonPadding="35, 0, 0, 0">
        </UserControl:TitleBarUserControl>
این کد برای MainWindow ئه، و خودتون میگید در زمان اجرا و در زمان طراحی MainWindow این Thickness اعمال میشه، ایرادی در اون مورد نمی بینید. اما این چه ربطی به زمان طراحی خود UserControl داره؟ وقتی شما یک UserControl دارید که در پنجره Window1 و Window2 و Window3 و Window4 استفاده شده و در هر کدوم برای TitleBarIconButtonPadding مقدار متفاوتی در نظر گرفته اید، وقتی دارید خود کنترل UserControl رو طراحی می کنید، Designer باید چهار تا مقدار برای TitleBarIconButtonPadding کنترل نشون شما بده؟ یا مقدار پیشفرض خود TitleBarIconButtonPadding رو؟
که هم ، زمان اجرای برنامه ، و هم ، زمان طراحی در MainWindow ، همین مقدار را داره و درست کار میکنه .
اما زمان طراحی در UserControl ، مقدار پیش فرض اولیه ی خودش که صفر باشه را داره (و با این مقداری که تغییر کرد ، تغییر نمیکنه) .
چرا میگید تغییر کرد؟ شما بفرمایید وقتی دارید UserControl رو طراحی می کنید کد داخل MainWindow.xaml چرا باید اعمال بشه؟ کجای کد cs یا xaml اون UserControl به MainWindow اشاره شده؟ چرا کد داخل Window1.xaml اعمال نشه؟ ارتباط پنهانی بین ساختن شیء از UserControl و MainWindow هست؟ UserControl تون وابستگی به MainWindow داره؟
 

SajjadKhati

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


استاد ، من 3 تا Window به نام های MainWindow و SettingWindow و ExceptionManagementWindow دارم که از کلاس PoshtibangirToloWindowBase (که این هم از کلاس Window) ارث بری میکنن .

کلاس PoshtibangirToloWindowBase ، شامل پروپرتی های VssBackupViewModel و ExtensionMethodViewModel (که از نوع ViewModel هستن) و همچنین پروپرتیِ UI (که از نوع PoshtibangirToloUI که این کلاس هم از InteropWithUI ارث بری میکنه و شامل اعضایی هست که کارهای مربوط به رابط کاربری را میرسه) .
خود کلاسِ PoshtibangirToloUI هم پروپرتی های VssBackupViewModel و ExtensionMethodViewModel را دارن .

همونطور که خودم در نظر داشتم و پیشنهاد دادین ، پروپرتی های VssBackupViewModel و ExtensionMethodViewModel ، یکبار ، در MainWindow ، مقداردهی میشن اما در ویندوزهای دیگه (و همچنین در کلاس PoshtibangirToloUI که یه پروپرتی از نوعِ PoshtibangirToloWindowBase به عنوانِ Owner ، تعریف کردم) ، با مشخص کردنِ پروپرتیِ Owner شون ، اگه مقدارِ این پروپرتی ها ، null باشه ، میره به ویندوزِ Owner اش مراجعه میکنه و از مقادیرِ این پروپرتی ها ، در ویندوزِ مالکش استفاده میکنه .

------

در PoshtibangirToloUI ، یه پروپرتی بنام FixedDriveComboBoxItems از نوع List<ComboBoxItem> تعریف کردم که لیست درایوهای ثابت در سیستم کاربر نهایی را برمیگردونه و متدی هم که این پروپرتی را مقداردهی میکنه ، در متد سازنده ی PoshtibangirToloUI فراخونی میشه .

در SettingWindow و ExceptionManagementWindow هم دو تا کمبوباکس دارم که میخوام مقادیرِ FixedDriveComboBoxItems را توش نمایش بدم . بنابراین هر دو کمبوباکسی که در دو ویندوز جداگانه هستن را به پروپرتیِ FixedDriveComboBoxItems ئه PoshtibangirToloUI ، عمل Binding براشون انجام دادم .

---

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

البته همونطور که گفته بودم ، در همه ی پنجره ها ، از یک شی برای پروپرتی های مربوط به ViewModel و پروپرتیِ UI استفاده میکردم که در MainWindow ، برای یکبار ، مقداردهی کرده بودم . توی PoshtibangirToloUI هم از مقادیرِ همون پروپرتی های ViewModel در MainWindow استفاده میکردم .

بعد که بیشتر فکر کردم ، دیدم دلیل این مشکل اینه که چون در همه ی ویندوز ها (برای پروپرتیِ UI) ، از یک شی از PoshtibangirToloUI استفاده میکردم و وقتی Binding انجام میدیم ، به خودِ اون کالکشن ، بایندینگ صورت نمیگیره بلکه به شیِ CollectionView ای که مقادیر آیتمی را که (در لیست کمبوباکس) انتخاب کرده بودیم ، در خودش ذخیره میکنه ، بخاطر اینه که وقتی در کمبوباکس در ویندوز ، آیتمی را انتخاب میکردیم ، در کمبوباکس در ویندوز دیگه ، همون آیتم ، انتخاب شده بود .

بنابراین وقتی به پروپرتیِ UI که در ویندوزها بودن (از نوعِ PoshtibangirToloUI بودن) ، مقدار جدیدی دادم ، این مشکل حل شد .
(مقادیرِ پروپرتی های ViewModel در همه ی ویندوز و حتی در PoshtibangirToloUI ، هنوز از یک شیِ واحد استفاده میکنن) .


===========================


اما علاوه بر این ، یه مشکل دیگه هم وجود داره (که زمانی که مشکل قبلی بود ، این مشکل هم همراش بود) .
اینکه وقتی فرضا پنجره ی SettingWindow را باز میکنم و گزینه ای را در کمبوباکس انتخاب میکنم ، دوباره که پنجره ی SettingWindow را میبندم و مجددا باز میکنم (که باز در این صورت ، شیِ جدیدی از SettingWindow ساخته میشه) ، و مجددا که کمبوباکس را کلیک میکنم ، میبینم که قبلا گزینه ای را که در همین کمبوباکس انتخاب کرده بودم ، بدون اینکه در سری جدید انتخاب کنم ، از قبل انتخاب شده بود (همچنین ، در متد سازنده ی SettingWindow ، شیِ جدیدی به پروپرتیِ UI که از نوعِ PoshtibangirToloUI بود ، میدادم) .


دقت کنید که مشکل قبلی این بود که وقتی آیتمی را در کمبوباکسِ پنجره ی SettingWindow انتخاب میکردم ، در کمبوباکسِ پنجره ی ExceptionManagementWindow هم انتخاب میشد و برعکس .
اما این بار ، کمبوباکس در دو پنجره دیگه ارتباط شون از هم جدا شد . اما در همون پنجره ، وقتی مجددا باز میکردم (که شیِ جدیدی هم از SettingWindow و هم از UI ئه SettingWindow ساخته میشد) ، همون مشکل وجود داره .

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


1.JPG
که گزینه ی 2 و 3 با هم انتخاب شدم .
اول فکر کردم شاید مشکل از تمپلیت باشه . غیر فعالش کردم ، دیدم باز هم این مشکل هست .


به هر حال ، وقتی در متد سازنده ی پنجره ها ، مقدار جدیدی برای پروپرتی های مربوط به ViewModel ساختم و اونها را به شیِ جدیدی که برای پروپرتیِ UI ساختم ، ارسال کردم ، دیدم که این مشکلات هم حل شد.

اما دلیلش را متوجه نشدم که چطور شد که با مقدار جدید دادن به پروپرتی های ViewModel ، این مشکل هم حل شد؟
من بخشی از کدهای پروژه را که فکر میکنم به این مشکل شاید مربوط باشه را جوری که خلاصه هم بشه ، در پست بعدی (پست زیر) میذارم .

ببخشید که خیلی طولانی شد.
خیلی ممنون استاد .
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
بخشی از کدهای مربوط به مشکل در پست قبلی :

کمبوباکسِ در پنجره ی SettingWindow که Binding انجام دادم (Binding ئه کمبوباکس در پنجره ی ExceptionManagementWindow هم همینطوره) :

XML:
                                <ComboBox Name="SelectDriveComboBox" HorizontalAlignment="Center" VerticalAlignment="Top" HorizontalContentAlignment="Center"
                                          Margin="0, 5" Width="320" Height="28" FlowDirection="LeftToRight" TabIndex="2" Text="لطفا درایوی را انتخاب کنید"
                                          ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:PoshtibangirToloWindowBase}, Path=UI.FixedDriveComboBoxItems}">
                                </ComboBox>

متد سازنده ی SettingWindow (در متد سازنده ی ExceptionManagementWindow هم همینطوره) :

C#:
        public SettingWindow()
        {
            InitializeComponent();



            this.VssBackupViewModel = new VssBackupViewModel();
            this.ExtensionMethodViewModel = new ExtensionMethodViewModel();
            this.UI = new PoshtibangirToloUI(this.VssBackupViewModel, this.ExtensionMethodViewModel);
        }

در کد متد سازنده ی بالا ، وقتی شیِ جدید برای پروپرتی های VssBackupViewModel و ExtensionMethodViewModel و UI ساخته نشه (3 خط آخر) ، مشکلاتی که گفتم ، پیش میاد . وگرنه درست و بدون مشکل کار میکنه .

زمانی که از پنجره ها در رویداد کلیک دکمه ای شی میسازیم :

C#:
        private void SettingOperation()
        {
            SettingWindow settingWindow = new SettingWindow();
            settingWindow.Owner = this;
            this.Title = settingWindow.Title;
            settingWindow.ShowDialog();

            this.Title = "پشتیبانگیر طلوع";
        }

متد سازنده PoshtibangirToloUI :

C#:
        public PoshtibangirToloUI(VssBackupViewModel vssBackupViewModel, ExtensionMethodViewModel extensionMethodViewModel)
        {
            this.VssBackupViewModel = vssBackupViewModel;
            this.ExtensionMethodViewModel = extensionMethodViewModel;

            this.SetFixedDriveComboBoxItemsProperty();
        }

متد SetFixedDriveComboBoxItemsProperty در کلاس PoshtibangirToloUI :

C#:
        private void SetFixedDriveComboBoxItemsProperty()
        {
            DriveInfo[] driveInfos = ExtensionMethodViewModel.GetFixedNtfsDrives();
            if (driveInfos == null || driveInfos.Length < 1)
                return;

            this.FixedDriveComboBoxItems = new List<ComboBoxItem>();
            foreach (DriveInfo driveInfo in driveInfos)
            {
                this.ExtensionMethodViewModel.DriveInfoObjectForExtensionMethod = driveInfo;
                string driveInfoFormatted = this.ExtensionMethodViewModel.DriveInfoFormatted();
               
                ComboBoxItem comboBoxItem = new ComboBoxItem();
                comboBoxItem.Content = driveInfoFormatted;
                comboBoxItem.Tag = driveInfo;

                this.FixedDriveComboBoxItems.Add(comboBoxItem);
            }
        }


بخشی از کلاس ExtensionMethodViewModel :

C#:
    public class ExtensionMethodViewModel : ViewModelBase
    {
            public DriveInfo DriveInfoObjectForExtensionMethod { get; set; }
           
        public string DriveInfoFormatted()
        {
            return this.DriveInfoObjectForExtensionMethod.DriveInfoFormatted();
        }
    }


و بخشی از کلاس ExtensionMethodForDriveInfo :
C#:
    public static class ExtensionMethodForDriveInfo
    {
                public static string DriveInfoFormatted(this DriveInfo driveInfo)
        {
            string driveName = driveInfo.Name.Substring(0, 1);
            double driveTotalSize;
            DataUnit dataUnit = (DataUnitConvert.ConvertByteToDataUnit(driveInfo.TotalSize, DataUnit.TeraByte, 2) >= 1) ? DataUnit.TeraByte : DataUnit.GigaByte;
            byte decimalNumber = (byte)((dataUnit == DataUnit.TeraByte) ? 2 : 1);
            driveTotalSize = DataUnitConvert.ConvertByteToDataUnit(driveInfo.TotalSize, dataUnit, decimalNumber);
            // متن آیتم نهایی برای اضافه کردن به عنوان آیتم کنترلِ comboBox
            string driveInfoFormatted = "Drive " + driveName;
            if (driveInfo.VolumeLabel != null && driveInfo.VolumeLabel != "")
                driveInfoFormatted += " [" + driveInfo.VolumeLabel + "] ";
            driveInfoFormatted += " - " + driveTotalSize + " ";
            driveInfoFormatted += (dataUnit == DataUnit.TeraByte) ? "TB" : "GB";

            return driveInfoFormatted;
        }
    }

همچنین وقتی که Binding نمیکردم و از متد برای پر کردن اعضای کمبوباکس استفاده میکردم ، هیچ مشکلی نبود .


ببخشید استاد که زیاد شد .
البته اگه مایل بودین ، جواب بدین (یا ندین) . چون شاید بررسی کردنش یه کم وقت بگیره .
تشکر استاد .
 

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

بالا