
Bài 14
DataGrid (phần II)
Dùng Dataview để Filter và Sort
Thường thường, khi điều khiển trong thời gian thật (real-time control), là
Operator, ta muốn các alarms có ưu tiên cao và mới xãy ra nhất được hiển
thị trên hết. Đôi khi, ta chỉ muốn thấy các alarm priority 3 (ưu tiên cao
nhất) mà thôi. Để thực hiện các việc nầy, ta dùng Dataview Object.Thay
vì dùng thẳng table alarm của DataSet alarmlist làm datasource của
DataGrid1, ta sẽ dùng một DataView derived from (đến từ) table alarm. Ta
có thể Sort (sắp theo thứ tự) các alarms/records theo Priority hay áp dụng
Filter (sàn lọc) vào DataView để chỉ thấy những thứ gì mình muốn, thí dụ
chỉ có alarms priority 3 thôi.Nên nhớ là nằm đàng sau vẫn là table alarm,
nhưng Dataview đóng vai trò cặp kiếng mát màu giúp cho ta thấy những
thứ gì và theo cách ta muốn. Mỗi khi ta thay một cặp kiếng, ta lại thấy
những thứ khác.Dưới đây là Sub BtnLoadXMLData_Click được sửa lại một
chút để dùng DataView:
Private Sub BtnLoadXMLData_Click(ByVal sender As
System.Object, ByVal e As System.EventArgs) _
Handles BtnLoadXMLData.Click
' Instantiate a DataSet type alarmlist
DS = New alarmlist()
' Load the XML data from file AlarmList.xml in the
source code folder. Note that the program EXE resides
' in the bin subfolder
DS.ReadXml("../AlarmList.xml")
' Bind the Datagrid DataSource to this new DataSet
table alarm
' DataGrid1.DataSource = DS.alarm
' Create a Dataview from DS
DV1 = New System.Data.DataView(DS.alarm)
' Sort alarms by priority, then datetime
' DESC stands for descending order,i.e. biggest on
top
DV1.Sort = "priority DESC, datetime DESC"
' Bind the Datagrid DataSource to Dataview
DataGrid1.DataSource = DV1
AddCustomDataTableStyle()

' Display the number of alarms in each priority
DisplayTotal()
End Sub
Để ý Dataview object DV1 được derived từ DS.alarm. Sau đó ta Sort các
alarms theo thứ tự ưu tiên, rồi trong số những alarm có cùng priority ta lại
Sort chúng theo datetime (ở đây data type của datetime chỉ là
string).Ngoài ra để đếm con số các alarms thuộc mỗi priority ta có thể
dùng Dataview với filter rồi xem property Count của nó như sau:
Private Sub DisplayTotal()
' Create a Dataview object from table DS.alarm
Dim DVP1 As New System.Data.DataView(DS.alarm)
' Apply filter
DVP1.RowFilter = "priority = 1"
' Display Count of records in this Dataview
NumPrio1.Text = "Prio1: " & DVP1.Count.ToString
Dim DVP2 As New System.Data.DataView(DS.alarm)
DVP2.RowFilter = "priority = 2"
NumPrio2.Text = "Prio2: " & DVP2.Count.ToString
Dim DVP3 As New System.Data.DataView(DS.alarm)
DVP3.RowFilter = "priority = 3"
NumPrio3.Text = "Prio3: " & DVP3.Count.ToString
NumTotal.Text = "Total: " &
DS.alarm.Rows.Count.ToString
Dim bmb As BindingManagerBase =
Me.BindingContext(DataGrid1.DataSource,
DataGrid1.DataMember)
NumDisplayed.Text = "Displayed: " &
bmb.Count.ToString
End Sub
Chắc bạn đã để ý thấy thay vì iterate qua mỗi record để đếm con số
alarms thuộc priority 1,2 hay 3, ta đã dùng ba Dataviews để filter ra
alarms thuộc ba priorities khác nhau rồi lấy trị số Count của mỗi Dataview.
Đây là lối lập trình dựa vào những gì có sẵn càng nhiều càng tốt để tránh
tạo ra bugs.Ngoài ra, để đếm con số hàng alarms được thật sự hiển thị bất
cứ lúc nào ta dùng BindingManagerBase object trong hai hàng code
dưới đây:
Dim bmb As BindingManagerBase =
Me.BindingContext(DataGrid1.DataSource,
DataGrid1.DataMember)
NumDisplayed.Text = "Displayed: " & bmb.Count.ToString
Ta đặt thêm ba buttons để filter alarms với code sau đây:

Private Sub Btn1and2_Click(ByVal sender As
System.Object, ByVal e As System.EventArgs) _
Handles Btn1and2.Click
DV1.RowFilter = "priority < 3"
Dim bmb As BindingManagerBase =
Me.BindingContext(DataGrid1.DataSource,
DataGrid1.DataMember)
NumDisplayed.Text = "Displayed: " &
bmb.Count.ToString
End Sub
Private Sub Btn1Only_Click(ByVal sender As
System.Object, ByVal e As System.EventArgs) _
Handles Btn1Only.Click
DV1.RowFilter = "priority = 1"
Dim bmb As BindingManagerBase =
Me.BindingContext(DataGrid1.DataSource,
DataGrid1.DataMember)
NumDisplayed.Text = "Displayed: " &
bmb.Count.ToString
End Sub
Private Sub BtnAllAlarms_Click(ByVal sender As
System.Object, ByVal e As System.EventArgs) _
Handles BtnAllAlarms.Click
DV1.RowFilter = ""
Dim bmb As BindingManagerBase =
Me.BindingContext(DataGrid1.DataSource,
DataGrid1.DataMember)
NumDisplayed.Text = "Displayed: " &
bmb.Count.ToString
End Sub
Bạn có thể chay chương trình và bấm các nút vừa mới thêm vào để xem
các alarms được filtered như thế nào.

Làm việc với một Row trong DataGrid
Khi một alarm mới được báo cáo và hiển thị, hệ thống điều khiển real-time
thường hay phát ra những tiếng Beep nho nhỏ để nhắc Operator xử lý sự
cố tạo ra alarm. Việc đầu tiên Operator sẽ làm là Acknowledge (xác
nhận là tôi biết rồi, khổ lắm, nói mãi!) cái alarm bằng cách right click lên
Row hiển thị alarm rồi click menuCommand Acknowledge từ
PopupMenu.Khi bạn đã Acknowledge một alarm rồi thì cái ACKN checkbox
sẽ được đánh dấu và nếu hệ thống không còn alarm nào chưa được
acknowledged thì nó sẽ ngừng Beep. Ngoài ra, có khi vì bạn biết là lý do
gây ra một alarm nào đó không quan trọng (thí dụ nhân viên kỹ thuật
đang sửa và thử cái sensor của alarm ấy) và bạn không muốn alarm ấy
được báo cáo trong tương lai, bạn có thể Isolate (cô lập hóa) nó. Khi nào
muốn cho nó hoạt động bình thường trở lại, bạn sẽ Enable (tác động)
nó.Bây giờ bạn hãy đặt một ContextMenu control vào form và Edit cho nó
ba menuCommands tên mnuAckn(Acknowledge),
mnuIsolate(Isolate) và mnuEnable(Enable) như trong hình dưới đây:

Mỗi khi user right click lên một hàng alarm, ContextMenu1 sẽ hiển thị chỉ
những menuCommands thích hợp với tình huống. Tức là nếu alarm chưa
được acknowledged thì mới có menuCommand Acknowledge, khi alarm
chưa bị isolated thì mới có menuCommand Isolate, nếu đã bị isolated rồi
thì chỉ có MenuCommand Enable.Bình thường, nếu bạn click lên một
checkbox còn trống trong DataGrid1, checkbox ấy sẽ được đánh dấu.
Nhưng trong chương trình của chúng ta tại đây ta không muốn cho user
làm việc ấy mà phải Acknowlege, Isolate hay Enable bằng PopupMenu. Do
đó bạn hãy cho property ReadOnly của DataGrid1 bằng True.Lúc
chương trình nhận được Event MouseDown từ DataGrid1 ta sẽ tìm cách
xác định lúc bấy giờ Mouse đang nằm trên alarm line nào bằng cách chạy
Method HitTest của DataGrid1. Khi DataGrid1 HitTest vị trí của Mouse với
instruction myGrid.HitTest(e.X, e.Y), nó sẽ cho ta một Object
HitTestInfo. Property Row của HitTestInfo là hàng thứ mấy trong
DataGrid1. Để lấy ra đúng DataRowView nào đang hiển thị ở
HitTestInfo.Row ấy ta phải dựa vào BindingManagerBase. Cái
DataRowView mà ta đang tìm chính là DataRowView của
BindingManagerBase với position bằng HitTestInfo.Row ấy.Trong chương
trình nầy, ta sẽ chứa DataRowView ấy trong variable drv. Dưới đây là
code để xử lý Event MouseDown của DataGrid1, để ý là ta hiển thị bên
dưới cái description của alarm được clicked bằng statement
Label1.Text = drv("description") để cho user một feedback:
' Variable used to store selected DataRowView

