[WPFカスタムコントロール]チェックボックスが含まれているListBoxItemを作成します

1.はじめに

エクシードwpftoolkitは提供CheckListBox、以下の効果を:

しかし、それは、それで非常に良いではありませんので、そのない参照UWP ListViewコントロールが達成として良いとしてではなく、アニメーションを見てもいい:

次のようにそのスタイルは次のとおりです。

<ListViewItemPresenter ContentTransitions="{TemplateBinding ContentTransitions}"
    x:Name="Root"
    Control.IsTemplateFocusTarget="True"
    FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
    SelectionCheckMarkVisualEnabled="{ThemeResource ListViewItemSelectionCheckMarkVisualEnabled}"
    CheckBrush="{ThemeResource ListViewItemCheckBrush}"
    CheckBoxBrush="{ThemeResource ListViewItemCheckBoxBrush}"
    DragBackground="{ThemeResource ListViewItemDragBackground}"
    DragForeground="{ThemeResource ListViewItemDragForeground}"
    FocusBorderBrush="{ThemeResource ListViewItemFocusBorderBrush}"
    FocusSecondaryBorderBrush="{ThemeResource ListViewItemFocusSecondaryBorderBrush}"
    PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackground}"
    PointerOverBackground="{ThemeResource ListViewItemBackgroundPointerOver}"
    PointerOverForeground="{ThemeResource ListViewItemForegroundPointerOver}"
    SelectedBackground="{ThemeResource ListViewItemBackgroundSelected}"
    SelectedForeground="{ThemeResource ListViewItemForegroundSelected}"
    SelectedPointerOverBackground="{ThemeResource ListViewItemBackgroundSelectedPointerOver}"
    PressedBackground="{ThemeResource ListViewItemBackgroundPressed}"
    SelectedPressedBackground="{ThemeResource ListViewItemBackgroundSelectedPressed}"
    DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
    DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
    ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
    ContentMargin="{TemplateBinding Padding}"
    CheckMode="{ThemeResource ListViewItemCheckMode}"
    RevealBackground="{ThemeResource ListViewItemRevealBackground}"
    RevealBorderThickness="{ThemeResource ListViewItemRevealBorderThemeThickness}"
    RevealBorderBrush="{ThemeResource ListViewItemRevealBorderBrush}">

本物件はたくさんありますが、そこにはカスタムメソッドのCheckBoxのスタイルはありませんが、それはまた、アニメーションを達成するためにどのように参照することはできません。幸いUWPも、それは完全なレイアウト、visualSTATEのを持っているListViewItemExpandedスタイル、などを提供するだけでなく、ほぼ100行MultiSelectStatesの一部だけを取る500行の合計がほとんど存在し、これはあまりにも複雑であり、これはWPFでいくつかの問題ですしかし、実装がはるかに簡単。

実装2

Microsoftのドキュメントには、持っている方法について説明しますチェックボックスでListViewItemsを作成し、原理は非常に簡単です:

<DataTemplate x:Key="FirstCell">
  <StackPanel Orientation="Horizontal">
    <CheckBox IsChecked="{Binding Path=IsSelected, 
      RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}"/>
  </StackPanel>
</DataTemplate>

それはFindAncestor経由でのListViewItem IsSelected結合特性には、このチェックボックスコントロールのテンプレートやバインドのチェックボックスを追加することです。メソッドのリストビューが、それはまた、リストボックスに適用されます。私は、ListBoxコントロールを、このアプローチをカプセル化して使用していますので、現在基本的に無機能は、単に各ListBoxItemの前にあるチェックボックスを追加します。以前に導入されたのItemsControlをカスタマイズする方法を、あなたはまた、3を必要とする、ListBoxコントロールをカスタマイズしたいです:

  1. 定義されたリストボックス
  2. ListBoxItem協会とのListBox
  3. ロジックを実現するリストボックス
public class ExtendedListBox : ListBox
{
    public static readonly DependencyProperty IsMultiSelectCheckBoxEnabledProperty =
        DependencyProperty.Register(nameof(IsMultiSelectCheckBoxEnabled), typeof(bool), typeof(ExtendedListBox), new PropertyMetadata(true));

    public bool IsMultiSelectCheckBoxEnabled
    {
        get { return (bool)GetValue(IsMultiSelectCheckBoxEnabledProperty); }
        set { SetValue(IsMultiSelectCheckBoxEnabledProperty, value); }
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ExtendedListBoxItem();
    }
}


public class ExtendedListBoxItem : ListBoxItem
{
    public ExtendedListBoxItem()
    {
        DefaultStyleKey = typeof(ExtendedListBoxItem);
    }
}

上記は、すべてのコードです。定義ExtendedListBoxExtendedListBoxItem二つのクラス、その後、書き換えGetContainerForItemOverrideこの2つのクラスの関連、そして最後にExtendedListBoxUWPリストビューが提供模倣するコードIsMultiSelectCheckBoxEnabledプロパティ、XAMLが提供する他の主要な機能を:

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition/>
</Grid.ColumnDefinitions>
<Primitives:KinoResizer>
    <CheckBox Margin="{TemplateBinding Padding}"
              IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
              IsTabStop="False"
              x:Name="SelectionCheckMark"/>
</Primitives:KinoResizer>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Grid.Column="1"
                  Margin="{TemplateBinding Padding}"/>

チェックボックスをパッケージ化するControlTemplate使用リサイズ、これはトランジションアニメーションのCheckBox表示または非表示のための時間です。その後、合わせてControlTemplate.Triggersで2 DataTriggerを追加し、リストボックスに所属IsMultiSelectCheckBoxEnabledし、SelectionMode表示または非表示SelectionCheckMark:

<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBox},Path=SelectionMode}"
             Value="Single">
    <Setter Property="Visibility"
            TargetName="SelectionCheckMark"
            Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBox},Path=IsMultiSelectCheckBoxEnabled}"
             Value="False">
    <Setter Property="Visibility"
            TargetName="SelectionCheckMark"
            Value="Collapsed" />
</DataTrigger>

次のように最終的な結果は以下のとおりです。

3. [追加visualSTATEの

WPFのボタンControlTemplateのは、visualSTATEのを使用しませんが、ボタンのサポートvisualSTATEのは、ユーザーがvisualSTATEのはのControlTemplateを使用してカスタマイズすることができます。ExtendedListBoxItem模倣UWP MultiSelectEnabledとキャリアがそのListBoxItem ListBoxコントロールIsMultiSelectCheckBoxEnabledとのselectionModeを知っている必要があり、ListBoxItem Ownerプロパティを追加し、リストボックスPrepareContainerForItemOverride関数をオーバーライドする必要があるため、visualSTATEのMultiSelectDisabled 2を提供し、この機能はListBoxItemであります所有者の割り当て:

protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
    base.PrepareContainerForItemOverride(element, item);
    if (element is ExtendedListBoxItem listBoxItem)
        listBoxItem.Owner = this;
}

所有者ListBoxItemがIsMultiSelectCheckBoxEnabledのselectionModeと更新visualSTATEの変更を監視し、変更するために使用されたときにこれらの2つの値:

protected virtual void OnOwnerChanged(ExtendedListBox oldValue, ExtendedListBox newValue)
{
    if (oldValue != null)
    {
        var descriptor = DependencyPropertyDescriptor.FromProperty(ListBox.SelectionModeProperty, typeof(ExtendedListBox));
        descriptor.RemoveValueChanged(newValue, OnSelectionModeChanged);

        descriptor = DependencyPropertyDescriptor.FromProperty(ExtendedListBox.IsMultiSelectCheckBoxEnabledProperty, typeof(ExtendedListBox));
        descriptor.RemoveValueChanged(newValue, OnIsMultiSelectCheckBoxEnabledChanged);
    }
    if (newValue != null)
    {
        var descriptor = DependencyPropertyDescriptor.FromProperty(ListBox.SelectionModeProperty, typeof(ExtendedListBox));
        descriptor.AddValueChanged(newValue, OnSelectionModeChanged);

        descriptor = DependencyPropertyDescriptor.FromProperty(ExtendedListBox.IsMultiSelectCheckBoxEnabledProperty, typeof(ExtendedListBox));
        descriptor.AddValueChanged(newValue, OnIsMultiSelectCheckBoxEnabledChanged);
    }
}

private void OnSelectionModeChanged(object sender, EventArgs args)
{
    UpdateVisualStates(true);
}

private void OnIsMultiSelectCheckBoxEnabledChanged(object sender, EventArgs args)
{
    UpdateVisualStates(true);
}

为了使用VisualState我在ControlTemplate多写了80行代码,因为没有用上VisualTransition所以这个ControlTemplate有一些Bug,反正只是用来验证添加的两个VisualState是否有效。在ListBoxItem里用Trigger比使用VisualState更简洁有效。

4. 使用同样的原理为DataGrid的行添加ChechBox

DataGrid也可以用同样的原理为每一行添加CheckBox,只不过DataGrid的Template会负责很多。

首先自定义一个DataGrid类:

public class ExtendedDataGrid : DataGrid, IMultiSelector
{
    // Using a DependencyProperty as the backing store for IsMultiSelectCheckBoxEnabled.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsMultiSelectCheckBoxEnabledProperty =
        DependencyProperty.Register(nameof(IsMultiSelectCheckBoxEnabled), typeof(bool), typeof(ExtendedDataGrid), new PropertyMetadata(true));

    public ExtendedDataGrid()
    {
        DefaultStyleKey = typeof(ExtendedDataGrid);
    }

    public bool IsMultiSelectCheckBoxEnabled
    {
        get { return (bool)GetValue(IsMultiSelectCheckBoxEnabledProperty); }
        set { SetValue(IsMultiSelectCheckBoxEnabledProperty, value); }
    }
}

然后定义一个RowHeaderTemplate

<DataTemplate x:Key="DataGridRowHeaderTemplate">
    <Grid>
        <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}, Mode=FindAncestor}}"
                      x:Name="SelectionCheckBox"/>
    </Grid>
</DataTemplate>

在DataGrid的Style上应用这个RowHeaderTemplate。最后再DataGrid的Style的Triggers中添加两个DataTrigger:

<Trigger Property="SelectionMode" Value="Single">
    <Setter Property="HeadersVisibility"  Value="Column" />
</Trigger>
<Trigger Property="IsMultiSelectCheckBoxEnabled" Value="False">
    <Setter Property="HeadersVisibility"  Value="Column"/>
</Trigger>

HeadersVisibility是个DataGridHeadersVisibility的属性,它用于控制DataGrid行和列的Header是否显示,因为我在每一行的开头放了CheckBox(就是使用上面定义的RowHeaderTempalte),所以定一只只显示Column的Header的话相当于隐藏了这个CheckBox,运行效果如下:

5. 结语

ListBox和DataGrid的自定义是个很大的话题,这里只实现最简单的功能,通常会根据业务需求逐渐增加更多需求。如果有更复杂的需求,我建议买商业的控件,毕竟DataGrid的自定义可以很复杂,花时间不如花钱。

6. 参考

How to_ Create ListViewItems with a CheckBox - WPF _ Microsoft Docs

ListBox Class (System.Windows.Controls) _ Microsoft Docs

DataGrid Class (System.Windows.Controls) _ Microsoft Docs

7. 源码

Kino.Toolkit.Wpf_ExtendedListBox.cs at master

Kino.Toolkit.Wpf_ExtendedDataGrid.cs at master

おすすめ

転載: www.cnblogs.com/dino623/p/Create_ListBoxIte_with_a_CheckBox.html