PDA

View Full Version : مقالات آموزشی و کاربردی در زمینه WPF



Mahdi.Kiani
دوشنبه 23 دی 1387, 23:07 عصر
با سلام
در این تاپیک دوستان می توانند، مقالات مختلف آموزشی و کاربردی در زمینه WPF را قرار دهند.
توجه به ذکر چند نکته می تواند در روند کاربردی بودن این تاپیک موثر باشد.
1) از قرار دادن مقالاتی که جنبه تئوریک دارد، در این تاپیک خود داری کنید و آن ها در بخش های مربوطه ارسال نمایید.
2) سعی شود، مطالب به صورت روان و ساده بیان شود.
3) سعی شود، مطالب همراه با نمونه کد و حتی المقدور همراه با نمونه برنامه تست شده و مطمئن (پیوست شده با مقاله) باشد.
4)مطالب نوشته شده توسط خودتان و یا ترجمه شده باشد. از فرستادن پست هایی که صرفا لینک به یک مقاله دیگر می باشد، در این تاپیک خود داری نمایید.
5)چنانچه مطالب ترجمه، یک مقاله دیگر می باشد، لینک منبع را نیز در پایان مقاله ذکر نمایید.
6)حتی المقدور، مقالات را در قالب pdf در پایان پست خود ضمیمه کنید.
7)هنگام لینک دادن به عکس های آپلود شده در سایتهای دیگر، ساز عکس را متناسب با صفحه در نظر بگیرید.
8)عنوان مناسبی برای پست های خود انتخاب کنید، تا در فهرست بندی مطالب در آینده، مشکلی پیش نیاید.

با تشکر./
مهدی کیانی

پ و:
به لطف خدا، تاپیک مربوط به آموزش WPF نیز در آینده ای نزدیک مجددا ادامه داده خواهد شد.

Mahdi.Kiani
دوشنبه 23 دی 1387, 23:14 عصر
نمونه کد : دارد
فرمت pdf : دارد

مقدمه:
در ادامه مقالات آموزشي WPF ، در اين مقاله، نحوه ساخت دکمه هاي سفارشي را در تکنولوژي WPF به صورت ساده و روان آموزش خواهم داد. براي فهم مطالب اين مقاله و استفاده و بهره وري از آن، نيازمند است تا خواننده با مفاهيم کلي WPF از جمله تعريف Brush ها، Style ها، نحوه ايجاد Resource ها و استفاده از آن ها، آشنايي داشته باشد. در اين مقاله از Microsoft Visual Studio.Net 2008 ، نسخه Professional به روز رساني شده با Service Pack1 استفاده شده است.

ايجاد پروژه:
در جهت سهولت ياد گيري کاربر، سعي کرده ام که به ساده ترين شکل ممکن، اين آموزش را در اختيار شما قرار دهم.
يک پروژه از نوع WPF Application ايجاد کنيد. نام دلخواهي را براي آن انتخاب نماييد. من در پروژه خودم آن را CustomButton ناميدم. در پنجره Solution Explorer بر روي Window1 راست کليلک نماييد و گزينه View Designer را کليک نماييد. مطمئن شويد که در قسمت کدهاي XAML مربوط به Window1 هستيد.
کد XAML مربوط به Window1 به صورت زير مي باشد:






<Window x:Class="CustomButton.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>

</Grid>
</Window>




قبل ازتعريف <Grid> کد هاي زير را وارد نماييد.



<Window.Resources>

</Window.Resources>



کدهايي که براي ساخت دکمه خواهيم نوشت، در قسمت مربوط به Resource هاي Window1 قرار خواهند گرفت.(به نکته 1 آخر مقاله توجه نماييد.)
براي ساخت دکمه سفارشي يک Style جديد ايجاد خواهيم کرد، و سپس با تعريف خاصيت Template، شکل و حالت دلخواه خود را به دکمه خواهيم داد. اما قبل از ايجادStyle جديد، تعاريف مربوط به Brush ها را خواهيم آورد. در قسمت Resource ها، کد هاي زير را وارد نماييد.






<!-- ************************************************** * -->
<!-- DefaultBackBrush -->
<!-- ************************************************** * -->
<LinearGradientBrush x:Key="DefaultBackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FF2e2e2e" Offset="0"/>
<GradientStop Color="#FF606984" Offset="0.025"/>
<GradientStop Color="#FF3a404d" Offset="0.475"/>
<GradientStop Color="#FF2e2e2e" Offset="0.5"/>
</LinearGradientBrush>
<!-- ************************************************** * -->







<!-- ************************************************** * -->

<!-- HighLightBackBrush -->
<!-- ************************************************** * -->
<LinearGradientBrush x:Key="HighLightBackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FF2e2e2e" Offset="0"/>
<GradientStop Color="#FF8a9ac6" Offset="0.025"/>
<GradientStop Color="#FF3a404d" Offset="0.475"/>
<GradientStop Color="#FF2e2e2e" Offset="0.5"/>
</LinearGradientBrush>
<!-- ************************************************** * -->

<!-- ************************************************** * -->
<!-- DisabledBackBrush -->
<!-- ************************************************** * -->
<LinearGradientBrush x:Key="DisabledBackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FF3f3c3c" Offset="0"/>
<GradientStop Color="#FF74798a" Offset="0.025"/>
<GradientStop Color="#FF5e6168" Offset="0.475"/>
<GradientStop Color="#FF3f3c3c" Offset="0.5"/>
</LinearGradientBrush>
<!-- ************************************************** * -->


<!-- ************************************************** * -->
<!-- IsDefaultedBorderColor -->
<!-- ************************************************** * -->
<SolidColorBrush x:Key="IsDefaultedBorderColor" Color="#FF2362ff"/>
<!-- ************************************************** * -->

<!-- ************************************************** * -->
<!-- IsCancelBorderColor -->
<!-- ************************************************** * -->
<SolidColorBrush x:Key="IsCancelBorderColor" Color="#FFFF0000"/>
<!-- ************************************************** * -->


<!-- ************************************************** * -->
<!-- FocusedBorderColor -->
<!-- ************************************************** * -->
<SolidColorBrush x:Key="FocusedBorderColor" Color="#FFbdccef"/>
<!-- ************************************************** * -->


<!-- ************************************************** * -->
<!-- DisabledForeColor -->
<!-- ************************************************** * -->
<SolidColorBrush x:Key="DisabledForeColor" Color="#FFc3c3c3"/>
<!-- ************************************************** * -->

<!-- ************************************************** * -->
<!-- DefaultForeColor -->
<!-- ************************************************** * -->
<SolidColorBrush x:Key="DefaultForeColor" Color="#FFFFFFFF"/>
<!-- ************************************************** * -->


<!-- ************************************************** * -->
<!-- DefaultBorderColor -->
<!-- ************************************************** * -->
<SolidColorBrush x:Key="DefaultBorderColor" Color="#FF000000"/>
<!-- ************************************************** * -->


<!-- ************************************************** * -->
<!-- ButtonRotater -->
<!-- ************************************************** * -->
<RotateTransform x:Key="ButtonRotater" Angle="180" />
<!-- ************************************************** * -->



امي که براي هر آيتم تعيين گرديده است، به خوبي بيانگر عملکر آن آيتم نيز ميباشد. به عنوان مثال، آيتم DisabledForeColor ، رنگ مربوط به دکمه را، در زماني که دکمه، در حالت Disable مي باشد، مشخص مي نماييد. به آيتم ButtonRotater توجه نماييد. اين آيتم يک RotateTransform مي باشد، که در زماني که دکمه توسط کاربر فشرده مي شود، باعث چرخش 180 درجه اي Border مربوط به دکمه مي شود که اين امر باعث مي شود، فشرده شدن دکمه توسط کاربر به خوبي مشخص شود و هنگام فشرده شدن، ظاهر دکمه با حالت پيش فرض آن متفاوت باشد. (قبل از خواند ادامه مطالب به نکته شماره 2 آخر مقاله توجه کنيد.)
حال تعاريف مربوط به Style دکمه را در ادامه کد هاي فوق بنويسيد:





<!-- ************************************************** * -->
<!-- CustomButton -->
<!-- ************************************************** * -->
<Style TargetType="{x:Type Button}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Foreground" Value="{StaticResource DefaultForeColor}"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid
Margin="0"
Background="Transparent"
>
<Border
x:Name="ButtonBorder"
Background="{StaticResource DefaultBackBrush}"
BorderBrush="{StaticResource DefaultBorderColor}"
CornerRadius="4"
BorderThickness="1"
Padding="2"
Margin="0"/>

<ContentPresenter
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"


/>

</Grid>

<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter
TargetName="ButtonBorder"
Property="BorderBrush"
Value="{StaticResource FocusedBorderColor}"/>
</Trigger>

<Trigger Property="IsDefaulted" Value="True">
<Setter TargetName="ButtonBorder" Property="BorderBrush" Value="{StaticResource IsDefaultedBorderColor}"/>
</Trigger>
<Trigger Property="IsCancel" Value="True">
<Setter TargetName="ButtonBorder" Property="BorderBrush" Value="{StaticResource IsCancelBorderColor}"/>
</Trigger>

<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonBorder" Property="Background" Value="{StaticResource HighLightBackBrush}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonBorder" Property="Background" Value="{StaticResource DefaultBackBrush}"/>
<Setter TargetName="ButtonBorder" Property="LayoutTransform" Value="{StaticResource ButtonRotater}"/>
</Trigger>

<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ButtonBorder" Property="Background" Value="{StaticResource DisabledBackBrush}"/>
<Setter Property="Foreground" Value="{StaticResource DisabledForeColor}"/>

</Trigger>

</ControlTemplate.Triggers>

</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ************************************************** * -->



همانطور که در مقدمه مقاله آورده شد، خواننده بايد با مطالب تعريف Style ها، Trigger ها و ... آشنايي داشته باشد. به همين دليل در اين موارد توضيحي نخواهم داد. فقط به ذکر چند نکته در اکتفا خواهم کرد.

هنکام تعاريف Style ها معمولا يک Key براي آن در نظر خواهيم گرفت. که با دستور زير مشخص مي شود:




x:Key="[style key name]"



اما در Style فوق، من کليدي براي آن تعريف نکرده ام. اما تعريفي با عنوان




TargetType="{x:Type Button}"


را آورده ام. در واقع با اين عمل، Style مربوطه به تمامي دکمه هاي درون Window1 (و اگر Style در app.xaml تعريف شده باشد؛ به تمام دکمه هاي به کار رفته در همه Window هاي برنامه) اعمال خواهد شد و نيازي به مقدار دهي خاصيت Style دکمه ها، نخواهد بود.

براي کنترل رفتار دکمه، در حالت هاي مختلف، از Trigger ها و خواص مربوطه استفاده شده است. به عنوان مثال با تعريف Trigger زير




<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ButtonBorder" Property="Background" Value="{StaticResource DisabledBackBrush}"/>
<Setter Property="Foreground" Value="{StaticResource DisabledForeColor}"/>



رنگ پس زمينه و پيش زمينه دکمه در حالتي که دکمه به صورت Disable ميباشد را به رنگ هاي متناسب و تعريف شده در بالاي Style نسبت مي دهد.


نکته 1:
چنانچه بخواهيد، از دکمه سفارشي، در همه Windows هاي برنامه استفاده نماييد، تعاريف مربوطه را در قسمت Resource ها فايل App.xaml قرار دهيد. علاوه براين مي توانيد يک Resource Dictionary ايجاد کرده و توسط دستور Merge Dictionaries ، آن را به Resource هاي برنامه اضافه نماييد. من در برنامه هاييم اغلب ازاين حالت استفاده مي کنم به اين دليل که قابليت Reusable بودن بالايي را به همراه خواهد داشت.
نکته 2:
مي توانيد، براي ايجاد Brush ها نيز يک Resource Dictionary در نظر بگيريد، تعاريف مربوط به Brush ها و عناصري را که در قسمت هاي مختلف ساخت کنترل هاي سفارشي شما، مشترک مي باشند را در آن نوشته و سپس آن را هنکام ايجاد کنترل هاي سفارشي، به آنها اضافه نماييد. اين عمل در هنگام تعريف و ساخت Style ها که در آن ها اغلي عناصر از رنگ هاي يکساني بهره مي برند، باعث صرفه جويي در وقت و کئ نويسي سريع و همچنين خوانا تر مي شود و از نوشتن کد هاي تکراري جلوگيري مي کند.

نکته3:
دقت کنيد که زماني که يک Style جديد براي يکي از عنصرهاي موجود در Visual Studio تعريف مي نماييد، تمامي کنترل آن عنصر در اختيار شما خواهد بود و در واقع مي توانيد عناصر و کنترل هاي جديد با ظاهر دلخواه خود را به وجود آوريد.

تبريک!!!
شما اولين کنترل سفارشي خود را با موفقيت ايجاد کرديد و مي توانيد از ان در برنامه هايتان استفاده نماييد.
شکل زير، نمونه هايي از دکمه ايجاد شده را در حالت هاي مختلف نشان مي دهد.





http://i41.tinypic.com/2u61sok.jpg




اميدوارم که اين مقاله نيز، کمکي در جهت ارتقاء سطح دانش شما درزمينه برنامه نويسي و استفاده از تکنولوژي WPF در برنامه هايتان نمايد.
خواهشمند است نظرات ،انتقادات و پيشنهادات خود را در رابطه با مقاله فوق از طريق ايميل با بنده در ميان گذاريد.

Amir Oveisi
شنبه 26 بهمن 1387, 05:40 صبح
نمونه کد : دارد
pdf : فعلا ندارد

مقدمه:
چند روز پیش دیدم که خیلیا دنبال Messagebox‌ فارسی و Customize‌شده هستند، گفتم یکی با WPF بنویسم تا دوستان با WPF‌و قابلیت هاش بیشتر آشنا بشن و ببینن چقدر لذت بخشه کار با WPF!

توضیحات:
این MessageBox هم میتونه فارسی باشه هم انگلیسی (پیش فرضش انگلیسی هست) و تقریبا همه قابلیت های Messagebox استاندارد رو داره + امکان تغییر Style‌همه قسمت هاش.

چند نمونه عکس ازش:

http://h2qd8a.bay.livefilestore.com/y1pC3lNR9MSqfBhINmvpPn8b0AElEnFH_0xcpnezzYLlmsWVNw ZIS2Slc7lF19i86tU0FX7Zkkh1X3PfRYWdGcSHf_cI1RyvXO1/7eb0b62a2b.jpg?psid=1

http://h2qd8a.bay.livefilestore.com/y1ptCYxU4vmHJVqSDb0T2dD--kLjOTZ0sWZMSxfaDU2do79Owy9BQOwJDSl0pi780KRFenXhzVf g-fYJ83KDuxt96WFCgGUxdxN/0a187b375c.jpg?psid=1

برای تغییر اسکین :


WPFMessageBox.BackgroundBrush
WPFMessageBox.BorderBrush
WPFMessageBox.ButtonsStyle
WPFMessageBox.TitleTextStyle
WPFMessageBox.MessageTextStyle

قبل از Show کردن، با مقدار دهی به هر کدوم از ویژگی های بالا میتونین اسکین اونو تغییر بدید.

http://h2qd8a.bay.livefilestore.com/y1prYe8yhTDgVGgay-FJQOBVCRsnczsuH1emPPubtMx0sLUMV_gExWTuXGromg8Qyr8d HJN3RWaywYiYZUOB0DzSdEUMO-q3NdU/51fcddba31.jpg?psid=1

برای فارسی کردن :


WPFMessageBox.IsPersian = true;

و بعد Show میکنید.

http://h2qd8a.bay.livefilestore.com/y1pduuI20Tle1zt2swEcu60F-2d17MEd45BqUN_-ixMGKRglCj5oDT2pJ5AObHJe07RxJpqaVpYvjK36uczyYPE49D fkPBBAT2t/6665737380.jpg?psid=1

اینا هم دو تا style‌ مختلف که کد XAML‌ ش تو فایل ضمیمه هم موجوده:

http://h2qd8a.bay.livefilestore.com/y1pOoN9VNOZqz8TlkriZjInPvr5ha3DumyRAkwuv_Zj6y3Vf0r E0l5bWA-krD1xmTAFGpOzkSxH5hoLd0UscwnoqYLen4xR8gEr/5d024dcf2f.jpg?psid=1

http://h2qd8a.bay.livefilestore.com/y1pXKLFWleSe0czpVY24pRLlXbrsgDa8PfT-4pOzTp9aGamAcbnq68XUis8fL8QjreUAcVW30AFIN_K7gkmFkG EgVwovDIlFU8k/0057a75c13.jpg?psid=1

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

دانلود سورس (http://www.4shared.com/file/87580071/1f43db6d/MessegeBoxSample.html)

موفق باشید
اویسی

Amir Oveisi
دوشنبه 01 شهریور 1389, 05:06 صبح
نمونه کد: دارد
pdf:فعلا ندارد

مقدمه

یک نمونه برنامه ساده با استفاده از الگوی طراحی MVVM و Entity Framework در WPF برای کار با دیتابیس، که اعمال Select, Insert, Update و Delete را با استفاده از ساختار های الگوی MVVM انجام می دهد.
در صورتی که با الگوی MVVM آشنا نیستید مطالبی را که جناب آقای نصیری که در سایت شخصیشون (http://vahidnasiri.blogspot.com/2010/04/m-v-vm.html) در مورد MVVM قرار داده اند، مطالعه نمایید.


http://h2qd8a.bay.livefilestore.com/y1p6C5sewZGqK1JN9QRnelj8qxedbbVonAFjyvCXvyfGCdX8_j vxgzyqAsoCfcSb9rmbBBANCyFWfTAbm8XqgpRkYk7Lb7zRJrM/4.jpg?psid=1


توضیحات

شمای کلی برنامه بصورت زیر است که نمایانگر لایه های مختلف MVVM می باشد.

http://h2qd8a.bay.livefilestore.com/y1pyx7NfzUEPXnhtSrLtfDrphLjsPFceOmzJHGY9M8xMaYb9a7 4LRKScGHSsDdjwXoI00T7byzAmWD3zXMub7YWnOhNtEOfwx62/1.jpg?psid=1

لایه DataAccess:
شامل فایل دیتابیس است که یک جدول به نام Employee، دارای دو فیلد نام و ID می باشد. در این لایه می توان از انواع data source ها نظیر فایل های xml و ... نیز استفاده نمود.
البته توجه داشته باشید که چنین لایه ای در الگوی MVVM نامبرده نشده است اما برای شفافیت بیشتر کدنویسی معمولا برنامه نویسانی که با MVVM سر و کار دارند، چنین اقدامی می کنند!

لایه Model:
در این لایه باید مدلی از داده ای که در DataAccess وجود دارد، ایجاد کنیم. با استفاده از Entity Framework، یک Entity Data Model خواهیم داشت که به عنوان مدلی آماده از لایه DataAccess می توان از آن نام برد. زیرا کدهای مورد نیاز برای ایجاد کلاس های Model را بصورت خودکار تولید می کند.

[assembly: EdmSchemaAttribute()]

namespace MVVM_EntityFramework.Model
{
#region Contexts

/// <summary>
/// No Metadata Documentation available.
/// </summary>
public partial class EmployeeRepository : ObjectContext
{
#region Constructors

/// <summary>
/// Initializes a new EmployeeRepository object using the connection string found in the 'EmployeeRepository' section of the application configuration file.
/// </summary>
public EmployeeRepository() : base("name=EmployeeRepository", "EmployeeRepository")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}

/// <summary>
/// Initialize a new EmployeeRepository object.
/// </summary>
public EmployeeRepository(string connectionString) : base(connectionString, "EmployeeRepository")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}

/// <summary>
/// Initialize a new EmployeeRepository object.
/// </summary>
public EmployeeRepository(EntityConnection connection) : base(connection, "EmployeeRepository")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}

#endregion

#region Partial Methods

partial void OnContextCreated();

#endregion

#region ObjectSet Properties

/// <summary>
/// No Metadata Documentation available.
/// </summary>
public ObjectSet<Employee> Employees
{
get
{
if ((_Employees == null))
{
_Employees = base.CreateObjectSet<Employee>("Employees");
}
return _Employees;
}
}
private ObjectSet<Employee> _Employees;

#endregion
#region AddTo Methods

/// <summary>
/// Deprecated Method for adding a new object to the Employees EntitySet. Consider using the .Add method of the associated ObjectSet&lt;T&gt; property instead.
/// </summary>
public void AddToEmployees(Employee employee)
{
base.AddObject("Employees", employee);
}

#endregion
}


#endregion

#region Entities

/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmEntityTypeAttribute(NamespaceName="dbModel", Name="Employee")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Employee : EntityObject
{
#region Factory Method

/// <summary>
/// Create a new Employee object.
/// </summary>
/// <param name="id">Initial value of the id property.</param>
public static Employee CreateEmployee(global::System.Int32 id)
{
Employee employee = new Employee();
employee.id = id;
return employee;
}

#endregion
#region Primitive Properties

/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false , IsNullable=true)]
[DataMemberAttribute()]
public global::System.String Name
{
get
{
return _Name;
}
set
{
OnNameChanging(value);
ReportPropertyChanging("Name");
_Name = StructuralObject.SetValidValue(value, true);
ReportPropertyChanged("Name");
OnNameChanged();
}
}
private global::System.String _Name;
partial void OnNameChanging(global::System.String value);
partial void OnNameChanged();

/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int32 id
{
get
{
return _id;
}
set
{
if (_id != value)
{
OnidChanging(value);
ReportPropertyChanging("id");
_id = StructuralObject.SetValidValue(value);
ReportPropertyChanged("id");
OnidChanged();
}
}
}
private global::System.Int32 _id;
partial void OnidChanging(global::System.Int32 value);
partial void OnidChanged();

#endregion

}

#endregion

}


لایه View:

برای View یک user control به نام EmployeeListView داریم که دارای یک DataGrid برای نمایش اطلاعات مشتریان، دو TextBox برای ورود اطلاعات و سه Button برای اعمال Insert و Update و Delete است. برای این کار از Command استفاده شده است.
برای کد XAML آن به این شکل است: (به Binding ها دقت کنید)

<UserControl x:Class="MVVM_EntityFramework.View.EmployeeListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">


<Grid>
<Grid.InputBindings>
<KeyBinding Key="I" Modifiers="Control" Command="{Binding InsertCommand}"/>
<KeyBinding Key="U" Modifiers="Control" Command="{Binding UpdateCommand}"/>
<KeyBinding Key="D" Modifiers="Control" Command="{Binding DeleteCommand}"/>
</Grid.InputBindings>
<StackPanel Margin="5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<DataGrid ItemsSource="{Binding AllEmployees}" AutoGenerateColumns="False" Margin="5" AlternatingRowBackground="#FFD2CECE" Background="{x:Null}" HorizontalGridLinesBrush="#FFC6C6C6" VerticalGridLinesBrush="#FFC6C6C6" BorderBrush="#FFA3A3A3">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="ID">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding id}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel Orientation="Horizontal" Margin="5" DataContext="{Binding NewEmployee}">
<TextBlock Text="Name:" Margin="5"/>
<TextBox Width="70" Margin="5" Text="{Binding Path=Name, Mode=OneWayToSource}"/>
<TextBlock Text="ID:" Margin="5"/>
<TextBox Width="70" Margin="5" Text="{Binding Path=id, Mode=OneWayToSource}"/>
</StackPanel>
<StackPanel Margin="5" Orientation="Horizontal">
<Button Content="Insert" Command="{Binding InsertCommand}" Margin="5" Width="50"/>
<Button Content="Update" Command="{Binding UpdateCommand}" Margin="5" Width="50"/>
<Button Content="Delete" Command="{Binding DeleteCommand}" Margin="5" Width="50"/>
</StackPanel>
<TextBlock Margin="10" Foreground="DarkGray" Text="Shortcut Keys: Insert = Ctrl+I , Update = Ctrl+U , Delete = Ctrl+D" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</UserControl>


لایه ViewModel:
بطور کلی برای هر View یک ViewModel باید باشد تا منطق آن را تولید نماید.
در اینجا ما دو View داریم، user control و main wondow.
کار main window نمایش View های دیگر در داخل خود است به همین دلیل جزو لایه View محسوب نمی شود. اما برای کنترل منطق آن باید یک ViewModel برای آن نیز داشته باشیم تا بتوانیم قابلیت نمایش View های مختلف در داخل آن را بوجود بیاوریم.
کلاس ViewModelBase را نیز برای داشتن یک چارجوب کلی برای تمام ViewModel ایجاد می کنیم و همه ViewModel های موجود از آن ارث خواهند برد.

ViewModelBase

namespace MVVM_EntityFramework.ViewModel
{
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
protected ViewModelBase()
{
}

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}

public void Dispose()
{
this.OnDispose();
}

protected virtual void OnDispose()
{
}
}
}


MainWindowViewModel

namespace MVVM_EntityFramework.ViewModel
{
public class MainWindowViewModel : ViewModelBase
{
readonly EmployeeRepository _employeeRepository;

ObservableCollection<ViewModelBase> _viewModels;

public MainWindowViewModel()
{
_employeeRepository = new EmployeeRepository();
EmployeeListViewModel viewModel = new EmployeeListViewModel(_employeeRepository);
this.ViewModels.Add(viewModel);
}

public ObservableCollection<ViewModelBase> ViewModels
{
get
{
if (_viewModels == null)
{
_viewModels = new ObservableCollection<ViewModelBase>();
}
return _viewModels;
}
}
}
}



EmployeeListViewModel
به تعاریف Command ها توجه نمایید.


namespace MVVM_EntityFramework.ViewModel
{
class EmployeeListViewModel : ViewModelBase
{


readonly EmployeeRepository _employeeRepository;

RelayCommand _insertCommand;
RelayCommand _updateCommand;
RelayCommand _deleteCommand;

public Employee NewEmployee { get; set; }

public ObservableCollection<Employee> AllEmployees
{
get;
private set;
}

public EmployeeListViewModel(EmployeeRepository employeeRepository)
{
if (NewEmployee == null)
NewEmployee = new Employee();
if (employeeRepository == null)
{
throw new ArgumentNullException("employeeRepository");
}
_employeeRepository = employeeRepository;
var tmp = from o in employeeRepository.Employees

select o;
this.AllEmployees = new ObservableCollection<Employee>(tmp.ToList());
}

protected override void OnDispose()
{
this.AllEmployees.Clear();
}



public ICommand InsertCommand
{
get
{
if (_insertCommand == null)
{
_insertCommand = new RelayCommand(param => this.InsertCommandExecute(), param => this.InsertCommandCanExecute);
}
return _insertCommand;
}
}

public ICommand UpdateCommand
{
get
{
if (_updateCommand == null)
{
_updateCommand = new RelayCommand(param => this.UpdateCommandExecute(), param => this.UpdateCommandCanExecute);
}
return _updateCommand;
}
}

public ICommand DeleteCommand
{
get
{
if (_deleteCommand == null)
{
_deleteCommand = new RelayCommand(param => this.DeleteCommandExecute(), param => this.DeleteCommandCanExecute);
}
return _deleteCommand;
}
}

void InsertCommandExecute()
{
Employee emp = Employee.CreateEmployee(NewEmployee.id);
emp.Name = NewEmployee.Name;
_employeeRepository.Employees.AddObject(emp);
_employeeRepository.SaveChanges();
var tmp = from o in _employeeRepository.Employees

select o;
this.AllEmployees = new ObservableCollection<Employee>(tmp.ToList());
OnPropertyChanged("AllEmployees");
}


bool InsertCommandCanExecute
{
get
{
var tmp = from o in _employeeRepository.Employees
where o.id == NewEmployee.id
select o;
if (tmp != null && tmp.Count() > 0)
{
return false;
}
return true;
}
}

void UpdateCommandExecute()
{
var tmp = from o in _employeeRepository.Employees
where o.id == NewEmployee.id
select o;
tmp.First().Name = NewEmployee.Name;
_employeeRepository.SaveChanges();
var tmp2 = from o in _employeeRepository.Employees
select o;
this.AllEmployees = new ObservableCollection<Employee>(tmp2.ToList());
OnPropertyChanged("AllEmployees");
}


bool UpdateCommandCanExecute
{
get
{
var tmp = from o in _employeeRepository.Employees
where o.id == NewEmployee.id
select o;
if (tmp != null && tmp.Count() > 0)
{
return true;
}
return false;
}
}

void DeleteCommandExecute()
{
var tmp = _employeeRepository.Employees.Single(o => o.id == NewEmployee.id);
_employeeRepository.Employees.DeleteObject(tmp);
_employeeRepository.SaveChanges();
var tmp2 = from o in _employeeRepository.Employees
select o;
this.AllEmployees = new ObservableCollection<Employee>(tmp2.ToList());
OnPropertyChanged("AllEmployees");
}


bool DeleteCommandCanExecute
{
get
{
var tmp = from o in _employeeRepository.Employees
where o.id == NewEmployee.id
select o;
if (tmp != null && tmp.Count() > 0)
{
return true;
}
return false;
}
}
}
}


نکته :
فایل App.cs را بصورت زیر تغییر می دهیم تا در زمان لود برنامه، محتوی main window را مشخص کنیم. main window قابلیت نمایش هر ViewModel ای را دارد و هر ViewModel دارای یک View است که در نهایت آن View دز main window نمایش داده می شود.

namespace MVVM_EntityFramework
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
var viewModel = new MainWindowViewModel();
window.DataContext = viewModel;
window.Show();
}

}
}

دقت کنید که در بخش XAML کلاس App قسمت مربوط به StaurtupUri را حذف کنید.

و در آخر، کد XAML مربوط به MainWindow به شکل زیر خواهد بود:
(به Resource های آن و Binding مربوط به ItemsSource دقت نمایید)


<Window x:Class="MVVM_EntityFramework.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MVVM_EntityFramework.ViewModel"
xmlns:vw="clr-namespace:MVVM_EntityFramework.View"
Title="MainWindow" Height="350" Width="525" Background="#FFBCB4B4">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:EmployeeListViewModel}">
<vw:EmployeeListView />
</DataTemplate>

</Window.Resources>
<Grid Margin="4">
<Border BorderBrush="#FFA3A0A0" BorderThickness="1" CornerRadius="5">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFD6D2D2" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<ItemsControl ItemsSource="{Binding ViewModels}" Margin="4" />
</Border>
</Grid>
</Window>

نتیجه:
MainWindow را به گونه ای طراحی کردیم که قادر به نمایش دادن ViewModel های مختلف باشد. هر ViewModel نیز یک منطق است که داده ها را از Model (یا DataAccess) گرفته و با استفاده از یک View، در MainWindow نمایش می دهد. توجه داشته باشید که هیچ کدام از فایل های XAML، دارای کدنویسی در Code-behind خود نیستند (به جز کد های پیش فرض خودشان) و این امر سبب می شود به راحتی بتوانید View های خود را به دلخواه و بدون نگرانی از به هم ریختن کدها تغییر دهید. تنها کار لازم، اعمال Binding ها در کد XAML است!
برای پرسش، بحث و تبادل نظر به لینک زیر مراجعه کنید:
http://barnamenevis.org/forum/showthread.php?t=196333

ali.aghdam
سه شنبه 16 شهریور 1389, 10:35 صبح
کد دارد
PDF ندارد
منبع : Aliaghdam.blogspot.com (http://aliaghdam.blogspot.com/2010/09/wpf.html)

در بعضی برنامه های بنا به دلایلی برای توسعه دهنده هیچ اهمیتی ندارد که کاربر چند نمونه از برنامه را اجرا کند ولی اگر زمانی اجرای برنامه محدود بشود (به یک فرم )،برای این جور برنامه ها 3 نوع سناریو وجود دارد که به شرح آنها می پردازیم.
اولین سناریو را می توان نرافزاری در نظر گرفت که فقط یک نمونه از آن اجرا می شود (فقط یک پنجره اصلی) و می تواند پارامتر های را در هنگام اجرا بپذیرد به طور مثال برنامه ی مثل ویندوز مدیا پلیر و سناریو دیگر این است که در واقع از نرم افزار فقط یک نمونه اجرا می شود ولی دارای تعدادی پنجره مختلف است (Document Base) از این برنامه ها می توان MS Word رو مثال زد که کاربر شاید فکر بکند چند نمونه از برنامه را اجرا کرده در صورتی که فقط یک نمونه از برنامه اجرا شده و پنجره های مختلف را مدیریت می کند که از افزوده شدن سربار اضافی به سیستم جلوگیری می کند و سناریو آخری که می توان در نظر داشت برنامه ای که فقط یک نمونه از ان اجرا می شود (فقط یک پنجره اصلی) و هیچ پارامتر ورودی نمی پذیرد.البته سناریوهای دیگری را نیز می توان نام برد ولی ما به همین سه سناریو اکتفا می کنیم


برای پیاده سازی این قبیل نرم افزار ها راه حل های زیادی وجود دارد که هر کدام کم وبیش این قابلیت را به ما می دهند ولی ما قصد بررسی روشی رو داریم که در آن بتوان هر سه سناریو را پیاده سازی کرد


مثلا برای این منظور از Mutex استفاده می کنند که در آن نمی توان سناریویی اول و دوم را پیاده سازی کرد و یا پردازش ها بررسی می کنند که باز هم شامل سناریویی 1و2 نمی شود


public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// Get Reference to the current Process
Process thisProc = Process.GetCurrentProcess();
// Check how many total processes have the same name as the current one
if (Process.GetProcessesByName(thisProc.ProcessName). Length > 1)
{
// If ther is more than one, than it is already running.
MessageBox.Show("Application is already running.");
Application.Current.Shutdown();
return;
}

base.OnStartup(e);
}
}
با ارائه شدن WPF توسط مایکرسافت برای این منظور در C#‎ هیچ تمهیداتی پیش بینی نشده بود ولی تیم توسعه VB.NET این امکان را فراهم کرده بودند که ما نیز قصد استفاده از آن را در C#‎ داریم

در VB.NET در فضای نام کلاسی وجود دارد به نام http://vahid.nasiri.googlepages.com/weblink.gifMicrosoft.VisualBasic.ApplicationServic es (http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.aspx) که این امکان را برای ما فراهم می کندو ما با مشتق کردن کلاسی از این کلاس می توانیم موفق به پیاده سازی هر سه سناریو بشویم.
این کلاس سه عضو را پیاده سازی می کند که از آنها برای این منظور استفاده می کنیم



http://vahid.nasiri.googlepages.com/weblink.gifIsSingleInstance (http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.windowsf ormsapplicationbase.issingleinstance.aspx) : یک خصیصه است و مشخص می کند که آیا از این برنامه فقط یک نمونه اجرا بشود یا نه؟ که ما آن را در سازنده کلاس با true مقدار دهی می کنیم.
http://vahid.nasiri.googlepages.com/weblink.gifOnStartup (http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.windowsf ormsapplicationbase.onstartup.aspx) : این رویداد زمانی رخ می دهد که اولین نمونه از برنامه در حال ساخته شدن است و می توانیم به آرگومان های ورودی دسترسی داشته باشیم( StartupEventArgs ).
http://vahid.nasiri.googlepages.com/weblink.gifOnStartupNextInstance (http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.windowsf ormsapplicationbase.onstartupnextinstance.aspx) : همانطور که از نامش پیداست این زمانی رخ می دهد که نمونه دیگری از برنامه در حال ساخته شدن باشد.در این رویداد نیز می توان به آرگومان های ورودی دسترسی داشته باشیم ( StartupNextInstanceEventArgs )

خوب برای این که بتوانید به این کلاس دسترسی پیدا کنید با ید یک ارجاع به اسمبلی Microsoft.VisuaBasic.dllدر پروژه داشته باشید.

در اینجا ما سناریوی دوم را بررسی می کنیم که مشکلترین آنهاست و توسعه دهندگان اکثرا با پیاده سازی آن مشکل دارند
ابتدا یک کلاس را از WindowsFormsApplicationBase مشتق می کنیم و پیاده سازی زیر را انجام می دهیم


using Microsoft.VisualBasic.ApplicationServices;

public class SingleInstanceManager : WindowsFormsApplicationBase
{
public App app { get; private set; }

public SingleInstanceManager()
{
this.IsSingleInstance = true;
}

protected override bool OnStartup(StartupEventArgs e)
{
// First time app is launched
app = new App();
app.Run();

return false;
}

protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
app.MyWindow.Activate();

app.ProcessArgs(eventArgs.CommandLine.ToArray(), false);

}
}
این کلاس یک نمونه از شی Application مورد نظر ما را نگهداری می کند (App) و در رویداد OnStartup یک نمونه از شی Application ساخته می شود و در رویداد OnStartNextInstance پنجره اصلی برنامه با نام MyWindow فعال می شود و سپس آرگومان های ورودی پردازش می شوند .توجه کنید که این قاعده کلی است و اگر این قسمت را متوجه شوید 70% راه را طی نموده اید.
حال ما قسمت کد نویسی شی Application را بررسی می کنیم (نه کد XAML)


/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public MainWindow MyWindow { get; private set; }

protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);

// Create and show the application's main window
MyWindow = new MainWindow();
MyWindow.Show();

//cache Arguments
ProcessArgs (e.Args, true);
}

//cache Arguments
public void ProcessArgs(string[] args, bool firstInstance)
{
if (firstInstance )
{
MyWindow.ProcessArgs(args, true );
}else
//cache Arguments
MyWindow.ProcessArgs(args, false);
}


}

در این کلاس تابع ProcessArgs آرگومان های ورودی را گرفته و تجزیه می کند سپس اطلاعات را به تابعی با همین نام در شئ MyWindow تحویل می دهد (ورودی ها در ادامه بررسی خواهند شد)



حال نگاهی به پیاده سازی MyWindow می اندازیم




public MainWindow()
{
InitializeComponent();
}

//Parse Arguments
public void ProcessArgs(string[] args, bool firstInstance)
{
if (!firstInstance)
{
Block.Text += "Other try" + Environment.NewLine; ;
if (args.Length >= 1)
{
string temp = "With Argument" + Environment.NewLine;

for (int i = 0; i <args.Length ; i++)
{
temp += "args " + i + " = " + args[i] + Environment.NewLine;
}

this.Block.Text += temp;
}

}
else
{
Block.Text = "First Instance" + Environment.NewLine; ;
if (args.Length >= 1)
{
string temp = "With Arguments" + Environment.NewLine;

for (int i = 0; i < args.Length; i++)
{
temp += "args " + i + " = " + args[i] + Environment.NewLine;
}
this.Block.Text += temp;
}

}
}
در این پنجره که به عنوان پنجره اصلی برنامه است شما می بایست آرگومان های ورودی را بررسی (Parse) کنید و نمایش صحیحی رو بسته به برنامه در اختیار کاربر قرار دهید که ما در اینجا وقت به نمایش آرگومان های ورودی بسنده می کنیم.
ورودی اول آرگومتن های ورودی است و ورودی دوم مشخص می کند که آیا نمونه اول و اصلی است یا نمونه دیگر (اجرای اول با دیگر)

توجه کنید که تا این جا این راه حل برای یک Build از برنامه کار می کند واگر بخواهید که برای انواع Build های نرم افزار در یک سیستم جواب بدهد باید در فایل Assembly.cs پروژه خود یک اتریبیوت به نام GuidAttribute را بیفزایید چون اگر Guidرو مقداردهی نکنید دات نت در هر بار Build یک Guid به آن اختصاص می دهدو در Build های مختلف برنامه نمی توانید یک فرم واحد داشته باشید.


[assembly: GuidAttribute("67ab3432-7388-4f47-af08-edabe2306cfd")]حاصل برنامه بالا با توجه به ورودی های مختلف در تصویر زیر مشخص است


http://aliaghdam.persiangig.com/General_Picture/Post/end.png (http://aliaghdam.persiangig.com/General_Picture/Post/end.png)

منابع : من اول این نکته رو در کتاب http://vahid.nasiri.googlepages.com/weblink.gifPro WPF in C#‎ 2008" by Matthew MacDonald (http://www.amazon.com/Pro-WPF-2008-Presentation-Professionals/dp/1590599551?&camp=212361&linkCode=wey&tag=pietschsoft-20&creative=380729) خوندم بعد از http://vahid.nasiri.googlepages.com/weblink.gifMSDN (http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.aspx)ت میلش کردم.

فایل پروژه را می توانید از { http://vahid.nasiri.googlepages.com/weblink.gifاینجا (http://aliaghdam.persiangig.com/General_Picture/Post/WPF%20Built-In%20Single%20Instance%20Feature.zip) } دریافت کنید.

Amir Oveisi
شنبه 29 آبان 1389, 17:49 عصر
نرم افزار رمز نگاری فایل

امکانات برنامه:

رمزنگاری فایل بر اساس کلمه عبوری که به آن داده میشود و با الگوریتمAES، بطوریکه فقط با همان کلمه عبور، رمز گشایی خواهد شد.
قابلیت رمزنگاری محتویات فولدر ها بصورت تو در تو (همراه با زیرفولدر ها)
امکان رمزنگاری و رمزگشایی بصورت همزمان و با Thread های مجزا، بدون کاهش سرعت
محاسبه Elapsed Time و Remaining Time با استفاده از Thread مجزا


ویژگی های فنی:

استفاده از تکنولوژی WPF
کاملا Multi Thread
طراحی شده با معماری MVVM
استفاده از Aero Glass Theme ویندوز 7
با استفاده از Visula Studio 2010 و .NET Framework 3.5 نوشته شده است.


http://6xq0qg.bay.livefilestore.com/y1p-4sGXWUnD2q2iyFCvY0z7d83QRlBgVGqH6EXyqhGpTIDE1uoG6x n65dR0Zx7RL5uXJxSZAdNQc5GzTKcR3T7B0_V9_9YYWvd/bde1%20%282%29.jpg?psid=1

چند نکته که بر عهده شما خواهد بود:

Exception Handling ها اعمال نشده اند.
Validation ها در رویداد CanExecute برای Command ها، اعمال نشده اند.
گزینه On The Fly Encryption در نسخه open source فعال نمی باشد.

لینک دریافت برنامه همراه با سورس (http://www.4shared.com/file/HSc3QIsK/BeRMOoDAWPFFileEncryptor.html)

توضیحات:

من این برنامه رو چند سال پیش نوشته بودم ولی معماری خاصی نداشت و تقریبا spaghetti code بود.
چند روز پیش تصمیم گرفتم این برنامه را با معماری MVVM بنویسم تا ببینم از نظر performance چه تغییراتی اتفاق میفتد. نتیجه خیلی جالب بود، اعمال رمزنگاری و رمزگشایی نزدیک 30% سریع تر انجام شدند!

ساختار پروژه:
http://tgynca.bay.livefilestore.com/y1p7UhQLlqHVf0J6KUMAvSnNnIUlvWj29ZAeZuT14qGmqRSatn WG5T42Rdusgx1KxWzI6hNBsI4_kTnt5eE8LxAzm1QfMRwuzg6/classes.jpg?psid=1

کلاس های داخل فولدر Converters، کلاس هایی هستند که در Binding ها استفاده می شوند. کاربرد آن زمانی است که مقداری که Bind می شود باید تغییراتی (از نظر نوع داده یا فرمت نمایش) بکند. به عنوان مثال کلاس زیر برای NOT کردن مقدار یک متغیر bool در کد Xaml را ممکن می سازد:
[ValueConversion(typeof(bool), typeof(bool))]
public class XamlNotOperatorConverter : IValueConverter
{

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{

return !(bool)value;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
}

سپس برای استفاده از این کلاس در کد Xaml باید یک object از آن در resource های کنترل مورد نظر تعریف کرد و آن را به عنوان مقدار پارامتر Converter برای Binding قرار داد:

<UserControl.Resources>
<project:XamlNotOperatorConverter x:Key="NotOperatorConverter"/>
</UserControl.Resources>

<Expander IsExpanded="{Binding ElementName=expanderDecrypt, Path=IsExpanded, Converter={StaticResource NotOperatorConverter}}" />

با این کار، بدون نوشتن کد برای event handling، زمانی که یکی از expander ها باز شود، expander دیگر بصورت خودکار بسته خواهد شد.

لایه Model
در فولدر Model، مدل داده ای که قرار است در برنامه با آن سر و کار داشته باشیم تعریف شده است. در برنامه قصد نمایش اطلاعات مربوط به فایل ها را داریم و میخواهیم نام، سایز و وضعیت یک فایل را نشان دهیم. وضعیت یک فایل می تواند نرمال، در حال پردازش و پردازش شده باشد که هر کدام با یک رنگ مشخص می شود. به همین دلیل نوع داده فیلد Status از نوع Brush در نظر گرفته شده است.

لایه View
فولدر View شامل یک user control است که در واقع قرار است در داخل MainWindow نمایش داده شود. اما بر اساس قوانین MVVM، هیچ منطقی را شامل نمی شود د دارای code behind نیست. منطق مورد نیاز برنامه بصورت کامل در لایه ViewModel پیاده سازس خواهد شد.

لایه ViewModel
فولدر ViewModels شامل کلاس هایی است که منطق برنامه را فراهم می کنند. یک کلاس پایه وجود دارد که هر ViewModel ای از آن ارث می برد. یک ViewModel برای MainWindow و یک ViewModel برای user control ایجاد شده است. در حالت کلی، برای هر View، یک ViewModel وجود خواهد داشت.

کلاس PasswordHelper:
فیلد Password در کنترل PasswordBox یک DependencyProperty نیست و به همین دلیل (و نیز دلایل امنیتی) امکان Bind آن وجود ندارد. برای حل این مسئله یک کلاس طراحی شده که یک Attached Property به PasswordBox اضافه می کند و می توان مقدار آن را Bind کرد.

کلاس AeroGlassProvider:
این کلاس با استفاده از API های ویندوز 7 یا وسیتا، قابلیت استفاده از Aero Theme ویندوز را در برنامه فراهم می کند.

کلاس Crypto:
این کلاس هم لایه ارتباط با فایل است که می توان آن را لایه Data Access هم در نظر گرفت. در این کلاس با استفاده از الگوریتم AES اعمال رمزنگاری و رمزگشایی انجام می شوند.

کلاس RelayCommand:
آقای Josh Smith این کلاس را نوشته اند که امکان استفاده ساده از Action ها و Predicate ها را به برنامه نویسان می دهد.

توضیحات کامل در مورد معماری MVVM بزودی با استفاده از یک پروژه ساده ارائه خواهد شد.

Amir Oveisi
یک شنبه 29 آبان 1390, 20:47 عصر
PDF‌:‌دارد
نمونه کد: دارد

مقدمه
با گسترش استفاده از كامپيوتر در بسياري از امور روزمره انسان ها سازگار بودن برنامه ها با سليقه كاربران به يكي از نياز هاي اصلي برنامه هاي كامپيوتري تبديل شده است. بدون شك زبان و فرهنگ يكي از مهم ترين عوامل در ايجاد ارتباط نزديك بين برنامه و كاربر به شمار مي رود و نقشي غير قابل انكار در ميزان موفقيت يك برنامه به عهده دارد. از اين رو در اين نوشته تلاش بر آن است تا يكي از ساده ترين و در عين حال كارا ترين راه هاي ممكن براي ايجاد برنامه هاي چند زبانه با استفاده از تكنولوژي WPF آموزش داده شود.

مروري بر روش هاي موجود
همواره روش هاي مختلفي براي پياده سازي يك ايده در دنياي نرم افزار وجود دارد كه هر روش را مي توان بر حسب نياز مورد استفاده قرار داد. در برنامه هاي مبتني بر WPF معمولا از دو روش عمده براي اين منظور استفاده مي شود:
1- استفاده از فايل هاي .resx
در اين روش كه براي Win App نيز استفاده مي شود، اطلاعات مورد نياز براي هر زبان به شكل جدول هايي داراي كليد و مقدار در داخل يك فايل .resx نگهداري مي شود و در زمان اجراي برنامه بر اساس انتخاب كاربر اطلاعات زبان مورد نظر از داخل فايل .resx load شده و نمايش داده مي شود. يكي از ضعف هايي كه اين روش در عين ساده بودن دارد اين است كه همه اطلاعات مورد نياز داخل assembly اصلي برنامه قرار مي گيرد و امكان افزودن زبان هاي جديد بدون تغيير دادن برنامه اصلي ممكن نخواهد بود.
2- استفاده از فايل هاي .csv كه به فايل هاي .dll تبديل مي شوند
در اين روش كه مختص برنامه هاي WPF مي باشد با استفاده از ابزار هاي موجود در كامپايلر WPF براي هر كنترل يك property به نام Uid ايجاد شده و مقدار دهي مي شود. سپس با ابزار ديگري ( كه جزو ابزار هاي كامپايلر محسوب نمي شود ) از فايل .csproj پروژه يك خروجي اكسل با فرمت .csv ايجاد مي شود كه شامل Uid هاي كنترل ها و مقادير آن ها است. پس از ترجمه متون مورد نظر به زبان مقصد با كمك ابزار ديگري فايل اكسل مورد نظر به يك .net assembly تبديل مي شود و داخل پوشه اي با نام culture استاندارد ذخيره مي شود. ( مثلا براي زبان فارسي نام پوشه fa-IR خواهد بود ). زماني كه برنامه اجرا مي شود بر اساس culture اي كه در سيستم عامل انتخاب شده است و در صورتي كه براي آن culture فايل .dll اي موجود باشد، زبان مربوط به آن culture را load خواهد كرد. با وجود اين كه اين روش مشكل روش قبلي را ندارد و بيشتر با ويژگي هاي WPF سازگار است اما پروسه اي طولاني براي انجام كار ها دارد و به ازاي هر تغييري بايد كل مراحل هر بار تكرار شوند. همچنين مشكلاتي در نمايش برخي زبان ها ( از جمله فارسي ) در اين روش مشاهده شده است.

روش سوم!
روش سوم اما كاملا بر پايه WPF و در اصطلاح WPF-Native مي باشد. ايده از آنجا ناشي شده است كه براي ايجاد skin در برنامه هاي WPF استفاده مي شود. در ايجاد برنامه هاي Skin-Based به اين شيوه عمل مي شود كه skin هاي مورد نظر به صورت style هايي در داخل resource dictionary ها قرار مي گيرند. سپس آن resource dictionary به شكل .dll كامپايل مي شود. در برنامه اصلي نيز همه كنترل ها style هايشان را به شكل dynamic resource از داخل يك resource dictionary مشخص شده load مي كنند. حال كافي است براي تغيير skin، resource dictionary مورد نظر از .dll مشخص load شود و resource dictionary اي كه در حال حاضر در برنامه از آن استفاده مي شود با resource dictionary اي كه load شده جايگزين شود. كنترل ها مقادير جديد را از resource dictionary جديد به شكل كاملا خودكار دريافت خواهند كرد.
خوب! به سادگي مي توان از اين روش براي تغيير زبان برنامه نيز استفاده كرد.

پياده سازي
در اين قسمت نحوه پياده سازي اين روش با ايجاد يك نمونه برنامه ساده كه داراي دو زبان انگليسي و فارسي خواهد بود آموزش داده مي شود.
ابتدا يك پروژه WPF Application در Visual Studio 2010 ايجاد كنيد. در MainWindow سه كنترل Button قرار دهيد و يك ComboBox كه قرار است زبان هاي موجود را نمايش دهد و با انتخاب يك زبان، نوشته هاي درون Button ها متناسب با آن تغيير خواهند كرد.


https://byfiles.storage.live.com/y1ptKpM_oS48iC_oTFzFCCdodNFwrHUR-4S_211PbuLctlc_upHdL65aLyU-QU-kFdXiXHsw6hsTtc/Capture4.PNG?psid=1

توجه داشته باشيد كه براي Button ها نبايد به صورت مستقيم مقداري به Content شان داده شود. بلكه بايد مقدار مورد نظر از داخل resource dictionary كه خواهيم ساخت به شكل dynamic گرفته خواهد شد. پس در اين مرحله يك ResourceDictionary به پروژه اضافه خواهيم كرد و در آن resource هايي به شكل string ايجاد خواهيم كرد. هر resource داراي يك Key مي باشد كه بر اساس آن، Button مورد نظر مقدار آن Resource را load خواهد كرد. فايل ResourceDictionary را Culture_en-US.xaml نامگذاري كنيد و مقادير مورد نظر را به آن اضافه نماييد.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="button1">Hello!</system:String>
<system:String x:Key="button2">How Are You?</system:String>
<system:String x:Key="button3">Are You OK?</system:String>

</ResourceDictionary>


دقت كنيد كه namespace اي كه كلاس string در آن قرار دارد به فايل xaml اضافه شده است.

پس از اين كار فايل App.xaml به اين شكل خواهد بود:
<Application x:Class="BeRMOoDA.WPF.LocalizationSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>

<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Culture_en-US.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

</Application.Resources>
</Application>


براي استفاده از اين resource ها به عنوان Content براي Button ها نيز آن ها را به صورت DynamicResource، load خواهيم كرد:
<Button Content="{DynamicResource ResourceKey=button1}" />
<Button Content="{DynamicResource ResourceKey=button2}" />
<Button Content="{DynamicResource ResourceKey=button3}" />


بسيار خوب! اكنون بايد شروع به ايجاد يك ResourceDictionary براي زبان فارسي كنيم و آن را به صورت يك فايل .dll كامپايل نماييم.
براي اين كار يك پروژه جديد در قسمت WPF از نوع User control ايجاد مي كنيم و نام آن را Culture_fa-IR_Farsi قرار مي دهيم. لطفا شيوه نامگذاري را رعايت كنيد چرا كه در ادامه به آن نياز خواهيم داشت.
پس از ايجاد پروژه فايل UserControl1.xaml را از پروژه حذف كنيد و يك ResourceDictionary با نام Culture_fa-IR.xaml اضافه كنيد. محتواي آن را پاك كنيد و محتواي فايل Culture_en-US.xaml را از پروژه قبلي به صورت كامل در فايل جديد كپي كنيد. دو فايل بايد ساختار كاملا يكساني از نظر key براي resource هاي موجود داشته باشند. حالا زمان ترجمه فرا رسيده است! رشته هاي دلخواه را ترجمه كنيد و پروژه را build نماييد.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Culture_fa-IR_Farsi.xaml"/>
</ResourceDictionary.MergedDictionaries>
<system:String x:Key="button1">سلام!</system:String>
<system:String x:Key="button2">حالت چطوره؟</system:String>
<system:String x:Key="button3">خوبی؟</system:String>
</ResourceDictionary>


خروجي يك فايل با نام Culture_fa-IR_Farsi.dll خواهد بود.

در اين مرحله كاري كه بايد انجام دهيم چيست؟
راهكاري ارئه دهيم تا بتوان فايل هاي .dll مربوط به زبان ها را در زمان اجراي برنامه load كرده و نام زبان ها را در داخل ComboBox اي كه داريم نشان دهد. سپس با انتخاب هر زبان در ComboBox، محتواي Button ها بر اساس زبان انتخاب شده تغيير كند.
براي سهولت كار، نام فايل ها را به گونه اي انتخاب كرديم كه بتوانيم ساده تر به اين هدف برسيم. نام هر فايل از سه بخش تشكيل شده است:

Culture_[standard culture notation]_[display name for this culture].dll
يعني اگر فايل Culture_fa-IR_Farsi.dll را در نظر بگيريم، Culture نشان دهنده اين است كه اين فايل مربوط به يك culture مي باشد. fa-IR نمايش استاندارد culture براي كشور ايران و زبان فارسي است و Farsi هم مقداري است كه مي خواهيم در ComboBox براي اين زبان نمايش داده شود.
پوشه اي با نام Languages در كنار فايل .exe برنامه ايجاد كنيد و فايل Culture_fa-IR_Farsi.dll را درون آن كپي كنيد. تصميم داريم همه .dll هاي مربوط به زبان ها را داخل اين پوشه قرار دهيم تا مديريت آن ها ساده تر شود.
براي مديريت بهتر فايل هاي مربوط به زبان ها يك كلاس با نام CultureAssemblyModel خواهيم ساخت كه هر object از آن نشانگر يك فايل زبان خواهد بود. يك كلاس با اين نام به پروژه اضافه كنيد و property هاي زير را در آن تعريف نماييد:
public class CultureAssemblyModel
{
//the text will be displayed to user as language name (like Farsi)
public string DisplayText { get; set; }
//name of .dll file (like Culture_fa-IR_Farsi.dll)
public string Name { get; set; }
//standar notation of this culture (like fa-IR)
public string Culture { get; set; }
//name of resource dictionary file name inside the loaded .dll (like Culture_fa-IR.xaml)
public string XamlFileName { get; set; }
}


براي ComboBox نيز يك DataTemplate تعريف مي كنيم تا فقط فيلد DisplayText از اين كلاس را نمايش دهد:
<ComboBox HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" MinWidth="100" Name="comboboxLanguages">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayText}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

براي load كردن نام فايل هاي .dll مربوط به زبان ها متدي تعريف مي كنيم به اسم LoadCultureAssmeblies به شكل زير:
//will keep information about loaded assemblies
public List<CultureAssemblyModel> CultureAssemblies { get; set; }

//loads assmeblies in languages folder and adds their info to list
void LoadCultureAssemblies()
{
//we should be sure that list is empty before adding info (do u want to add some cultures more than one? of course u dont!)
CultureAssemblies.Clear();
//creating a directory represents applications directory\languages
DirectoryInfo dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirector y + "\\languages");
//getting all .dll files in the language folder and its sub dirs. (who knows? maybe someone keeps each culture file in a seperate folder!)
var assemblies = dir.GetFiles("*.dll", SearchOption.AllDirectories);
//for each found .dll we will create a model and set its properties and then add to list for (int i = 0; i < assemblies.Count(); i++)
{
CultureAssemblyModel model = new CultureAssemblyModel() { DisplayText = assemblies[i].Name.Split('.', '_')[2], Culture = assemblies[i].Name.Split('.', '_')[1], Name = assemblies[i].Name, XamlFileName =assemblies[i].Name.Substring(0, assemblies[i].Name.LastIndexOf(".")) + ".xaml" };
CultureAssemblies.Add(model);
}
}


اطلاعات مورد نياز از فايل هاي .dll خوانده شده و در ليست نگهداري مي شوند. براي نمايش دادن نام زبان ها در ComboBox كافي است اين ليست را به عنوان ItemsSource قرار دهيم:
comboboxLanguages.ItemsSource = CultureAssemblies;

با handle كردن رويداد selectionChanged مربوط به ComboBox زبان انتخاب شده را به متد LoadCulture كه در فايل App.cs (كلاس اصلي برنامه) تعريف كرده ايم ارسال مي كنيم:
//loads selected culture
public void LoadCulture(CultureAssemblyModel culture)
{
//setting current culture of applpications main thread to selected one.
System.Threading.Thread.CurrentThread.CurrentUICul ture = new System.Globalization.CultureInfo(culture.Culture);
System.Threading.Thread.CurrentThread.CurrentCultu re = new System.Globalization.CultureInfo(culture.Culture);
//creating a FileInfo object represents .dll file of selected cultur
FileInfo assemblyFile = new FileInfo("languages\\" + culture.Name);
//loading .dll into memory as a .net assembly
var assembly = Assembly.LoadFile(assemblyFile.FullName);
//getting .dll file name
var assemblyName = assemblyFile.Name.Substring(0, assemblyFile.Name.LastIndexOf("."));
//creating string represents structure of a pack uri (something like this: /{myassemblyname;component/myresourcefile.xaml}
string packUri = string.Format(@"/{0};component/{1}", assemblyName, culture.XamlFileName);
//creating a pack uri
Uri uri = new Uri(packUri, UriKind.Relative);
//now we have created a pack uri that represents a resource object in loaded assembly
//and its time to load that as a resource dictionary (do u remember that we had resource dictionary in culture assemblies? don't u?)
var dic = Application.LoadComponent(uri) as ResourceDictionary;
dic.Source = uri;
//here we will remove current merged dictionaries in our resource dictionary and add recently-loaded resource dictionary as e merged dictionary
var mergedDics = this.Resources.MergedDictionaries;
if (mergedDics.Count > 0)
mergedDics.Clear();
mergedDics.Add(dic);
}

void comboboxLanguages_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedCulture = (CultureAssemblyModel)comboboxLanguages.SelectedIt em;
App app = Application.Current as App;
app.LoadCulture(selectedCulture);
}



متد LoadCulture با گرفتن مشخصات زبان ارسال شده، culture سيستم را به زبان مورد نظر تغيير داده و resource dictionary مربوط به آن را از داخل فايل .dll آن زبان load كرده و آن را با resource dictionary فعلي برنامه جايگزين مي كند. Button ها كه محتواي خود را به شكل dynamic از داخل اين resource dictionary دريافت مي كردند حالا از داخل فايل جايگزين شده خواهند گرفت.

https://byfiles.storage.live.com/y1pLbKwcBj_ba4qFakRbLIMS3X3RfxeW-vkKaHLP3YZF-yDj64xZcY3Jn8HkRSki-Ddc19KbA901Ng/Capture9.PNG?psid=1

كار انجام شد!
از مزيت هاي اين روش مي توان به WPF-Native بودن، سادگي در پياده سازي، قابليت load كردن هر زبان جديدي در زمان اجرا بدون نياز به كوچك ترين تغيير در برنامه اصلي و همچنين پشتيباني كامل از نمايش زبان هاي مختلف از جمله فارسي اشاره كرد.

لینک اول PDF و لینک دوم نمونه برنامه هستند.

morteza_carefree
جمعه 02 مرداد 1394, 10:26 صبح
دوست عزیز ممنون از آموزش خوبت واقعا عالی بود
امکانش هست مال skin هارم بگی ؟

morteza_carefree
پنج شنبه 08 مرداد 1394, 11:27 صبح
PDF‌:‌دارد
نمونه کد: دارد

مقدمه
با گسترش استفاده از كامپيوتر در بسياري از امور روزمره انسان ها سازگار بودن برنامه ها با سليقه كاربران به يكي از نياز هاي اصلي برنامه هاي كامپيوتري تبديل شده است. بدون شك زبان و فرهنگ يكي از مهم ترين عوامل در ايجاد ارتباط نزديك بين برنامه و كاربر به شمار مي رود و نقشي غير قابل انكار در ميزان موفقيت يك برنامه به عهده دارد. از اين رو در اين نوشته تلاش بر آن است تا يكي از ساده ترين و در عين حال كارا ترين راه هاي ممكن براي ايجاد برنامه هاي چند زبانه با استفاده از تكنولوژي WPF آموزش داده شود.

مروري بر روش هاي موجود
همواره روش هاي مختلفي براي پياده سازي يك ايده در دنياي نرم افزار وجود دارد كه هر روش را مي توان بر حسب نياز مورد استفاده قرار داد. در برنامه هاي مبتني بر WPF معمولا از دو روش عمده براي اين منظور استفاده مي شود:
1- استفاده از فايل هاي .resx
در اين روش كه براي Win App نيز استفاده مي شود، اطلاعات مورد نياز براي هر زبان به شكل جدول هايي داراي كليد و مقدار در داخل يك فايل .resx نگهداري مي شود و در زمان اجراي برنامه بر اساس انتخاب كاربر اطلاعات زبان مورد نظر از داخل فايل .resx load شده و نمايش داده مي شود. يكي از ضعف هايي كه اين روش در عين ساده بودن دارد اين است كه همه اطلاعات مورد نياز داخل assembly اصلي برنامه قرار مي گيرد و امكان افزودن زبان هاي جديد بدون تغيير دادن برنامه اصلي ممكن نخواهد بود.
2- استفاده از فايل هاي .csv كه به فايل هاي .dll تبديل مي شوند
در اين روش كه مختص برنامه هاي WPF مي باشد با استفاده از ابزار هاي موجود در كامپايلر WPF براي هر كنترل يك property به نام Uid ايجاد شده و مقدار دهي مي شود. سپس با ابزار ديگري ( كه جزو ابزار هاي كامپايلر محسوب نمي شود ) از فايل .csproj پروژه يك خروجي اكسل با فرمت .csv ايجاد مي شود كه شامل Uid هاي كنترل ها و مقادير آن ها است. پس از ترجمه متون مورد نظر به زبان مقصد با كمك ابزار ديگري فايل اكسل مورد نظر به يك .net assembly تبديل مي شود و داخل پوشه اي با نام culture استاندارد ذخيره مي شود. ( مثلا براي زبان فارسي نام پوشه fa-IR خواهد بود ). زماني كه برنامه اجرا مي شود بر اساس culture اي كه در سيستم عامل انتخاب شده است و در صورتي كه براي آن culture فايل .dll اي موجود باشد، زبان مربوط به آن culture را load خواهد كرد. با وجود اين كه اين روش مشكل روش قبلي را ندارد و بيشتر با ويژگي هاي WPF سازگار است اما پروسه اي طولاني براي انجام كار ها دارد و به ازاي هر تغييري بايد كل مراحل هر بار تكرار شوند. همچنين مشكلاتي در نمايش برخي زبان ها ( از جمله فارسي ) در اين روش مشاهده شده است.

روش سوم!
روش سوم اما كاملا بر پايه WPF و در اصطلاح WPF-Native مي باشد. ايده از آنجا ناشي شده است كه براي ايجاد skin در برنامه هاي WPF استفاده مي شود. در ايجاد برنامه هاي Skin-Based به اين شيوه عمل مي شود كه skin هاي مورد نظر به صورت style هايي در داخل resource dictionary ها قرار مي گيرند. سپس آن resource dictionary به شكل .dll كامپايل مي شود. در برنامه اصلي نيز همه كنترل ها style هايشان را به شكل dynamic resource از داخل يك resource dictionary مشخص شده load مي كنند. حال كافي است براي تغيير skin، resource dictionary مورد نظر از .dll مشخص load شود و resource dictionary اي كه در حال حاضر در برنامه از آن استفاده مي شود با resource dictionary اي كه load شده جايگزين شود. كنترل ها مقادير جديد را از resource dictionary جديد به شكل كاملا خودكار دريافت خواهند كرد.
خوب! به سادگي مي توان از اين روش براي تغيير زبان برنامه نيز استفاده كرد.

پياده سازي
در اين قسمت نحوه پياده سازي اين روش با ايجاد يك نمونه برنامه ساده كه داراي دو زبان انگليسي و فارسي خواهد بود آموزش داده مي شود.
ابتدا يك پروژه WPF Application در Visual Studio 2010 ايجاد كنيد. در MainWindow سه كنترل Button قرار دهيد و يك ComboBox كه قرار است زبان هاي موجود را نمايش دهد و با انتخاب يك زبان، نوشته هاي درون Button ها متناسب با آن تغيير خواهند كرد.


https://byfiles.storage.live.com/y1ptKpM_oS48iC_oTFzFCCdodNFwrHUR-4S_211PbuLctlc_upHdL65aLyU-QU-kFdXiXHsw6hsTtc/Capture4.PNG?psid=1

توجه داشته باشيد كه براي Button ها نبايد به صورت مستقيم مقداري به Content شان داده شود. بلكه بايد مقدار مورد نظر از داخل resource dictionary كه خواهيم ساخت به شكل dynamic گرفته خواهد شد. پس در اين مرحله يك ResourceDictionary به پروژه اضافه خواهيم كرد و در آن resource هايي به شكل string ايجاد خواهيم كرد. هر resource داراي يك Key مي باشد كه بر اساس آن، Button مورد نظر مقدار آن Resource را load خواهد كرد. فايل ResourceDictionary را Culture_en-US.xaml نامگذاري كنيد و مقادير مورد نظر را به آن اضافه نماييد.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="button1">Hello!</system:String>
<system:String x:Key="button2">How Are You?</system:String>
<system:String x:Key="button3">Are You OK?</system:String>

</ResourceDictionary>


دقت كنيد كه namespace اي كه كلاس string در آن قرار دارد به فايل xaml اضافه شده است.

پس از اين كار فايل App.xaml به اين شكل خواهد بود:
<Application x:Class="BeRMOoDA.WPF.LocalizationSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>

<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Culture_en-US.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

</Application.Resources>
</Application>


براي استفاده از اين resource ها به عنوان Content براي Button ها نيز آن ها را به صورت DynamicResource، load خواهيم كرد:
<Button Content="{DynamicResource ResourceKey=button1}" />
<Button Content="{DynamicResource ResourceKey=button2}" />
<Button Content="{DynamicResource ResourceKey=button3}" />


بسيار خوب! اكنون بايد شروع به ايجاد يك ResourceDictionary براي زبان فارسي كنيم و آن را به صورت يك فايل .dll كامپايل نماييم.
براي اين كار يك پروژه جديد در قسمت WPF از نوع User control ايجاد مي كنيم و نام آن را Culture_fa-IR_Farsi قرار مي دهيم. لطفا شيوه نامگذاري را رعايت كنيد چرا كه در ادامه به آن نياز خواهيم داشت.
پس از ايجاد پروژه فايل UserControl1.xaml را از پروژه حذف كنيد و يك ResourceDictionary با نام Culture_fa-IR.xaml اضافه كنيد. محتواي آن را پاك كنيد و محتواي فايل Culture_en-US.xaml را از پروژه قبلي به صورت كامل در فايل جديد كپي كنيد. دو فايل بايد ساختار كاملا يكساني از نظر key براي resource هاي موجود داشته باشند. حالا زمان ترجمه فرا رسيده است! رشته هاي دلخواه را ترجمه كنيد و پروژه را build نماييد.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Culture_fa-IR_Farsi.xaml"/>
</ResourceDictionary.MergedDictionaries>
<system:String x:Key="button1">سلام!</system:String>
<system:String x:Key="button2">حالت چطوره؟</system:String>
<system:String x:Key="button3">خوبی؟</system:String>
</ResourceDictionary>


خروجي يك فايل با نام Culture_fa-IR_Farsi.dll خواهد بود.

در اين مرحله كاري كه بايد انجام دهيم چيست؟
راهكاري ارئه دهيم تا بتوان فايل هاي .dll مربوط به زبان ها را در زمان اجراي برنامه load كرده و نام زبان ها را در داخل ComboBox اي كه داريم نشان دهد. سپس با انتخاب هر زبان در ComboBox، محتواي Button ها بر اساس زبان انتخاب شده تغيير كند.
براي سهولت كار، نام فايل ها را به گونه اي انتخاب كرديم كه بتوانيم ساده تر به اين هدف برسيم. نام هر فايل از سه بخش تشكيل شده است:

Culture_[standard culture notation]_[display name for this culture].dll
يعني اگر فايل Culture_fa-IR_Farsi.dll را در نظر بگيريم، Culture نشان دهنده اين است كه اين فايل مربوط به يك culture مي باشد. fa-IR نمايش استاندارد culture براي كشور ايران و زبان فارسي است و Farsi هم مقداري است كه مي خواهيم در ComboBox براي اين زبان نمايش داده شود.
پوشه اي با نام Languages در كنار فايل .exe برنامه ايجاد كنيد و فايل Culture_fa-IR_Farsi.dll را درون آن كپي كنيد. تصميم داريم همه .dll هاي مربوط به زبان ها را داخل اين پوشه قرار دهيم تا مديريت آن ها ساده تر شود.
براي مديريت بهتر فايل هاي مربوط به زبان ها يك كلاس با نام CultureAssemblyModel خواهيم ساخت كه هر object از آن نشانگر يك فايل زبان خواهد بود. يك كلاس با اين نام به پروژه اضافه كنيد و property هاي زير را در آن تعريف نماييد:
public class CultureAssemblyModel
{
//the text will be displayed to user as language name (like Farsi)
public string DisplayText { get; set; }
//name of .dll file (like Culture_fa-IR_Farsi.dll)
public string Name { get; set; }
//standar notation of this culture (like fa-IR)
public string Culture { get; set; }
//name of resource dictionary file name inside the loaded .dll (like Culture_fa-IR.xaml)
public string XamlFileName { get; set; }
}


براي ComboBox نيز يك DataTemplate تعريف مي كنيم تا فقط فيلد DisplayText از اين كلاس را نمايش دهد:
<ComboBox HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" MinWidth="100" Name="comboboxLanguages">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayText}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

براي load كردن نام فايل هاي .dll مربوط به زبان ها متدي تعريف مي كنيم به اسم LoadCultureAssmeblies به شكل زير:
//will keep information about loaded assemblies
public List<CultureAssemblyModel> CultureAssemblies { get; set; }

//loads assmeblies in languages folder and adds their info to list
void LoadCultureAssemblies()
{
//we should be sure that list is empty before adding info (do u want to add some cultures more than one? of course u dont!)
CultureAssemblies.Clear();
//creating a directory represents applications directory\languages
DirectoryInfo dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirector y + "\\languages");
//getting all .dll files in the language folder and its sub dirs. (who knows? maybe someone keeps each culture file in a seperate folder!)
var assemblies = dir.GetFiles("*.dll", SearchOption.AllDirectories);
//for each found .dll we will create a model and set its properties and then add to list for (int i = 0; i < assemblies.Count(); i++)
{
CultureAssemblyModel model = new CultureAssemblyModel() { DisplayText = assemblies[i].Name.Split('.', '_')[2], Culture = assemblies[i].Name.Split('.', '_')[1], Name = assemblies[i].Name, XamlFileName =assemblies[i].Name.Substring(0, assemblies[i].Name.LastIndexOf(".")) + ".xaml" };
CultureAssemblies.Add(model);
}
}


اطلاعات مورد نياز از فايل هاي .dll خوانده شده و در ليست نگهداري مي شوند. براي نمايش دادن نام زبان ها در ComboBox كافي است اين ليست را به عنوان ItemsSource قرار دهيم:
comboboxLanguages.ItemsSource = CultureAssemblies;

با handle كردن رويداد selectionChanged مربوط به ComboBox زبان انتخاب شده را به متد LoadCulture كه در فايل App.cs (كلاس اصلي برنامه) تعريف كرده ايم ارسال مي كنيم:
//loads selected culture
public void LoadCulture(CultureAssemblyModel culture)
{
//setting current culture of applpications main thread to selected one.
System.Threading.Thread.CurrentThread.CurrentUICul ture = new System.Globalization.CultureInfo(culture.Culture);
System.Threading.Thread.CurrentThread.CurrentCultu re = new System.Globalization.CultureInfo(culture.Culture);
//creating a FileInfo object represents .dll file of selected cultur
FileInfo assemblyFile = new FileInfo("languages\\" + culture.Name);
//loading .dll into memory as a .net assembly
var assembly = Assembly.LoadFile(assemblyFile.FullName);
//getting .dll file name
var assemblyName = assemblyFile.Name.Substring(0, assemblyFile.Name.LastIndexOf("."));
//creating string represents structure of a pack uri (something like this: /{myassemblyname;component/myresourcefile.xaml}
string packUri = string.Format(@"/{0};component/{1}", assemblyName, culture.XamlFileName);
//creating a pack uri
Uri uri = new Uri(packUri, UriKind.Relative);
//now we have created a pack uri that represents a resource object in loaded assembly
//and its time to load that as a resource dictionary (do u remember that we had resource dictionary in culture assemblies? don't u?)
var dic = Application.LoadComponent(uri) as ResourceDictionary;
dic.Source = uri;
//here we will remove current merged dictionaries in our resource dictionary and add recently-loaded resource dictionary as e merged dictionary
var mergedDics = this.Resources.MergedDictionaries;
if (mergedDics.Count > 0)
mergedDics.Clear();
mergedDics.Add(dic);
}

void comboboxLanguages_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedCulture = (CultureAssemblyModel)comboboxLanguages.SelectedIt em;
App app = Application.Current as App;
app.LoadCulture(selectedCulture);
}



متد LoadCulture با گرفتن مشخصات زبان ارسال شده، culture سيستم را به زبان مورد نظر تغيير داده و resource dictionary مربوط به آن را از داخل فايل .dll آن زبان load كرده و آن را با resource dictionary فعلي برنامه جايگزين مي كند. Button ها كه محتواي خود را به شكل dynamic از داخل اين resource dictionary دريافت مي كردند حالا از داخل فايل جايگزين شده خواهند گرفت.

https://byfiles.storage.live.com/y1pLbKwcBj_ba4qFakRbLIMS3X3RfxeW-vkKaHLP3YZF-yDj64xZcY3Jn8HkRSki-Ddc19KbA901Ng/Capture9.PNG?psid=1

كار انجام شد!
از مزيت هاي اين روش مي توان به WPF-Native بودن، سادگي در پياده سازي، قابليت load كردن هر زبان جديدي در زمان اجرا بدون نياز به كوچك ترين تغيير در برنامه اصلي و همچنين پشتيباني كامل از نمايش زبان هاي مختلف از جمله فارسي اشاره كرد.

لینک اول PDF و لینک دوم نمونه برنامه هستند.

دوست عزیز من تو پروژه خودم تو این خط به مشکل بر میخورم
var dic = Application.LoadComponent(uri) as ResourceDictionary;

StackOverflowException

morteza_carefree
پنج شنبه 08 مرداد 1394, 11:56 صبح
PDF‌:‌دارد
نمونه کد: دارد

مقدمه
با گسترش استفاده از كامپيوتر در بسياري از امور روزمره انسان ها سازگار بودن برنامه ها با سليقه كاربران به يكي از نياز هاي اصلي برنامه هاي كامپيوتري تبديل شده است. بدون شك زبان و فرهنگ يكي از مهم ترين عوامل در ايجاد ارتباط نزديك بين برنامه و كاربر به شمار مي رود و نقشي غير قابل انكار در ميزان موفقيت يك برنامه به عهده دارد. از اين رو در اين نوشته تلاش بر آن است تا يكي از ساده ترين و در عين حال كارا ترين راه هاي ممكن براي ايجاد برنامه هاي چند زبانه با استفاده از تكنولوژي WPF آموزش داده شود.

مروري بر روش هاي موجود
همواره روش هاي مختلفي براي پياده سازي يك ايده در دنياي نرم افزار وجود دارد كه هر روش را مي توان بر حسب نياز مورد استفاده قرار داد. در برنامه هاي مبتني بر WPF معمولا از دو روش عمده براي اين منظور استفاده مي شود:
1- استفاده از فايل هاي .resx
در اين روش كه براي Win App نيز استفاده مي شود، اطلاعات مورد نياز براي هر زبان به شكل جدول هايي داراي كليد و مقدار در داخل يك فايل .resx نگهداري مي شود و در زمان اجراي برنامه بر اساس انتخاب كاربر اطلاعات زبان مورد نظر از داخل فايل .resx load شده و نمايش داده مي شود. يكي از ضعف هايي كه اين روش در عين ساده بودن دارد اين است كه همه اطلاعات مورد نياز داخل assembly اصلي برنامه قرار مي گيرد و امكان افزودن زبان هاي جديد بدون تغيير دادن برنامه اصلي ممكن نخواهد بود.
2- استفاده از فايل هاي .csv كه به فايل هاي .dll تبديل مي شوند
در اين روش كه مختص برنامه هاي WPF مي باشد با استفاده از ابزار هاي موجود در كامپايلر WPF براي هر كنترل يك property به نام Uid ايجاد شده و مقدار دهي مي شود. سپس با ابزار ديگري ( كه جزو ابزار هاي كامپايلر محسوب نمي شود ) از فايل .csproj پروژه يك خروجي اكسل با فرمت .csv ايجاد مي شود كه شامل Uid هاي كنترل ها و مقادير آن ها است. پس از ترجمه متون مورد نظر به زبان مقصد با كمك ابزار ديگري فايل اكسل مورد نظر به يك .net assembly تبديل مي شود و داخل پوشه اي با نام culture استاندارد ذخيره مي شود. ( مثلا براي زبان فارسي نام پوشه fa-IR خواهد بود ). زماني كه برنامه اجرا مي شود بر اساس culture اي كه در سيستم عامل انتخاب شده است و در صورتي كه براي آن culture فايل .dll اي موجود باشد، زبان مربوط به آن culture را load خواهد كرد. با وجود اين كه اين روش مشكل روش قبلي را ندارد و بيشتر با ويژگي هاي WPF سازگار است اما پروسه اي طولاني براي انجام كار ها دارد و به ازاي هر تغييري بايد كل مراحل هر بار تكرار شوند. همچنين مشكلاتي در نمايش برخي زبان ها ( از جمله فارسي ) در اين روش مشاهده شده است.

روش سوم!
روش سوم اما كاملا بر پايه WPF و در اصطلاح WPF-Native مي باشد. ايده از آنجا ناشي شده است كه براي ايجاد skin در برنامه هاي WPF استفاده مي شود. در ايجاد برنامه هاي Skin-Based به اين شيوه عمل مي شود كه skin هاي مورد نظر به صورت style هايي در داخل resource dictionary ها قرار مي گيرند. سپس آن resource dictionary به شكل .dll كامپايل مي شود. در برنامه اصلي نيز همه كنترل ها style هايشان را به شكل dynamic resource از داخل يك resource dictionary مشخص شده load مي كنند. حال كافي است براي تغيير skin، resource dictionary مورد نظر از .dll مشخص load شود و resource dictionary اي كه در حال حاضر در برنامه از آن استفاده مي شود با resource dictionary اي كه load شده جايگزين شود. كنترل ها مقادير جديد را از resource dictionary جديد به شكل كاملا خودكار دريافت خواهند كرد.
خوب! به سادگي مي توان از اين روش براي تغيير زبان برنامه نيز استفاده كرد.

پياده سازي
در اين قسمت نحوه پياده سازي اين روش با ايجاد يك نمونه برنامه ساده كه داراي دو زبان انگليسي و فارسي خواهد بود آموزش داده مي شود.
ابتدا يك پروژه WPF Application در Visual Studio 2010 ايجاد كنيد. در MainWindow سه كنترل Button قرار دهيد و يك ComboBox كه قرار است زبان هاي موجود را نمايش دهد و با انتخاب يك زبان، نوشته هاي درون Button ها متناسب با آن تغيير خواهند كرد.


https://byfiles.storage.live.com/y1ptKpM_oS48iC_oTFzFCCdodNFwrHUR-4S_211PbuLctlc_upHdL65aLyU-QU-kFdXiXHsw6hsTtc/Capture4.PNG?psid=1

توجه داشته باشيد كه براي Button ها نبايد به صورت مستقيم مقداري به Content شان داده شود. بلكه بايد مقدار مورد نظر از داخل resource dictionary كه خواهيم ساخت به شكل dynamic گرفته خواهد شد. پس در اين مرحله يك ResourceDictionary به پروژه اضافه خواهيم كرد و در آن resource هايي به شكل string ايجاد خواهيم كرد. هر resource داراي يك Key مي باشد كه بر اساس آن، Button مورد نظر مقدار آن Resource را load خواهد كرد. فايل ResourceDictionary را Culture_en-US.xaml نامگذاري كنيد و مقادير مورد نظر را به آن اضافه نماييد.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="button1">Hello!</system:String>
<system:String x:Key="button2">How Are You?</system:String>
<system:String x:Key="button3">Are You OK?</system:String>

</ResourceDictionary>


دقت كنيد كه namespace اي كه كلاس string در آن قرار دارد به فايل xaml اضافه شده است.

پس از اين كار فايل App.xaml به اين شكل خواهد بود:
<Application x:Class="BeRMOoDA.WPF.LocalizationSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>

<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Culture_en-US.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

</Application.Resources>
</Application>


براي استفاده از اين resource ها به عنوان Content براي Button ها نيز آن ها را به صورت DynamicResource، load خواهيم كرد:
<Button Content="{DynamicResource ResourceKey=button1}" />
<Button Content="{DynamicResource ResourceKey=button2}" />
<Button Content="{DynamicResource ResourceKey=button3}" />


بسيار خوب! اكنون بايد شروع به ايجاد يك ResourceDictionary براي زبان فارسي كنيم و آن را به صورت يك فايل .dll كامپايل نماييم.
براي اين كار يك پروژه جديد در قسمت WPF از نوع User control ايجاد مي كنيم و نام آن را Culture_fa-IR_Farsi قرار مي دهيم. لطفا شيوه نامگذاري را رعايت كنيد چرا كه در ادامه به آن نياز خواهيم داشت.
پس از ايجاد پروژه فايل UserControl1.xaml را از پروژه حذف كنيد و يك ResourceDictionary با نام Culture_fa-IR.xaml اضافه كنيد. محتواي آن را پاك كنيد و محتواي فايل Culture_en-US.xaml را از پروژه قبلي به صورت كامل در فايل جديد كپي كنيد. دو فايل بايد ساختار كاملا يكساني از نظر key براي resource هاي موجود داشته باشند. حالا زمان ترجمه فرا رسيده است! رشته هاي دلخواه را ترجمه كنيد و پروژه را build نماييد.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Culture_fa-IR_Farsi.xaml"/>
</ResourceDictionary.MergedDictionaries>
<system:String x:Key="button1">سلام!</system:String>
<system:String x:Key="button2">حالت چطوره؟</system:String>
<system:String x:Key="button3">خوبی؟</system:String>
</ResourceDictionary>


خروجي يك فايل با نام Culture_fa-IR_Farsi.dll خواهد بود.

در اين مرحله كاري كه بايد انجام دهيم چيست؟
راهكاري ارئه دهيم تا بتوان فايل هاي .dll مربوط به زبان ها را در زمان اجراي برنامه load كرده و نام زبان ها را در داخل ComboBox اي كه داريم نشان دهد. سپس با انتخاب هر زبان در ComboBox، محتواي Button ها بر اساس زبان انتخاب شده تغيير كند.
براي سهولت كار، نام فايل ها را به گونه اي انتخاب كرديم كه بتوانيم ساده تر به اين هدف برسيم. نام هر فايل از سه بخش تشكيل شده است:

Culture_[standard culture notation]_[display name for this culture].dll
يعني اگر فايل Culture_fa-IR_Farsi.dll را در نظر بگيريم، Culture نشان دهنده اين است كه اين فايل مربوط به يك culture مي باشد. fa-IR نمايش استاندارد culture براي كشور ايران و زبان فارسي است و Farsi هم مقداري است كه مي خواهيم در ComboBox براي اين زبان نمايش داده شود.
پوشه اي با نام Languages در كنار فايل .exe برنامه ايجاد كنيد و فايل Culture_fa-IR_Farsi.dll را درون آن كپي كنيد. تصميم داريم همه .dll هاي مربوط به زبان ها را داخل اين پوشه قرار دهيم تا مديريت آن ها ساده تر شود.
براي مديريت بهتر فايل هاي مربوط به زبان ها يك كلاس با نام CultureAssemblyModel خواهيم ساخت كه هر object از آن نشانگر يك فايل زبان خواهد بود. يك كلاس با اين نام به پروژه اضافه كنيد و property هاي زير را در آن تعريف نماييد:
public class CultureAssemblyModel
{
//the text will be displayed to user as language name (like Farsi)
public string DisplayText { get; set; }
//name of .dll file (like Culture_fa-IR_Farsi.dll)
public string Name { get; set; }
//standar notation of this culture (like fa-IR)
public string Culture { get; set; }
//name of resource dictionary file name inside the loaded .dll (like Culture_fa-IR.xaml)
public string XamlFileName { get; set; }
}


براي ComboBox نيز يك DataTemplate تعريف مي كنيم تا فقط فيلد DisplayText از اين كلاس را نمايش دهد:
<ComboBox HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" MinWidth="100" Name="comboboxLanguages">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayText}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

براي load كردن نام فايل هاي .dll مربوط به زبان ها متدي تعريف مي كنيم به اسم LoadCultureAssmeblies به شكل زير:
//will keep information about loaded assemblies
public List<CultureAssemblyModel> CultureAssemblies { get; set; }

//loads assmeblies in languages folder and adds their info to list
void LoadCultureAssemblies()
{
//we should be sure that list is empty before adding info (do u want to add some cultures more than one? of course u dont!)
CultureAssemblies.Clear();
//creating a directory represents applications directory\languages
DirectoryInfo dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirector y + "\\languages");
//getting all .dll files in the language folder and its sub dirs. (who knows? maybe someone keeps each culture file in a seperate folder!)
var assemblies = dir.GetFiles("*.dll", SearchOption.AllDirectories);
//for each found .dll we will create a model and set its properties and then add to list for (int i = 0; i < assemblies.Count(); i++)
{
CultureAssemblyModel model = new CultureAssemblyModel() { DisplayText = assemblies[i].Name.Split('.', '_')[2], Culture = assemblies[i].Name.Split('.', '_')[1], Name = assemblies[i].Name, XamlFileName =assemblies[i].Name.Substring(0, assemblies[i].Name.LastIndexOf(".")) + ".xaml" };
CultureAssemblies.Add(model);
}
}


اطلاعات مورد نياز از فايل هاي .dll خوانده شده و در ليست نگهداري مي شوند. براي نمايش دادن نام زبان ها در ComboBox كافي است اين ليست را به عنوان ItemsSource قرار دهيم:
comboboxLanguages.ItemsSource = CultureAssemblies;

با handle كردن رويداد selectionChanged مربوط به ComboBox زبان انتخاب شده را به متد LoadCulture كه در فايل App.cs (كلاس اصلي برنامه) تعريف كرده ايم ارسال مي كنيم:
//loads selected culture
public void LoadCulture(CultureAssemblyModel culture)
{
//setting current culture of applpications main thread to selected one.
System.Threading.Thread.CurrentThread.CurrentUICul ture = new System.Globalization.CultureInfo(culture.Culture);
System.Threading.Thread.CurrentThread.CurrentCultu re = new System.Globalization.CultureInfo(culture.Culture);
//creating a FileInfo object represents .dll file of selected cultur
FileInfo assemblyFile = new FileInfo("languages\\" + culture.Name);
//loading .dll into memory as a .net assembly
var assembly = Assembly.LoadFile(assemblyFile.FullName);
//getting .dll file name
var assemblyName = assemblyFile.Name.Substring(0, assemblyFile.Name.LastIndexOf("."));
//creating string represents structure of a pack uri (something like this: /{myassemblyname;component/myresourcefile.xaml}
string packUri = string.Format(@"/{0};component/{1}", assemblyName, culture.XamlFileName);
//creating a pack uri
Uri uri = new Uri(packUri, UriKind.Relative);
//now we have created a pack uri that represents a resource object in loaded assembly
//and its time to load that as a resource dictionary (do u remember that we had resource dictionary in culture assemblies? don't u?)
var dic = Application.LoadComponent(uri) as ResourceDictionary;
dic.Source = uri;
//here we will remove current merged dictionaries in our resource dictionary and add recently-loaded resource dictionary as e merged dictionary
var mergedDics = this.Resources.MergedDictionaries;
if (mergedDics.Count > 0)
mergedDics.Clear();
mergedDics.Add(dic);
}

void comboboxLanguages_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedCulture = (CultureAssemblyModel)comboboxLanguages.SelectedIt em;
App app = Application.Current as App;
app.LoadCulture(selectedCulture);
}



متد LoadCulture با گرفتن مشخصات زبان ارسال شده، culture سيستم را به زبان مورد نظر تغيير داده و resource dictionary مربوط به آن را از داخل فايل .dll آن زبان load كرده و آن را با resource dictionary فعلي برنامه جايگزين مي كند. Button ها كه محتواي خود را به شكل dynamic از داخل اين resource dictionary دريافت مي كردند حالا از داخل فايل جايگزين شده خواهند گرفت.

https://byfiles.storage.live.com/y1pLbKwcBj_ba4qFakRbLIMS3X3RfxeW-vkKaHLP3YZF-yDj64xZcY3Jn8HkRSki-Ddc19KbA901Ng/Capture9.PNG?psid=1

كار انجام شد!
از مزيت هاي اين روش مي توان به WPF-Native بودن، سادگي در پياده سازي، قابليت load كردن هر زبان جديدي در زمان اجرا بدون نياز به كوچك ترين تغيير در برنامه اصلي و همچنين پشتيباني كامل از نمايش زبان هاي مختلف از جمله فارسي اشاره كرد.

لینک اول PDF و لینک دوم نمونه برنامه هستند.

دوست عزیز کلا این قسمت رو از فایل های xaml زبون ها وردار
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Culture_fa-IR_Farsi.xaml"/>
</ResourceDictionary.MergedDictionaries>