# برنامه نویسی با محصولات مایکروسافت > برنامه نویسی مبتنی بر Microsoft .Net Framework > گفتگو: ذخیره و بازیابی آیتمهای انتخاب شده از یک مجموعه CheckBox

## ROSTAM2

*به نام خدا.
سلام.

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

در این تاپیک با استفاده از یک Enumeration به یک لیست ویو که خصوصیت CheckBoxes از اون True شده آیتم اضافه می شه و سپس با استفاده از رویداد ItemChecked از ListView خصوصیتی که از نوع همون Enumeration هست با توجه به آیتم های انتخابی(CheckedItems) مقدار دهی می شه.*
CheckedListView.jpg
*و این هم بگم که Enumeration یک Attribute داره و اون هم Flags هست و هر کدوم از اجزای اون هم یک Attribute برای گرفتن متنی که قراره در لیست نمایش داده بشه می گیرن.*
Visual Basic

    <Flags()> Public Enum Languages
        <AmbientValue("None")> None = 0
        <AmbientValue("C#‎‎‎‎‎‎‎‎‎‎")> CSharp = 1
        <AmbientValue("Visual Basic")> VB = 2
        <AmbientValue("SQL")> SQL = 4
        <AmbientValue("XML")> XML = 8
        <AmbientValue("HTML")> HTML = 16
        <AmbientValue("C++‎‎‎‎‎‎‎‎‎‎")> CPP = 32
    End Enum

C#‎‎‎‎‎‎‎‎‎‎

      [Flags]public enum Languages
        {
            [AmbientValue("None")]None = 0,
            [AmbientValue("C#‎‎‎‎‎‎‎‎‎‎")]CSharp =1,
            [AmbientValue("Visual Basic")]VB =2,
            [AmbientValue("SQL")]SQL=4, 
            [AmbientValue("XML")]XML =8,
            [AmbientValue("HTML")]HTML =16,
            [AmbientValue("C++‎‎‎‎‎‎‎‎‎‎")]CPP=32
         }


*برای نمایش آیتم ها در لیست (ListView) از رویداد Load از فرم استفاده شده:*
Visual Basic

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        Dim Value As Integer = 0
        Dim MemInf As MemberInfo
        Dim MemAttr As AmbientValueAttribute
               For Each member As String In [Enum].GetNames(GetType(Languages))
            Value = [Enum].Parse(GetType(Languages), member)
            If Value > 0 Then
                MemInf = GetType(Languages).GetMember(member).FirstOrDefaul  t
                MemAttr = MemInf.GetCustomAttributes(GetType(AmbientValueAtt  ribute), False).FirstOrDefault
                Me.listView1.Items.Add(MemAttr.Value.ToString).Tag = Value
            End If
        Next
    End Sub

C#‎‎‎‎‎‎‎‎‎‎

        private void Form1_Load(object sender, EventArgs e)
        {
            int Value;
            MemberInfo MemInf;
            AmbientValueAttribute MemAttr;
foreach (string member in Enum.GetNames(typeof(Languages)))
            {
             Value = (int)Enum.Parse(typeof(Languages), member);
          if (Value > 0) { 
           MemInf = typeof(Languages).GetMember(member).FirstOrDefault  ();
           MemAttr = (AmbientValueAttribute)MemInf.GetCustomAttributes(  typeof(AmbientValueAttribute), false).FirstOrDefault();
           this.listView1.Items.Add(MemAttr.Value.ToString())  .Tag = Value;
                }
            }
            }


*با فعال کردن CheckBox از هر آیتم تابع LanguagesValue مقدار آیتم های انتخاب شده رو برای خصوصیت Language فراهم می کنه:*
Visual Basic

    Function LanguagesValue() As Languages
        Dim value% = 0
        For Each item As ListViewItem In Me.listView1.CheckedItems
            value += CInt(item.Tag)
        Next
        Return DirectCast(value, Languages)
    End Function

C#‎‎‎‎‎‎‎‎‎‎

        Languages LanguagesValue ()
        {
            int value = 0;
            foreach (ListViewItem item in this.listView1.CheckedItems)
            {
              value += (int)item.Tag;
            }
return (Languages)value;
        }


*که در رویداد ItemChecked از ListView این دستور بکار گرفته شده:*
Visual Basic

    Private Sub listView1_ItemChecked(sender As Object, e As ItemCheckedEventArgs) Handles listView1.ItemChecked
        Me.Language = LanguagesValue()
        Me.Numberlabel.Text = CInt(Me.Language)
        Me.Nameslabel.Text = Me.Language.ToString
    End Sub

C#‎‎‎‎‎‎‎‎‎‎

        private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            this.Language = LanguagesValue();
             this.Numberlabel.Text = ((int)this.Language).ToString();
            this.Nameslabel.Text = this.Language.ToString();
        }


*تا الآن یک مقدار داریم که عددی هست و می تونیم اون عدد رو ذخیره کنیم (که همون مقدار خصوصیت Language به عدد تبدیل بشه) می تونیم برای بازیابی آیتم های انتخاب شده از اون عدد استفاده کنیم:*
Visual Basic

    Private LanguageValue As Languages
    Public Property Language() As Languages
        Get
            Return LanguageValue
        End Get
        Set(ByVal value As Languages)
            LanguageValue = value
        End Set
    End Property

C#‎‎‎‎‎‎‎‎‎‎

public Languages Language { get; set; }


*برای ذخیره مقدار عددی از یک متغیر استفاه شده که دسترسی به اون در سطح ماجول هست:*
Visual Basic

Dim SavedValue As Integer

C#‎‎‎‎‎‎‎‎‎‎

int SavedValue;


*برای مقدار دهی اون از یک کلید استفاده شده با این کد:*
Visual Basic

    Private Sub button1_Click(sender As Object, e As EventArgs) Handles button1.Click
        Me.SavedValue = CInt(Me.Language)
    End Sub

C#‎‎‎‎‎‎‎‎‎‎

        private void button1_Click(object sender, EventArgs e)
        {
            SavedValue = (int)this.Language;
       }


*و برای استفاده از مقدار ذخیره شده و فعال شدن مجدد آیتم های لیست طبق مقدار ذخیره شده از متود SetValue استفاده شده:*
Visual Basic

    Private Sub SetValue(value As Integer)
        Dim MemName As String
        For Each Item As ListViewItem In Me.listView1.Items
            MemName = [Enum].GetName(GetType(Languages), Item.Tag)
            Item.Checked = DirectCast(value, Languages).ToString.Contains(MemName)
        Next
    End Sub

C#‎‎‎‎‎‎‎‎‎‎

        void SetValue(int value)
        {
            foreach (ListViewItem item in this.listView1.Items)
            {
              item.Checked = ((Languages)value).ToString().Contains(((Languages  )item.Tag).ToString());
            }              
           }


*با این روش می تونید یک تک مقدار عددی داشته باشید که برای ذخیره و بازیابی آیتم های انتحاب شده یک مجموعه از CheckBox استفاده کنید.(در دیتابیس یک فیلد از جدول)*

----------


## mazoolagh

سلام و روز خوش

چون اجازه گفتگو و تبادل نظر رو دادین چند چیز هست که به ذهنم رسید:

1- در مورد استفاده از enum :
درست هست و جواب میده ولی در واقع ابزاری هست که در سطح کدنویسی استفاده میشه و پیاده سازی این خواسته عملا نیازی به enum نداره!
بخصوص که این کار بنوعی hard-code هست و در زمان اجرا نمیشه اون رو تغییر داد، بنابراین هر تغییری در انتخاب ها نیاز به ویرایش enum داره.
در حالی که میشه انتخاب ها رو در یک جدول تعریف کرد و در فرم هم بصورت دینامیک ساخت.
از طرفی در این مورد نیاز به استفاده از attribute هم هست (چون نمیشه فرضا ++c رو مستقیم تعریف کرد) و خوندن آتریبیوت های enum مکافات داره و کدش هم بیخودی پیچیده است.

2- در مورد AmbientValue :
راستش نمیدونم این کارش دقیقا چی هست ولی فکر کنم آتریبیوت description اینجا مناسبتر باشه:
<Flags()>
Public Enum Languages As Byte
    <Description("C#‎‎‎‎‎‎‎‎‎")>           CSharp = &B_000001
    <Description("Visual Basic")> VB     = &B_000010
    <Description("SQL")>          SQL    = &B_000100
    <Description("XML")>          XML    = &B_001000
    <Description("HTML")>         HTML   = &B_010000
    <Description("C++‎‎‎‎‎‎‎‎‎")>          CPP    = &B_100000
End Enum
البته کد خوندن اون در اساس تغییری نمیکنه.
مقادیر رو هم بصورت باینری نوشتم که flag ها رو بهتر نشون بده.

2- در مورد انتخاب بین CheckedListBox یا ListView :
فکر کنم شما لیست ویو رو به این خاطر انتخاب کردین که آیتم checkedlistbox نمیتونه value رو ذخیره کنه،
البته لیست ویو هم نمیتونه و به همین رو  valueها رو در tag ریختین.
ولی راستش checkedlistbox میتونه value هر آیتم رو نگه داری کنه!
CheckedListBox.displaymemeber= "Name"
CheckedListBox.valuememeber= "Value"
CheckedListBox.items.add(new with {.Value= &B_00000001 , .Name= "C#‎‎‎"})

این valuemember و displaymember (و همچنین datasource) در دیزاینر دیده نمیشه (نمیدونم چرا) ولی در intellisense هست و کار هم میکنه.

همینجور که دیده میشه مقادیر Name و Value رو میشه از هر جایی خوند و الزاما نیازی به enum (و آتریبیوت برای اون) نیست.
سعی میکنم فردا یک نمونه هم اینجا پیوست کنم.

----------


## mazoolagh

فرض کنید:
 یک checkedlistbox خالی به اسم CLB_Languages 
و 2 تکسباکس TB_Value و TB_Text برای نمایش مقدار و لیست آیتمهای انتخاب شده
و تکسباکس TB_SetValue برای گرفتن مقدار عددی و تیک زدن آیتمهای متناظر با آن
رو روی فرم داریم.

در این صورت کل کد این مساله مشابه زیر خواهد بود:
Imports System.ComponentModel


Public Class Enum_Unbound_CheckedListBox


    <Flags()>
    Public Enum Languages As Byte
        <Description("C#‎‎‎‎‎")>           CSharp = &B_000001
        <Description("Visual Basic")> VB     = &B_000010
        <Description("SQL")>          SQL    = &B_000100
        <Description("XML")>          XML    = &B_001000
        <Description("HTML")>         HTML   = &B_010000
        <Description("C++‎‎‎‎‎")>          CPP    = &B_100000
    End Enum

    Private Sub Enum_Unbound_CheckedListBox_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        init()
    End Sub


    Private Sub init()
        With CLB_Languages
            If .Items.Count > 0 Then Exit Sub
            .DisplayMember = "Name"
            .ValueMember = "Value"
            Dim LangType As Type = GetType(Languages)
            Dim Name, Description As String
            For Each Value In System.Enum.GetValues(LangType)
                Name = System.Enum.GetName(LangType, Value)
                Description = LangType.GetMember(Name)(0).GetCustomAttributes(Ge  tType(DescriptionAttribute), False)(0).Description
                CLB_Languages.Items.Add(New With {.Name = Description, .Value = Value})
            Next
        End With
    End Sub
    
    Private Sub CLB_Languages_SelectedValueChanged(sender As Object, e As EventArgs) Handles CLB_Languages.SelectedValueChanged
        TB_Value.Text = GetValue()
        TB_Text.Text = GetText()
        'Console.Write(GetValue)
        'Console.WriteLine(" = " & GetText())
    End Sub
    
    Private Sub SetValue(Value As Byte)
        Try
            With CLB_Languages
                For i = 0 To .Items.Count - 1
                    .SetItemChecked(i, CBool(Value And .Items(i).Value))
                Next
            End With
            TB_Value.Text = GetValue()
            TB_Text.Text = GetText()
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical + MsgBoxStyle.SystemModal, Nothing)
        End Try
    End Sub
    
    Private Function GetValue() As Byte
        Dim value As New Byte
        For Each chkitm In CLB_Languages.CheckedItems
            value += chkitm.Value
        Next
        Return value
    End Function
    
    Public Function GetText() As String
        Dim s As New List(Of String)
        For Each chkitm In CLB_Languages.CheckedItems
            s.Add(chkitm.Name)
        Next
        Return String.Join(",", s.ToArray)
    End Function
    
    Private Sub BTN_SetValue_Click(sender As Object, e As EventArgs) Handles BTN_SetValue.Click
        Try
            SetValue(CByte(Me.TB_SetValue.Text))
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical + MsgBoxStyle.SystemModal, Nothing)
        End Try
    End Sub
End Class

enum.png

----------


## mazoolagh

همین فرم اگر آیتم های checkedlistbox با کد پر بشن (نه از enum)

Public Class Unbound_CheckedListBox
    
    Private Sub Unbound_CheckedListBox_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        init()
    End Sub
    
    Private Sub init()
        With CLB_Languages
            If .Items.Count > 0 Then Exit Sub
            .DisplayMember = "Name"
            .ValueMember = "Value"


            .Items.Add(New With {.Value = &B_00000001, .Name = "C#‎‎‎"})
            .Items.Add(New With {.Value = &B_00000010, .Name = "Visual Basic"})
            .Items.Add(New With {.Value = &B_00000100, .Name = "SQL"})
            .Items.Add(New With {.Value = &B_00001000, .Name = "XML"})
            .Items.Add(New With {.Value = &B_00010000, .Name = "HTML"})
            .Items.Add(New With {.Value = &B_00100000, .Name = "C++‎‎‎"})
        End With
    End Sub
    
    Private Sub CLB_Languages_SelectedValueChanged(sender As Object, e As EventArgs) Handles CLB_Languages.SelectedValueChanged
        TB_Value.Text = GetValue()
        TB_Text.Text = GetText()
        Console.Write(GetValue)
        Console.WriteLine(" = " & GetText())
    End Sub
    
    Private Sub SetValue(Value As Byte)
        Try
            With CLB_Languages
                For i = 0 To .Items.Count - 1
                    .SetItemChecked(i, CBool(Value And .Items(i).Value))
                Next
            End With
            TB_Value.Text = GetValue()
            TB_Text.Text = GetText()
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical + MsgBoxStyle.SystemModal, Nothing)
        End Try
    End Sub
    
    Private Function GetValue() As Byte
        Dim value As New Byte
        For Each chkitm In CLB_Languages.CheckedItems
            value += chkitm.Value
        Next
        Return value
    End Function
    
    Public Function GetText() As String
        Dim s As New List(Of String)
        For Each chkitm In CLB_Languages.CheckedItems
            s.Add(chkitm.Name)
        Next
        Return String.Join(",", s.ToArray)
    End Function
    
    Private Sub BTN_SetValue_Click(sender As Object, e As EventArgs) Handles BTN_SetValue.Click
        Try
            SetValue(CByte(Me.TB_SetValue.Text))
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical + MsgBoxStyle.SystemModal, Nothing)
        End Try
    End Sub
End Class

----------


## mazoolagh

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

Public Class Bound_CheckedListBox
    
    Dim dt As New DataTable
    
    Private Sub Bound_CheckedListBox_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        init()
    End Sub
    
    Private Sub init()
        If CLB_Languages.Items.Count > 0 Then Exit Sub
        dt.Columns.Add("Name", GetType(System.String))
        dt.Columns.Add("Value", GetType(System.Byte))
        CLB_Languages.DataSource = dt
        CLB_Languages.DisplayMember = "Name"
        CLB_Languages.ValueMember = "Value"
        AddItem("C#‎‎‎‎‎",           &B_00000001)
        AddItem("Visual Basic", &B_00000010)
        AddItem("SQL",          &B_00000100)
        AddItem("XML",          &B_00001000)
        AddItem("HTML",         &B_00010000)
        AddItem("C++‎‎‎‎‎    ",      &B_00100000)
    End Sub
    
    Private Sub AddItem(name As String, value As Byte)
        Dim dr As DataRow = dt.NewRow
        dr("Name") = name
        dr("Value") = value
        dt.Rows.Add(dr)
    End Sub
    
    Private Sub CLB_Languages_SelectedValueChanged(sender As Object, e As EventArgs) Handles CLB_Languages.SelectedValueChanged
        TB_Value.Text = GetValue()
        TB_Text.Text = GetText()
        'Console.Write(GetValue)
        'Console.WriteLine(" = " & GetText())
    End Sub
    
    Private Sub SetValue(Value As Byte)
        Try
            With CLB_Languages
                For i = 0 To .Items.Count - 1
                    .SetItemChecked(i, CBool(Value And .Items(i)("Value")))
                Next
            End With
            TB_Value.Text = GetValue()
            TB_Text.Text = GetText()
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical + MsgBoxStyle.SystemModal, Nothing)
        End Try
    End Sub
    
    Private Function GetValue() As Byte
        Dim value As New Byte
        For Each chkitm In CLB_Languages.CheckedItems
            value += chkitm("Value")
        Next
        Return value
    End Function
    
    Public Function GetText() As String
        Dim s As New List(Of String)
        For Each chkitm In CLB_Languages.CheckedItems
            s.Add(chkitm("Name"))
        Next
        Return String.Join(",", s.ToArray)
    End Function
    
    Private Sub BTN_SetValue_Click(sender As Object, e As EventArgs) Handles BTN_SetValue.Click
        Try
            SetValue(CByte(Me.TB_SetValue.Text))
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical + MsgBoxStyle.SystemModal, Nothing)
        End Try
    End Sub


End Class

----------

