如何计算ItemsControl内部的已检查复选框

时间:2019-08-02 09:06:21

标签: c# wpf checkbox itemscontrol

我在WPF中使用ItemsControl创建了多个复选框。但是我需要将复选框限制为20,以便用户可以选中/选中该复选框。如何检查已选中的复选框?

我尝试了尽可能多的研究,甚至将复选框绑定到多个命令,但是没有一个起作用。以下是通过Itemscontrol内的复选框获得的代码。之后,IsChecked

for (int i = 0; i < ItemsControlUnitPerStrip.Items.Count; i++)
{
    ContentPresenter container = (ContentPresenter)ItemsControlUnitPerStrip.ItemContainerGenerator.ContainerFromItem(ItemsControlUnitPerStrip.Items[i]);
    CheckBox checkBoxChecked = container.ContentTemplate.FindName("CheckBoxUnitPerStrip", container) as CheckBox;
    if (checkBoxChecked.IsChecked == true)
    {
        //iOPC.WriteTag(checkBoxChecked.Uid, checkBoxChecked.IsChecked);
    }
}

我的XAML代码

 <GroupBox x:Name="GroupBoxSamplingModeStrip" Header="Unit Per Strip" Grid.Row="0" Grid.Column="1">
                <ScrollViewer VerticalScrollBarVisibility="Auto">
                    <ItemsControl x:Name="ItemsControlUnitPerStrip"
                      VirtualizingPanel.IsVirtualizing="True"
                      VirtualizingPanel.VirtualizationMode="Recycling">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <UniformGrid Rows="{Binding StripRowsCount}"
                                 Columns="{Binding StripColumnsCount}"/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <CheckBox x:Name="CheckBoxUnitPerStrip"
                                 Uid="{Binding Tag}">
                                    <CheckBox.ToolTip>
                                        <ToolTip x:Name="TootlTipUnitPerStrip">
                                            <TextBlock Text="{Binding Key}"/>
                                        </ToolTip>
                                    </CheckBox.ToolTip>
                                </CheckBox>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </ScrollViewer>
            </GroupBox>

有关如何生成复选框的功能代码

  private void initializeUnitPerStrip()
    {
        unitPerStrip = new List<UtilitiesModel>();
        int totalRow = samplingModeModel.StripRows = 7;
        int totalCol = samplingModeModel.StripColumn = 15;
        int frontOffset = 8;
        int behindOffset = 0;
        for (int c = 1; c < totalCol; c++)
        {
            for (int r = 1; r < totalRow; r++)
            {
                unitPerStrip.Add(new UtilitiesModel
                {
                    Key = $"[{c}, {r}]",
                    Tag = $"{UTAC_Tags.S7Connection}DB{406},X{frontOffset}.{behindOffset}"
                });
            }
        }
        ItemsControlUnitPerStrip.ItemsSource = unitPerStrip;
    }

3 个答案:

答案 0 :(得分:1)

1)将具有通知属性更改事件的复选框属性绑定:

public class NotifyBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }

为方便起见,将负责事件的部分放在单独的小类中:

    ..
    <CheckBox x:Name="CheckBoxUnitPerStrip"
              Uid="{Binding Tag}"
              IsChecked="{Binding IsChecked}">
              <CheckBox.ToolTip>
                <ToolTip x:Name="TootlTipUnitPerStrip">
                    <TextBlock Text="{Binding Key}" />
                </ToolTip>
      </CheckBox.ToolTip>
</CheckBox>
..

XAML更改:

private void initializeUnitPerStrip()
        {
           ..
            for (int c = 1; c < totalCol; c++)
            {
                for (int r = 1; r < totalRow; r++)
                {
                    UtilitiesModel item = new UtilitiesModel 
                    {
                        Key = "[{c}, {r}]",
                        Tag = "{UTAC_Tags.S7Connection}DB{406},X{frontOffset}.{behindOffset}"
                    };
                    item.PropertyChanged += PropertyChangedFunc;
                    unitPerStrip.Add(item);
                }
            }
            ItemsControlUnitPerStrip.ItemsSource = unitPerStrip;
        }

2)接下来,我们将跟踪复选框状态更改的事件并为选中的复选框添加计数器;

功能上的细微变化:

private void PropertyChangedFunc(object sender, PropertyChangedEventArgs e)
        {
            UtilitiesModel obj = sender as UtilitiesModel;
            if(obj==null)return;

            if (e.PropertyName == "IsChecked")
            {
                iCount1 = obj.IsChecked ? iCount1 + 1 : iCount1 - 1;

                if (iCount1 > 19) //Block checking
                {
                    obj.IsChecked = false;
                }
            }
        }

添加用于检查属性更改事件的函数:

const url=new URL('http://www.somedomain.com/account/6099bb4e2eb24bab85e8b76851a14260/search?filter=a#top');

/*
url.pathname="/account/6099bb4e2eb24bab85e8b76851a14260/search"
url.pathname.split('/')=["","account","6099bb4e2eb24bab85e8b76851a14260","search"]
*/
const accountId=url.pathname.split('/')[2];
console.log(accountId);

如果iCount1-是一个计数器选中的复选框,则只需在任何地方声明它即可,例如在sampleModeModel中

答案 1 :(得分:0)

此答案使用MVVM,因此XAML中控件的名称已删除,因为MVVM中不需要它们。您的XAML如下所示:

    <Button Content="Count CheckBoxes" Command="{Binding CommandCount}"
                    HorizontalAlignment="Left"/>

    <GroupBox Header="Unit Per Strip" Grid.Row="0" Grid.Column="1">
        <ScrollViewer VerticalScrollBarVisibility="Auto">
            <ItemsControl VirtualizingPanel.IsVirtualizing="True"
                  VirtualizingPanel.VirtualizationMode="Recycling"
                                        ItemsSource="{Binding Path=UnitPerStrip}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Rows="{Binding StripRowsCount}"
                             Columns="{Binding StripColumnsCount}"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <CheckBox Uid="{Binding Tag}"
                                            IsChecked="{Binding IsChecked}">
                            <CheckBox.ToolTip>
                                <ToolTip >
                                    <TextBlock Text="{Binding Key}"/>
                                </ToolTip>
                            </CheckBox.ToolTip>
                        </CheckBox>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
    </GroupBox>

XAML的唯一真正区别是将绑定添加到CheckBox中的“ IsChecked”属性,并为ItemsControl的ItemsSource设置到名为“ UnitPerStrip”的属性的绑定。

然后,在ViewModel中,您需要设置UnitPerStrip属性:

    private List<UtilitiesModel> unitPerStrip;
    public List<UtilitiesModel> UnitPerStrip
    {
        get
        {
            return unitPerStrip;
        }
        set
        {
            if (value != unitPerStrip)
            {
                unitPerStrip = value;
                NotifyPropertyChanged("UnitPerStrip");
            }
        }
    }

UtilitiesModel类需要一个名为IsChecked的新属性,以跟踪选中CheckBox的时间。这样,您就不必为混乱的UI代码搞砸了。所有这些都可以在后端数据中轻松完成。

public class UtilitiesModel
{
    public string Key { get; set; }
    public string Tag { get; set; }
    public bool IsChecked { get; set; }
}

用于生成CheckBox的代码没有太大变化。您只需要确保添加IsChecked属性,然后在完成后将结果分配给UnitPerStrip属性即可。

private void initializeUnitPerStrip()
{
    List<UtilitiesModel> ups = new List<UtilitiesModel>();
    int totalRow = samplingModeModel.StripRows = 7;
    int totalCol = samplingModeModel.StripColumn = 15;
    int frontOffset = 8;
    int behindOffset = 0;
    for (int c = 1; c < totalCol; c++)
    {
        for (int r = 1; r < totalRow; r++)
        {
            ups.Add(new UtilitiesModel
            {
                Key = $"[{c}, {r}]",
                Tag = $"{UTAC_Tags.S7Connection}DB{406},X{frontOffset}.{behindOffset}",
                IsChecked = false;
            });
        }
    }
    UnitPerStrip = ups;
}

然后,检查多少个CheckBox的代码非常简单。它只检查ViewModel中的数据,而不必担心会弄乱UI的任何混乱:

    private void Count()
    {
        int count = 0;
        foreach (UtilitiesModel item in UnitPerStrip)
        {
            if (item.IsChecked) count++;
        }

        MessageBox.Show(count.ToString());

    }

答案 2 :(得分:0)

如果您不希望在模型中添加特殊的IsChecked属性,则可以使用IValueConverter
IsChecked这样的属性与视图相关,并且不应成为视图模型的一部分(如果可以避免的话)。当您更改绑定到视图模型的控件时,您可能还需要更改此属性或重命名该属性(例如IsExpanded等)。
我建议通常避免使用能反映视图模型内部视觉状态的属性。如果添加诸如IsVisibleIsPressedIsToggled之类的属性,则视图模型将变得肿。该属性属于Control

转换器(或DataTrigger)方法使绑定数据模型保持不变(仅具有与数据相关的属性)。为了保持视图模型整洁并不受UI逻辑的影响,例如调整IsVisibleIsChecked之类的属性以反映例如将集合重新排序到视图(例如插入或排序操作),所有UI逻辑和视觉细节,例如启用或禁用控件
应该仅由转换器和触发器处理:

<!-- Decalare the converter and set the MaxCount property -->
<Window.Resources>
  <local:ItemCountToBoolenaConverter x:Key="ItemCountToBoolenaConverter" 
                                     MaxCount="20" />
</Window.Resources>

<! -- The actual DataTemplate -->
<ItemsControl.ItemTemplate>
  <DataTemplate>
    <CheckBox x:Name="CheckBoxUnitPerStrip"
              IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem}, Converter={StaticResource ItemCountToBoolenaConverter}}">
      <CheckBox.ToolTip>
        <ToolTip x:Name="TootlTipUnitPerStrip">
          <TextBlock Text="{Binding Key}" />
        </ToolTip>
      </CheckBox.ToolTip>
    </CheckBox>
  </DataTemplate>
</ItemsControl.ItemTemplate>

ItemCountToBoolenaConverter

[ValueConversion(typeof(ListBoxItem), typeof(bool))]
class ItemCountToBoolenaConverter : IValueConverter
{
  public int MaxCount { get; set; }

  #region Implementation of IValueConverter

  /// <inheritdoc />
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    if (value is ListBoxItem itemContainer && TryFindParentElement(itemContainer, out ItemsControl parentItemsControl))

    {
      return parentItemsControl.Items.IndexOf(itemContainer.Content) < this.MaxCount;
    }

    return Binding.DoNothing;
  }

  /// <inheritdoc />
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotSupportedException();
  }

  #endregion

  // Consider to make this an Extension Method for DependencyObject
  private bool TryFindVisualParent<TParent>(DependencyObject child, out TParent resultElement) where TParent : DependencyObject
  {
    resultElement = null;

    if (child == null)
      return false;

    DependencyObject parentElement = VisualTreeHelper.GetParent(child);         

    if (parentElement is TParent)
    {
      resultElement = parentElement as TParent;
      return true;
    }

     return TryFindVisualParent(parentElement, out resultElement);
  }
}