我在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;
}
答案 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
等)。
我建议通常避免使用能反映视图模型内部视觉状态的属性。如果添加诸如IsVisible
,IsPressed
,IsToggled
之类的属性,则视图模型将变得肿。该属性属于Control
。
转换器(或DataTrigger
)方法使绑定数据模型保持不变(仅具有与数据相关的属性)。为了保持视图模型整洁并不受UI逻辑的影响,例如调整IsVisible
或IsChecked
之类的属性以反映例如将集合重新排序到视图(例如插入或排序操作),所有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);
}
}