PDA

View Full Version : گفتگو: ذخیره و بازیابی آیتمهای انتخاب شده از یک مجموعه CheckBox



ROSTAM2
شنبه 03 دی 1401, 21:20 عصر
به نام خدا.
سلام.

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

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

154390

و این هم بگم که 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
سه شنبه 06 دی 1401, 16:46 عصر
سلام و روز خوش

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

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
شنبه 10 دی 1401, 17:36 عصر
فرض کنید:
یک 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

154405

mazoolagh
شنبه 10 دی 1401, 17:44 عصر
همین فرم اگر آیتم های 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
شنبه 10 دی 1401, 17:51 عصر
همین فرم اگر 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