نمایش نتایج 1 تا 2 از 2

نام تاپیک: مشکل اعتبارسنجی Binding در DataGrid

  1. #1

    مشکل اعتبارسنجی Binding در DataGrid

    سلام دوستان .
    یه DataGrid دارم که به یه ObservableCollection<Person> ، بایندینگ (Binding) شد .
    کلاس Person ام پروپرتی هایی از قبیل PersonID و FirstName و LastName و PhoneNumber و NationalCode داره .

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

    طبق آموزش در لینک زیر :

    How to: Implement Validation with the DataGrid Control - WPF .NET Framework | Microsoft Learn

    --------

    من این کارها را انجام دادم اما مشکل اینه که وقتی اطلاعات یه سطر را وارد کردم و دکمه ی Enter در کیبرد را زدم (یعنی اطلاعات سطر وارد شد) ، در متد Validate در کلاس DataGridItemsSource_Persons_ValidationRule (در کد زیر) ، مقدار متغییر person در این متد را وقتی میگیرم ، مقدارش ، همون اطلاعاتی نیست که کاربر وارد کرده بود .

    بلکه مقدار متغییر person ،اطلاعاتی هست که (تا اون لحظه که در متد Validate هست) ، درون همون Binding Source Object بود . یعنی تا زمانی که اون متد اجرا بشه ، اطلاعات آیتم مورد نظر در پروپرتیِ Persons (در کلاس PhoneBook) را بهم نشون میده که این اطلاعات از قبل بود و برام مهم نیست .

    بلکه من در متد Validate ، اطلاعاتی که کاربر ویرایش کرد را نیاز دارم تا تصمیم بگیرم که آیا مقدار جدیدی که وارد شد ، معتبر هست یا نه .

    کد xaml در MainWindow.xaml :


    <Window x:Class="WPF_Practice_2.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"
    xmlns:local="clr-namespace:WPF_Practice_2"
    xmlns:localClasses="clr-namespace:WPF_Practice_2.Classes"
    xmlns:bindingValidations="clr-namespace:WPF_Practice_2.Classes.BindingValidation s"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">


    <Window.Resources>


    <localClasses:ViewModel x:Key="ViewModelKey"/>


    <CollectionViewSource x:Key="PersonsCollectionViewSourceKey"
    Source="{Binding
    Source={StaticResource ViewModelKey},
    Path=PhoneBook.Persons}"/>


    <ControlTemplate TargetType="{x:Type Control}" x:Key="PersonValidationRulesControlTemplateKey">
    <Grid Margin="0,-2,0,-2"
    ToolTip="{Binding RelativeSource={RelativeSource
    FindAncestor, AncestorType={x:Type DataGridRow}},
    Path=(Validation.Errors)[0].ErrorContent}">
    <Ellipse StrokeThickness="0" Fill="Red"
    Width="{TemplateBinding FontSize}"
    Height="{TemplateBinding FontSize}" />
    <TextBlock Text="!" FontSize="{TemplateBinding FontSize}"
    FontWeight="Bold" Foreground="White"
    HorizontalAlignment="Center" />
    </Grid>
    </ControlTemplate>


    </Window.Resources>




    <Grid DataContext="{Binding
    Source={StaticResource PersonsCollectionViewSourceKey}}">


    <DataGrid Name="PhoneBookDataGrid" HorizontalAlignment="Center" Height="271"
    Margin="0,153,0,0" VerticalAlignment="Top" Width="780"
    RowValidationErrorTemplate="{StaticResource PersonValidationRulesControlTemplateKey}"
    ItemsSource="{Binding}"


    <DataGrid.RowValidationRules>
    <bindingValidations:DataGridItemsSource_Persons_Va lidationRule/>
    </DataGrid.RowValidationRules>

    </DataGrid>

    </Grid>
    </Window>




    کد کلاس اعتبار سنجی :


    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;


    namespace WPF_Practice_2.Classes.BindingValidations
    {
    public class DataGridItemsSource_Persons_ValidationRule : ValidationRule
    {
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
    BindingGroup bindingGroup = value as BindingGroup;
    if (bindingGroup == null)
    return new ValidationResult(false, "عمل تبدیل ، ناموفق بود .");
    Person person = bindingGroup.Items[0] as Person;
    if (person == null)
    return new ValidationResult(false, "عمل تبدیل ، ناموفق بود .");




    if (person.PersonID < 0)
    return new ValidationResult(false, "شناسه باید عددی مثبت باشد");


    return ValidationResult.ValidResult;
    }






    }
    }





    بقیه ی کدهای کلاس های سی شارپ :


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Navigation;


    namespace WPF_Practice_2.Classes
    {
    public class Person ////: IDataErrorInfo
    {
    public int PersonID { get; set; }


    public string FirstName { get; set; }


    public string LastName { get; set; }


    public long PhoneNumber { get; set; }


    public string NationalCode { get; set; }








    public Person()
    {


    }




    public Person(string firstName, string lastName, long phoneNumber, string nationalCode)
    {
    this.FirstName = firstName;
    this.LastName = lastName;
    this.PhoneNumber = phoneNumber;
    this.NationalCode = nationalCode;
    }




    public Person(int personID, string firstName, string lastName, long phoneNumber, string nationalCode)
    {
    this.PersonID = personID;
    this.FirstName = firstName;
    this.LastName = lastName;
    this.PhoneNumber = phoneNumber;
    this.NationalCode = nationalCode;
    }


    }
    }




    و


    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;


    namespace WPF_Practice_2.Classes
    {
    public class PhoneBook
    {


    public ObservableCollection<Person> Persons { get; set; }


    public PhoneBook(ObservableCollection<Person> persons)
    {
    this.Persons = persons;
    }






    }
    }


    و


    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;


    namespace WPF_Practice_2.Classes
    {
    public class ViewModel
    {
    public PhoneBook PhoneBook { get; set; }


    public ViewModel()
    {
    ObservableCollection<Person> persons = new ObservableCollection<Person>();
    Person person = new Person(574, "احمد", "متوسلیان", 109854, "574");
    Person person2 = new Person(65, "غلامحسین", "افشردی", 58, "12");
    Person person3 = new Person(84, "محمد", "جهان آرا", 09115478, "00");
    Person person4 = new Person(313, "قاسم", "سلیمانی", 98, "");
    Person person5 = new Person(879, "سید مجتبی", "میر لوحی", 42515874, "7744");


    persons.Add(person);
    persons.Add(person2);
    persons.Add(person3);
    persons.Add(person4);
    persons.Add(person5);


    this.PhoneBook = new PhoneBook(persons);
    }




    }
    }



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


    من حتی از رویداد RowEditEnding در شی DataGrid هم استفاده کردم . این رویداد ، قبل از اینکه متد Validate (برای اعتبارسنجی) اجرا میشه اما دقیقا مثل متد Validate ، اطلاعاتی که کاربر برای اون سطر را (در رویداد RowEditEnding و در متغییر رویدادیِ e.Row.Item) بهم نمیده . بلکه این هم اطلاعات همون سطری که فعلا در پروپرتیِ Persons در کلاس PhoneBook هست را میده که اون اطلاعات اصلا ملاکم نیست و به دردم نمیخوره .

    برای شما هم این رویداد ، این طوره؟
    یعنی زمانی که کاربر مقادیر سطری را ویرایش یا اضافه کرد ، نشون نمیده؟

    ------

    بعد هم در همون Binding ، پروپرتیِ UpdateSourceTrigger را روی مقدار Explicit گذاشتم :


    ItemsSource="{Binding UpdateSourceTrigger=Explicit}"


    تا خودم با فراخونی متد BindingExpression.UpdateSource ، بعد از اینکه اطلاعات وارد شده ی کاربر را گرفتم ، اون متد را فراخوانی کنم تا Binding اش ، مقدار Target Object را برای Source Object ارسال کنه اما به احتمال زیاد چون (از طرف DataGrid) ، مقدار Binding را به کل کالکشن انجام دادم اما از اون طرف ، ,وقتی ، سطری اضافه یا ویرایش میشه ، کلِ شیِ کالکشن در Binding Source Object ، تغییری نمیکنه بلکه فقط سطرها که همون آیتم های اون کالکشن هستند ، تغییر میکنن ، احتمالا به همین دلیل تغییر در مقدارِ پروپرتیِ UpdateSourceTrigger ام تاثیری در رفتارش نداره .

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


    نهایتا اینکه دوستان به نظرتون چی کار کنم؟
    فرضا اگه میشه ، توی همون متد Validate یا اگه نشد ، حتی در رویداد RowEditEnding بتونم به مقداری که کاربر ویرایش یا اضافه کرد ، دسترسی پیدا کنم؟
    یا روش های دیگه برای اعتبار سنجی ، چه راهکاری پیشنهاد میدین؟

    دلیلش چیه که در متد Validate یا در رویداد RowEditEnding ، اطلاعاتی که کاربر ویرایش کرد را نمیده و آیا برای شما هم این دو تا متد و رویداد ، همینطوره؟

    و همچنین راهکارتون چیه؟


    ------

    روش پیاده سازی اینترفیس IDataErrorInfo را برای کالکشن انجام دادم اما هم اصلا اجرا نشد و اگه هم اجرا بشه ، نمیدونم در اون حالت برای کدوم آیتمِ کالکشن ، تغییرات اتفاق میافته .

    - روش دیگه اینکه بذارم اول ، Binding Source Object و کلا آیتم های کالکشن ام ، اول تغییرات روشون اِعمال بشن و بعد اعتبارسنجی انجام بشه (مثل همونی که در لینک مایکروسافت گفت و مقدار پروپرتی ValidationStep ئه ValidationRule (در کد من ، همون کلاس DataGridItemsSource_Persons_ValidationRule) را به مقدار UpdatedValue تغییر بدم تا این اتفاق بیفته) و بعد بیام اعتبار سنجی را انجام بدم

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

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

    روش دیگه ، شاید از Converter ها بشه استفاده کرد (تست نکردم) اما اگه هم بشه ، اصولی نیست .

    تشکر دوستان .

  2. #2

    نقل قول: مشکل اعتبارسنجی Binding در DataGrid

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

    مقدارِ این TextBox که شامل اطلاعاتی که کاربر ، ویرایش کرد ، هست ، نهایتا فقط تا زمانی که رویدادِ CellEditEnding ئه DataGrid اجرا بشه (توسط متغییرِ رویدادیِ e.EditingElement که بصورت پیش فرض ، اگه خودمون ControlTemplate ای ندیم ، از نوع TextBox هست) ، در دسترس هست . یعنی نهایتا تا همون موقع و همون رویداد میتونیم متوجه بشیم که کاربر ، چه اطلاعاتی را وارد و یا ویرایش کرد .

    یعنی حتی در رویداد RowEditEnding هم این اطلاعات دیگه در دسترس مون نیست .
    CellEditEnding ، زمانی هست که کاربر ، یک سلول را ویرایش کرد و کارش تمام شد (با unfocuse شدنِ سلول و هر جور دیگه ای) .
    RowEditEnding هم زمانی که ویرایش یک سطر ، به اتمام رسید .
    هر دو رویداد ، قبل از اجرای متد Validate برای اعتبارسنجی ، اجرا میشن .

    بنابراین یکی از راهکارها اینه که تا زمانی که درون رویداد CellEditEnding هستیم ، چون به متد Validate ، مقدارِ DataGrid.Items.CurrentItem مون (که در مثال بالا همون شیِ Person هست) ، ارسال میشه (به عنوان مقدار پارامتر value که از نوع BindingGroup و عضوِ Items[0] اش) ، پس اعضای این CurrentItem را به مقداری که کاربر درون اون TextBox قرار میده ،تغییر بدیم .

    ------

    یا بهتر اینکه (برای اینکه پروپرتیِ CurrentItem تغییری نکنه چون ممکنه یه جای دیگه مشکل ساز بشه و ندونیم) ، در این رویدادِ CellEditEnding ، اطلاعات را بگیریم و ذخیره کنیم و در زمانی که در رویداد RowEditEnding هستیم ، اون را برای متغییر یا پروپرتی استاتیک ای که در کلاس DataGridItemsSource_Persons_ValidationRule تعریف کردیم ، بفرستیم و بعد ، مقدار اون عضو استاتیک را در متد Validate ، زمانی که عملیات با موفقیت انجام شد (یعنی سر آخر) قبل از اینکه از متد خارج بشیم ، null کنیم .


    البته برای اینکه MVVM نقض نشه ، میشه در کلاسی که از ValidationRule ارث میبره (در اینجا کلاس DataGridItemsSource_Persons_ValidationRule) ، متدهای استاتیکی ساخت و وظایف مقداردهی ها را توسط این متدها به لایه ی ViewModel منتقل کرد (چون این کلاس در لایه ی ViewModel هست) ، پس روش زیر پیشنهاد میشه :

    در MainWindow.xaml :


    <Window x:Class="WPF_Practice_2.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"
    xmlns:local="clr-namespace:WPF_Practice_2"
    xmlns:localClasses="clr-namespace:WPF_Practice_2.Classes"
    xmlns:bindingValidations="clr-namespace:WPF_Practice_2.Classes.BindingValidation s"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">


    <Window.Resources>


    <localClasses:ViewModel x:Key="ViewModelKey"/>


    <CollectionViewSource x:Key="PersonsCollectionViewSourceKey"
    Source="{Binding
    Source={StaticResource ViewModelKey},
    Path=PhoneBook.Persons}"/>

    <ControlTemplate TargetType="{x:Type Control}"
    x:Key="PersonValidationRulesControlTemplateKey">
    <Grid Margin="0,-2,0,-2" Background="Transparent"
    ToolTip="{Binding RelativeSource={RelativeSource FindAncestor,
    AncestorType={x:Type DataGridRow}},
    Path=(Validation.Errors)[0].ErrorContent}">


    <Ellipse StrokeThickness="0" Fill="Red" Width="{TemplateBinding FontSize}"
    Height="{TemplateBinding FontSize}"/>
    <TextBlock Text="!" FontSize="{TemplateBinding FontSize}" FontWeight="Bold"
    Foreground="White" HorizontalAlignment="Center" />


    </Grid>
    </ControlTemplate>


    </Window.Resources>



    <Grid DataContext="{Binding
    Source={StaticResource PersonsCollectionViewSourceKey}}">


    <DataGrid Name="PhoneBookDataGrid" HorizontalAlignment="Center" Height="271"
    Margin="0,153,0,0" VerticalAlignment="Top" Width="780"
    AutoGenerateColumns="False"
    ItemsSource="{Binding ValidatesOnExceptions=True}"
    RowValidationErrorTemplate="{StaticResource PersonValidationRulesControlTemplateKey}"
    CellEditEnding="PhoneBookDataGrid_OnCellEditEnding "
    RowEditEnding="PhoneBookDataGrid_OnRowEditEnding"
    AlternationCount="{Binding Count}">


    <DataGrid.RowValidationRules>
    <bindingValidations:DataGridItemsSource_Persons_Va lidationRule/>
    </DataGrid.RowValidationRules>


    <DataGrid.Columns>
    <DataGridTextColumn Header="ردیف" IsReadOnly="True"
    Binding="{Binding AlternationIndex,
    RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>


    <DataGridTextColumn Header="شناسه"
    Binding="{Binding Path=PersonID, ValidatesOnExceptions=True}"/>


    <DataGridTextColumn Header="نام"
    Binding="{Binding Path=FirstName, ValidatesOnExceptions=True}"/>


    <DataGridTextColumn Header="نام خانوادگی"
    Binding="{Binding Path=LastName, ValidatesOnExceptions=True}"/>


    <DataGridTextColumn Header="شماره تماس"
    Binding="{Binding Path=PhoneNumber, ValidatesOnExceptions=True}"/>


    <DataGridTextColumn Header="کد ملی"
    Binding="{Binding Path=NationalCode, ValidatesOnExceptions=True}"/>
    </DataGrid.Columns>


    </DataGrid>

    </Grid>
    </Window>




    در MainWindow.xaml.cs :


    public partial class MainWindow : Window
    {
    /// <summary>
    /// برای فلگ کردن اینکه وقتی که رویداد RowEditEnding میرسد ، آیا قبل از آن ، سلول مربوط به آن سطر جاری تغییر کرد که باعث اجرای این رویداد شد
    /// تا برای اعتبار سنجی ارسال شود یا نه .
    /// </summary>
    private bool doesCurrentRowCellChangeFlag = false;








    private void PhoneBookDataGrid_OnCellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
    {
    if (this.doesCurrentRowCellChangeFlag == false)
    this.doesCurrentRowCellChangeFlag = true;


    DataGridItemsSource_Persons_ValidationRule.CellEdi tEnding(this.PhoneBookDataGrid.Items.CurrentItem,
    e.Column.SortMemberPath, ((TextBox) e.EditingElement).Text);
    }




    private void PhoneBookDataGrid_OnRowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
    {
    /// ممکنه این رویداد ، علاوه بر زمان ویرایش سلول ، مجددا در زمانی که سلول را که ویرایش کرده بودیم و فوکوس را روی یه کنترل دیگه میخوایم منتقل کنیم ،
    /// مجددا این رویداد اجرا بشه . این شرط ، برای جلوگیری از اجرای این رویداد در اون موقع هست تا عمل binding را به model منتقل نکنه .
    if (this.doesCurrentRowCellChangeFlag == false)
    {
    e.Cancel = true;
    return;
    }


    DataGridItemsSource_Persons_ValidationRule.RowEdit Ending();
    this.doesCurrentRowCellChangeFlag = false;
    }






    }




    ---

    در کلاس DataGridItemsSource_Persons_ValidationRule که در لایه ی ViewModel باشه (نوع کلاس Person ، در لایه ی Model تعریف شده) :



    public class DataGridItemsSource_Persons_ValidationRule : ValidationRule
    {
    private static Person editValidatePerson = null;


    private static Person tempEditValidatePerson = null;








    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
    /// اگر مقدار EditValidatePerson برابر با null بود ، پس یعنی اعتبار سنجی ، یکطرفه و از طرف Model داره به View منتقل میشه ، پس همه را معتبر بزنه .
    if (editValidatePerson == null)
    return ValidationResult.ValidResult;


    if (editValidatePerson.PersonID < 0)
    return new ValidationResult(false, "شناسه باید عددی مثبت باشد");


    /// فقط در این قسمت ، یعنی در صورت موفقیت آمیز بودن عمل ثبت ، null میکنیم چون اگر موفقیت آمیز نباشد ، اطلاعات پاک نشود
    /// و امکان ویرایشِ مجددِ آخرین اطلاعاتِ وارد شده ، وجود داشته باشد .
    tempEditValidatePerson = null;
    editValidatePerson = null;
    return ValidationResult.ValidResult;
    }






    /// <summary>
    /// نوع پارامتر person باید از نوع Person باشد .
    /// برای این اینجا obnect تعریف شده چون برای ارسال از View به اینجا ، نیاز به تبدیل نباشد تا MVVM نقض شود .
    /// </summary>
    public static void CellEditEnding(object currentRowPerson, string editedPersonItemPropertyName, string editedPersonItemValue)
    {
    Person tempPerson = currentRowPerson as Person;
    if (tempPerson == null)
    throw new Exception("person");


    if (tempEditValidatePerson == null)
    {
    tempEditValidatePerson = new Person(tempPerson.PersonID, tempPerson.FirstName, tempPerson.LastName,
    tempPerson.PhoneNumber, tempPerson.NationalCode);
    }


    if (editedPersonItemPropertyName == "PersonID")
    {
    int personID = -1;
    if (int.TryParse(editedPersonItemValue, out personID) == true)
    tempEditValidatePerson.PersonID = personID;
    }
    else if (editedPersonItemPropertyName == "FirstName")
    tempEditValidatePerson.FirstName = editedPersonItemValue;
    else if (editedPersonItemPropertyName == "LastName")
    tempEditValidatePerson.LastName = editedPersonItemValue;
    else if (editedPersonItemPropertyName == "PhoneNumber")
    {
    int phoneNumber = -1;
    if (int.TryParse(editedPersonItemValue, out phoneNumber) == true)
    tempEditValidatePerson.PhoneNumber = phoneNumber;
    }
    else if (editedPersonItemPropertyName == "NationalCode")
    tempEditValidatePerson.NationalCode = editedPersonItemValue;
    }




    public static void RowEditEnding()
    {
    editValidatePerson = tempEditValidatePerson;
    }




    }


    ولی واقعا این بدیهی هست که باید DataGrid جوری طراحی میشد که در متد Validate ، اطلاعاتی که قراره ثبت بشه را در اختیارمون بذارن .
    این چه طراحی ای بود ، تعجب میکنم!!

تاپیک های مشابه

  1. WPF DataGrid Binding
    نوشته شده توسط ali_up1 در بخش دسترسی به داده ها (ADO.Net و LINQ و ...)
    پاسخ: 3
    آخرین پست: یک شنبه 01 تیر 1393, 11:53 صبح
  2. سوال: عدم نمایش اطلاعات در Binding Navigator با استفاده از Binding Source.
    نوشته شده توسط mjzsoft25 در بخش C#‎‎
    پاسخ: 1
    آخرین پست: چهارشنبه 08 شهریور 1391, 16:28 عصر
  3. binding source , binding manager
    نوشته شده توسط odiseh در بخش VB.NET
    پاسخ: 3
    آخرین پست: جمعه 11 آبان 1386, 09:23 صبح

قوانین ایجاد تاپیک در تالار

  • شما نمی توانید تاپیک جدید ایجاد کنید
  • شما نمی توانید به تاپیک ها پاسخ دهید
  • شما نمی توانید ضمیمه ارسال کنید
  • شما نمی توانید پاسخ هایتان را ویرایش کنید
  •