實際項目中常常要實現(xiàn)有CheckBox列表框。但是WPF沒有自帶這樣的一個控件,下面就用Style來實現(xiàn)這樣的功能。而對于CheckBox列表框,又常常會有一個Select All的CheckBox來表示當前列表框的選擇狀態(tài)。這個功能也會被包含在下面的示例之中。效果如下圖所示。
對于單純的,沒有后臺數(shù)據(jù)綁定的情況下,這個功能可以用ItemContainerStyle來實現(xiàn)。代碼如下:
<Style x:Key="CheckListBoxItemContainerStyle"
TargetType="{x:Type ListBoxItem}">
<!--Set it un-focusable,
becaues the CheckBox in it should be focusable and only it.-->
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<CheckBox Content="{TemplateBinding Content}"
ContentTemplate= "{TemplateBinding ContentTemplate}"
ContentTemplateSelector= "{TemplateBinding ContentTemplateSelector}"
IsChecked= "{Binding IsSelected,
RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
其中要對Content和ContentTemplate等屬性進行綁定,以方便對其進行擴展,保證其通用性。這個Style一般會放在Application級別的Resource中。
對于有后臺數(shù)據(jù)綁定的情況,一般會有雙個屬性要綁定,一個是CheckBox里的Content,一個是CheckBox的IsChecked。綁定的路徑,只有在用一個Style的ListBox那里才知道,所以并不能寫在這個Style里,否則會破壞這個Style的通用性。比較合理的方式是基于這個現(xiàn)有的Style進行修改。
對于下面的數(shù)據(jù)類。
public class DataItem : INotifyPropertyChanged
{
private string name;
private bool isEnabled;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged( "Name");
}
}
public bool IsEnabled
{
get { return isEnabled; }
set
{
isEnabled = value;
OnPropertyChanged( "IsEnabled");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}
我們需要下面這個有針對性的Style來應(yīng)用數(shù)據(jù)綁定。
<Style x:Key="DataItemCheckListBoxStyle"
TargetType="{x:Type ListBox}"
BasedOn= "{StaticResource {x:Type ListBox}}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}"
BasedOn= "{StaticResource CheckListBoxItemContainerStyle}">
<Setter Property="IsSelected"
Value= "{Binding IsEnabled}"/>
<Setter Property="Margin" Value="2,2,0,0"/>
</Style>
</Setter.Value>
</Setter>
<Setter Property="SelectionMode" Value="Multiple"/>
</Style>
在上面的Style中,使用了ItemTemplate來指定CheckBox里的Content綁定到的屬性,并把ListBoxItem的IsSelected綁定數(shù)據(jù)的相應(yīng)屬性上。由于這個Style是針對特定數(shù)據(jù)寫的,所以應(yīng)當放置在使用這個Style的ListBox所在的Window的Resource中。
當然,也可以為ListBox添加兩個綁定類型的Attached Property來實現(xiàn)一個通用的Style。不過這個Property一樣要在使用的地方設(shè)置,其實沒有太大區(qū)別。有興趣的讀者可以自己試一下。
對于Select All這個CheckBox而言,用Attached Property倒是很方便。給CheckBox添加一個SyncTarget屬性指向要同步的ListBox,就可以在Window.xaml.cs之外的地方同步CheckBox和ListBox了。代碼如下:
public class ToggleButtonProperty
{
// Using a DependencyProperty as the backing store for SyncTarget.
This enables animation, styling, binding, etc...
public static readonly DependencyProperty SyncTargetProperty =
DependencyProperty.RegisterAttached( "SyncTarget", typeof(ListBox), typeof(ToggleButtonProperty),
new UIPropertyMetadata(new PropertyChangedCallback(OnSyncTargetChanged)));
public static ListBox GetSyncTarget(DependencyObject obj)
{
return obj.GetValue(SyncTargetProperty) as ListBox;
}
public static void SetSyncTarget(DependencyObject obj, ListBox value)
{
obj.SetValue(SyncTargetProperty, value);
}
private static void OnSyncTargetChanged
(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ToggleButton checker = sender as ToggleButton;
if (checker == null)
{
throw new InvalidOperationException( "SyncTarget property only works on ToggleButton.");
}
ListBox targetList = e.NewValue as ListBox;
if (targetList == null)
{
throw new InvalidOperationException( "Sync target must be a ListBox.");
}
//TODO: Un-subscribe OldValue's Event.
checker.Checked += (s, a) = >
{
targetList.SelectAll();
};
checker.Unchecked += (s, a) = >
{
targetList.UnselectAll();
};
targetList.SelectionChanged += (s, a) = >
{
checker.IsChecked = targetList.SelectedItems.Count == 0 ? false :
targetList.SelectedItems.Count == targetList.Items.Count ? (bool?)true : null;
};
}
}
使用方式也很簡單。如下代碼所示。
<DockPanel Margin="12">
<CheckBox Content="Select All"
Margin= "0,0,0,5"
DockPanel.Dock= "Top"
ext:ToggleButtonProperty.SyncTarget= "{Binding ElementName=checkListBox}"/>
<ListBox x:Name="checkListBox"
Style= "{StaticResource DataItemCheckListBoxStyle}"
ItemsSource= "{Binding Path=Items, ElementName=mainWindow}"/>
</DockPanel>
標簽:
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@ke049m.cn
文章轉(zhuǎn)載自:網(wǎng)絡(luò)轉(zhuǎn)載