编写Asp.net的同学,经常会遇到一个Repeater 或者一个GridView ,当用户点击编辑状态时我们的列表组件会自动跳转到可选择(可供删除、编辑、选择等)状态。这时候一般的做法都会在组件的前方自动生成一系列复选框“CheckBox”,需要删除/选择哪行时只要在前方的复选框勾一下,便可以得到该行的数据或者行ID等。
上面的做法是一个比较典型的Web做法,那么在WP7 里面要实现这样的效果如何实现呢?有些同学就会说了,那简单使用ListBox 在它的数据模板里面添加一个CheckBox不就完事了吗?是的,这样是一种做法,但带来的问题是你得去控制他选中哪行并且得到哪行的ID,并且在WP7 有限的屏幕中这种做法比较不妥,当用户想做选择时,我们才让对应的行有可供选择的状态才更佳。而这种做法在传统的WP7控件中,是没有的。因为我们必须时时去控制它的复选框显示或者隐藏,但在这里我推荐大家一个组件,自带CheckBOx并且默认有两种状态,一种为普通状态即呈现数据显示给用户,如下图:
另外一种状态为可选择状态,即用户可以对相应的行做删除等操作,如下图:
该组件的下载地址为:WindowsPhoneListBoxWithCheckBoxesControl
下面给出该组件的详细用法:
做过.Net 开发的对于如何使该组件的应该很清楚,这里将跳过此步骤。
如上图,该组件编写的XAML代码为如下:
<
my:ListBoxWithCheckBoxes
Name
="listBoxWithBoxes"
Margin
="0,0,0,0"
ItemsSource
="
{Binding SimpleModels}
"
>
<
ListBox.ItemTemplate
>
<
DataTemplate
>
<
StackPanel
Orientation
="Horizontal"
Margin
="0,0,0,20"
>
<
Rectangle
Height
="100"
Width
="100"
Fill
="#FFE5001b"
Margin
="12,0,9,0"
/>
<
StackPanel
>
<
TextBlock
Text
="
{Binding Name}
"
TextWrapping
="Wrap"
Style
="
{StaticResource PhoneTextLargeStyle}
"
/>
<
TextBlock
Text
="
{Binding Description}
"
TextWrapping
="Wrap"
Margin
="12,-6,12,0"
Style
="
{StaticResource PhoneTextSubtleStyle}
"
/>
</
StackPanel
>
</
StackPanel
>
</
DataTemplate
>
</
ListBox.ItemTemplate
>
</
my:ListBoxWithCheckBoxes
>
如上代码的数据模板,并未出现有CheckBox 控件,因为该组件己经将CheckBox控件整合在里面的选择状态中了。下面是具体如何为该组件添加数据。
首先该组件对应的行有标题和描述,这个在上面XAML代码中的数据模板可以看得出,查看该组件的ItemSource ,一起来看看它的代码是如何编写的:
public
class
SimpleModel : INotifyPropertyChanged
{
protected
string
itsName;
protected
string
itsDescription;
public
event
PropertyChangedEventHandler PropertyChanged;
public
string
Name
{
get
{
return
this
.itsName; }
set
{
this
.itsName
=
value; NotifyPropertyChanged(
"
Name
"
); }
}
public
string
Description
{
get
{
return
this
.itsDescription; }
set
{
this
.itsDescription
=
value; NotifyPropertyChanged(
"
Description
"
); }
}
protected
void
NotifyPropertyChanged(
string
thePropertyName)
{
if
(
this
.PropertyChanged
!=
null
)
{
this
.PropertyChanged(
this
,
new
PropertyChangedEventArgs(thePropertyName));
}
}
}
代码比较简单,封装了两个属性分别为他们注册PropertyChanged 事件响应数据变化。
而这个MODEL的数据来源于如下代码:
public
class
ListModel : INotifyPropertyChanged
{
public
event
PropertyChangedEventHandler PropertyChanged;
public
ObservableCollection
<
SimpleModel
>
SimpleModels {
get
;
private
set
; }
public
bool
IsDataLoaded {
get
;
private
set
; }
public
ListModel()
{
this
.SimpleModels
=
new
ObservableCollection
<
SimpleModel
>
();
}
///
<summary>
///
加载数据
///
</summary>
public
void
LoadData()
{
for
(
int
i
=
1
; i
<
1000
; i
++
)
{
this
.SimpleModels.Add(
new
SimpleModel() { Name
=
"
第
"
+
i
+
"
项
"
, Description
=
"
这是第
"
+
i
+
"
项数据
"
});
}
this
.IsDataLoaded
=
true
;
}
protected
void
NotifyPropertyChanged(
string
thePropertyName)
{
if
(
this
.PropertyChanged
!=
null
)
{
this
.PropertyChanged(
this
,
new
PropertyChangedEventArgs(thePropertyName));
}
}
}
代码跟上边的代码差不多,这里多了调用加载数据的方法LoadData()为上面的每个Model赋值。而加载代码首先为其添加一个全局属性:
public
static
ListBoxWithCheckBox.ViewModel.ListModel viewModel
=
null
;
//
获取数据
public
static
ViewModel.ListModel ViewModel
{
get
{
if
(viewModel
==
null
)
{
viewModel
=
new
ViewModel.ListModel();
}
return
viewModel;
}
}
转到MainPage的code behind 代码里面,在构造函数里面为DataContext 赋值,这里赋值的话上下文即可得到数据源,代码如下:
DataContext = App.ViewModel;
当应用程序导航进来时,调用加载全局属性去执行抓取数据的方法,代码如下:
protected
override
void
OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if
(
!
App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
base
.OnNavigatedTo(e);
}
最后的运行效果,我们选择第1 、2条数据做为欲删除的对象,然后删除看有啥变化?
点击删除后的效果:
mainPage 的code behind 完整代码如下:
完整代码
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Net;
using
System.Windows;
using
System.Windows.Controls;
using
System.Windows.Documents;
using
System.Windows.Input;
using
System.Windows.Media;
using
System.Windows.Media.Animation;
using
System.Windows.Shapes;
using
Microsoft.Phone.Controls;
using
Microsoft.Phone.Shell;
using
ListBoxWithCheckBox.ViewModel;
namespace
ListBoxWithCheckBox
{
public
partial
class
MainPage : PhoneApplicationPage
{
private
ApplicationBar applicationBarChoose;
private
ApplicationBarIconButton applicationBarIconButtonChoose;
private
ApplicationBar applicationBarDeleteOrCancel;
private
ApplicationBarIconButton applicationBarIconButtonDelete;
private
ApplicationBarIconButton applicationBarIconButtonCancel;
//
Constructor
public
MainPage()
{
InitializeComponent();
ConstructApplicationBar();
DataContext
=
App.ViewModel;
this
.Loaded
+=
new
RoutedEventHandler(MainPage_Loaded);
}
void
MainPage_Loaded(
object
sender, RoutedEventArgs e)
{
}
protected
override
void
OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if
(
!
App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
base
.OnNavigatedTo(e);
}
///
<summary>
///
构建应用程序条
///
</summary>
private
void
ConstructApplicationBar()
{
#region
--应用程序条“选择”菜单--
this
.applicationBarChoose
=
new
ApplicationBar();
this
.applicationBarIconButtonChoose
=
new
ApplicationBarIconButton(
new
Uri(
"
/content/ApplicationBar.Choose.png
"
,UriKind.Relative));
this
.applicationBarIconButtonChoose.Text
=
"
选择
"
;
this
.applicationBarIconButtonChoose.Click
+=
new
EventHandler(applicationBarIconButtonChoose_Click);
this
.applicationBarChoose.Buttons.Add(
this
.applicationBarIconButtonChoose);
this
.applicationBarChoose.IsMenuEnabled
=
true
;
this
.applicationBarChoose.IsVisible
=
true
;
this
.ApplicationBar
=
this
.applicationBarChoose;
#endregion
this
.applicationBarDeleteOrCancel
=
new
ApplicationBar();
#region
--删除--
this
.applicationBarIconButtonDelete
=
new
ApplicationBarIconButton(
new
Uri(
"
/content/ApplicationBar.Delete.png
"
,UriKind.Relative));
this
.applicationBarIconButtonDelete.Text
=
"
删除
"
;
this
.applicationBarIconButtonDelete.Click
+=
new
EventHandler(applicationBarIconButtonDelete_Click);
#endregion
#region
--取消--
this
.applicationBarIconButtonCancel
=
new
ApplicationBarIconButton(
new
Uri(
"
/content/ApplicationBar.Cancel.png
"
,UriKind.Relative));
this
.applicationBarIconButtonCancel.Text
=
"
取消
"
;
this
.applicationBarIconButtonCancel.Click
+=
new
EventHandler(applicationBarIconButtonCancel_Click);
#endregion
this
.applicationBarDeleteOrCancel.Buttons.Add(
this
.applicationBarIconButtonDelete);
this
.applicationBarDeleteOrCancel.Buttons.Add(
this
.applicationBarIconButtonCancel);
this
.applicationBarDeleteOrCancel.IsMenuEnabled
=
true
;
this
.applicationBarDeleteOrCancel.IsVisible
=
true
;
}
///
<summary>
///
listBox 为可选择状态
///
</summary>
private
void
SwitchToChooseState()
{
this
.listBoxWithBoxes.IsInChooseState
=
true
;
this
.ApplicationBar
=
this
.applicationBarDeleteOrCancel;
}
///
<summary>
///
listBox 为普通状态
///
</summary>
private
void
SwitchToNormalState()
{
this
.listBoxWithBoxes.IsInChooseState
=
false
;
this
.ApplicationBar
=
this
.applicationBarChoose;
}
///
<summary>
///
取消操作
///
</summary>
///
<param name="sender"></param>
///
<param name="e"></param>
void
applicationBarIconButtonCancel_Click(
object
sender, EventArgs e)
{
SwitchToNormalState();
}
///
<summary>
///
删除操作
///
</summary>
///
<param name="sender"></param>
///
<param name="e"></param>
void
applicationBarIconButtonDelete_Click(
object
sender, EventArgs e)
{
if
(MessageBox.Show(
"
你确定要删除选中项吗?
"
,
"
提示
"
,MessageBoxButton.OKCancel)
==
MessageBoxResult.OK)
{
foreach
(SimpleModel item
in
this
.listBoxWithBoxes.SelectedItems)
{
App.ViewModel.SimpleModels.Remove(item);
}
SwitchToNormalState();
}
}
///
<summary>
///
选择操作
///
</summary>
///
<param name="sender"></param>
///
<param name="e"></param>
void
applicationBarIconButtonChoose_Click(
object
sender, EventArgs e)
{
SwitchToChooseState();
}
}
}
这里推荐一个小技巧,当我们编写动态数据时,又不想运行即想从代码IDE看到运行效果,类似于这样:
这个效果还是要借用PhoneApplicationPage 的DataContext属性,具体如下编写代码:
- 编写一个数据xaml命名为:ViewModelSampleData.xaml 文件,该文件负责为SimpleModels 做数据,代码如下:
<
viewModels:ListModel
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels
="clr-namespace:ListBoxWithCheckBox.ViewModel"
>
<
viewModels:ListModel.SimpleModels
>
<
viewModels:SimpleModel
Name
="测试第一项"
Description
="这是测试的第一个节点"
/>
<
viewModels:SimpleModel
Name
="测试第二项"
Description
="这是测试的第二个节点"
/>
</
viewModels:ListModel.SimpleModels
>
</
viewModels:ListModel
>
- 在MainPage文件的XAML界面为DataContext赋值,代码如下:
d:DataContext="{d:DesignData ViewModelSampleData.xaml}"
Tip:该效果只运用于没有运行即可查看效果,运行后将会忽略。
怎么样,该组件不错吧,大家下载后试试吧。
源码下载:
ListBoxWithCheckBox Demo