یک لحظه صبر کنید. میگید Style پیشفرض رو ننوشتم. شما اول همچین کدی رو اجرا کنید تا ببینید Style پیشفرض داره یا نه :
C#:
private void Button_Click(object sender, RoutedEventArgs e)
{
var style = (Style)Application.Current.TryFindResource(typeof(CustomButton));
MessageBox.Show((style == null) ? "null" : "not null");
}
داره، چون not null ئه. بعد بیایید اون دو تا Style رو که در پروژه اول دارید comment کنید (هر دو شون که خطایی هم رخ نده) که ببینید چطور Style پیشفرض غیب میشه و null میشه. حالا اون Style داخل StyleResource.xaml رو uncomment کنید تا Style پیشفرض برگرده. چرا میگید Style پیشفرض ننوشتم؟ پس اون Style داخل StyleResource.xaml چیه؟
خیلی ممنون استاد از جواب کامل تون
بله . مفهومی که از استایل پیش فرض توی ذهنم شکل گرفته بود این بود که استایلی باشه که Setter نداشته باشه که اشتباه بود . متوجه شدم .
اگه اشتباه نکنم ، استایل پیش فرض ، به اولین استایل برای اون نوع داده ای که بنویسیم ، استایل پیش فرض محسوب میشه . درسته؟
شما وقتی یک Style رو به عنوان Style به المنتی معرفی می کنید (مثلا DarkStyle برای Window) اون صرفا Style اش میشه، معنی اش این نیست که Resource های Style ئه بشه جزو Resource های اون Window، منابع شون که ربطی بهم ندارند. اون Window فرزند اون Style نیست که به منابع Style ئه دسترسی داشته باشه.
فرق اینکه Resource ای در اون Window مشخص کنیم یا پروپرتیِ Style ئه اون ویندوز را (به DarkStyle و ...) مشخص و تنظیم کنیم ، چیه؟
من هنوز دقیقا متوجه نشدم که وقتی پروپرتیِ Style ئه Window را به مقدار DarkStyle تنظیم میکنیم ، چجور میشه که کنترل هایی که به عنوان فرزند اون Window تعریف کرده بودیم ، اتوماتیک استایل های داخل Resource ئه DarkStyle را میشناسن و اون استایل ها توی اون کنترل ها بصورت اتوماتیک سِت میشن؟
اینکه به DarkStyle دسترسی دارید صرفا به این خاطر ئه که منابع داخل StyleResource.xaml رو به Application.Resources اضافه کردید.
بله . جریان دسترسی را میدونم .
بالاخره DarkStyle یا باید Style پیشفرض رو داشته باشه یا نداشته باشه. اگر همچین وظیفه ای به عهده DarkStyle نیست نباید Style پیشفرض باشه.
DerivedStyles_3.rar
آها ، این ، DarkStyle هست که به عنوان استایل شناخته میشه؟
یعنی DarkStyle باید استایلِ پیش فرض برای CustomButton را توسط BaseOn برای خودش تعریف کنه . درسته؟
من فکر میکردم باید برای استایل ئه CustomButton ای که درون DarkStyle تعریف کرده بودیم ، مجددا یه استایل پیش فرض برای اون استایلِ CustomButton تعریف کنیم .بعد تعجب میکردم که اون استایلِ CustomButton ، خودش باید به عنوان استایل پیش فرض (مثل کد پروژه ی اول) محسوب بشه اما چرا در پروژه ی دوم برای این استایل ، مجددا باید استایل دیگه ای به عنوان استایل پیش فرض بنویسیم . پس اشتباه میکردم .
اما سئوالی که پیش میاد اینه که همونطور که در بالاتر پرسیده بودم ، پس چجوری میشه که با فراخونی استایلِ DarkStyle (به عنوان استایل Window) ، استایل هایی که در Resource ئه DarkStyle تعریف کرده بودیم ، شناسایی میشن و برای فرزندان Window بکار برده میشن؟
اگه میتونه شناسایی بشه ، پس چرا (مثل کد پروژه ی اول) ، به عنوان استایل پیش فرض نمیتونه بکار ببردش؟
------------------------------------------------------------------
خیلی ممنون از پروژه ای که درست کردین .
این جوری درست کردین که کدها ، عین پروژه ی اول هست . یعنی برخلاف پروژه ی دوم که استایل ها(ی کنترل های مختلف) ، به عنوان Resource در استایل دیگه (DarkStyle و ...) تعریف بشن ، بلکه هر کدوم از استایل های کنترل ها ، کلا در یک فایل xaml (بدون اینکه در Resource ئه استایل دیگه ای قرار بگیرن) ، تعریف میشن . و بجای تعریف 2 استایلِ DarkStyle و LightStyle ، میایم هر کدوم را درون یک فایل xaml مجزا تعریف میکنیم .
حالا این فایل xaml (که شامل استایل های مورد نظرمون هست) را در Resource ئه کنترل والد (مثلا Resource ئه Window) (بدیهی هست توسط MergedDictionaries) الحاق میکنیم (یعنی مثل کد زیر که نوشتید) :
XML:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary x:Name="mysource" Source="DarkStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
هر زمان هم که زمان اجرای برنامه ، در سی شارپ خواستیم استایل را عوض کنیم ، کافی هه Source ئه ResourceDictionary (در MergedDictionaries ئه ResourceDictionary ئه Window) را به اون فایل xaml (که استایل مورد نظرمون را توش تعریف کرده بودیم) تغییر بدیم .
Source ئه ResourceDictionary که یه شی URI میخواد و برای مقداردهی URI هم از دستور زیر پیروی میکنیم :
C#:
new Uri("/نام اسمبلی که معمولا همون نام فضای نام ای که ویندوز اصلی مون توش قرار داره هست;component/نام فایل xaml.xaml", UriKind.RelativeOrAbsolute);
بنابراین در این پروژه ای که دادین ، برای تغییر استایل ، کد زیر میشه :
C#:
private void CustomButton_Click(object sender, RoutedEventArgs e)
{
this.Resources.MergedDictionaries[0].Source = new Uri("/DerivedStyles;component/LightStyle.xaml", UriKind.RelativeOrAbsolute);
}
Few tricks about using a Resource Dictionary in WPF
www.codeproject.com
درست گفتم؟
پیشنهادی ندارم. اما به هر حال XML در جایی استفاده میشه که قراره داده در ساده ترین شکل ممکن و با ابتدایی ترین ویرایشگر ها قابل مشاهده و تغییر باشه، اگر نمیخواهید کسی اونها رو ببینه یا تغییر بده دیگه انتخاب XML منطقی نیست و مزیتی نداره.
پس به نظرتون اصلا نیازی به تامین امنیت فایل ای که تنظیمات را توش ذخیره میکنم ، هست؟
چون خود ویژال استودیو هم اگه اشتباه نکنم ، اطلاعات تنظیمات اش را در فایل xml ذخیره میکنه . درسته؟
حتما یک جای کار اشتباه کرده اید، گزینشی که نیست که بعضی ها رو اجرا کنه و بعضی ها رو نکنه.
من کد زیر را به متد Main در wpf اضافه میکنم :
C#:
public partial class App : System.Windows.Application {
private static System.Threading.Mutex SingleAppExecuteMutex;
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
public static void Main() {
// مشخص میکنه که آیا نخ ای (نخ جاری) که نام Mutex را مشخص میکنه ، مالک اولیه ی این Mutex محسوب بشه یا محسوب نشه .
// نام Mutex ، به عنوان رشته ، در آرگومان دوم متد سازنده ی Mutex مشخص میشه .
// در واقع مشخص میکنه که اولین نخ ای که نام Mutex را مشخص کرد ، اون نخ ، مالک اولیه ی Mutex باشه تا بتونه نخ های دیگه را از این نخ مالک ، مشخص کنه .
bool isThreadNamedMutedOwner = true;
// نام Mutex . این نام بصورت کاملا دلخواه هست و ربطی به نام پروسه ی برنامه نداره ولی ما برای نام با مصماتر ، برابر نام پروسه گرفتیم .
string mutexName = "PoshtibangirTolo";
// مشخص میکنه که آیا نخ جاری ای که Mutex را با نام ای که دادیم ، ایجاد کرد ، آیا مالک اولیه ی این Mutex هست یا اینکه نیست و Mutex ای با این نام ، قبلا توسط نخ دیگه ای ساخته شد؟
// طبق توضیحات این آرگومان ، این فیلد را نباید مقداردهی اولیه کنیم
bool isCreatingFirstMutex;
App.SingleAppExecuteMutex = new System.Threading.Mutex(isThreadNamedMutedOwner, mutexName, out isCreatingFirstMutex);
// اگه مالک اولیه ی این Mutex ، نخ جاری نبود . یعنی اگه یه بار دیگه این برنامه اجرا شد
if (isCreatingFirstMutex == false)
{
// به کاربر پیام بده و برنامه را ببند .
MessageBox.Show("این برنامه ، قبلا اجرا شد .");
App.Current.Shutdown();
}
PoshtibangirTolo.App app = new PoshtibangirTolo.App();
app.InitializeComponent();
app.Run();
}
}
اما وقتی چندین بار برنامه ام را اجرا میکنم ، توی همه ی دفعات اجرا میشه و در اجرای بار دوم به بعد ، نه اون پیام MessageBox.Show را نشون میده و نه از برنامه بیرون میره . در صورتی که همین کد را اگه توی متد OnStartup (که در کلاس App اون را override کردم) بنویسم ، درست کار میکنه .
استاد ، توضیحاتی که برای متغییرهای مربوط به Mutex در کد بالا نوشتم (کامنت ها) ، توضیحات درستی هستن؟
کلا ، درک ام از Mutex (از جنبه ی اینکه برنامه فقط یکبار اجرا بشه) ، طبق توضیحاتی که در کد بالا نوشتم ، درسته؟
و اینکه اون متغییرِ مربوط به Mutex (متغییر سراسری استاتیک با نام SingleAppExecuteMutex) را نباید بصورت شی گرا (غیرِ استاتیک) تعریف کنیم . درسته؟
چون چندین نخ از چندین پروسه ی مختلف باید بهش دسترسی داشته باشن . درست میگم؟
بنابراین ، توی هیچ رویدادی هم نمیشه Dispose اش کرد . چون در پروسه و نخ های دیگه باید بهش دسترسی داشته باشن .
درست میگم؟
ببخشید طولانی شد استاد .
تشکر استاد