在 Silverlight 中获取 xaml.cs 中复选框的内容

Getting content of checkboxes in xaml.cs in Silverlight

我是 silverlight 的新手。我正在尝试生成一个复选框列表(包含内容)。这个想法是用户将select其中一些复选框并按下一个按钮。然后我们尝试读取 selected 复选框的内容以进行进一步处理。我不知道那里会有多少个复选框,因此我不能使用绑定。 这是 .xaml 文件中的代码片段。

<StackPanel Grid.Row="21" Grid.Column="1" Margin="5" VerticalAlignment="Center">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <ItemsControl Name="infoPairItems" ItemsSource="{Binding InfoPair}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <CheckBox Grid.Column="0" Name="infoPairSelectBox" IsEnabled="True" IsThreeState="False" 
                        Margin="0,5" FontSize="12" IsChecked="bool"
                        Content="{Binding Converter={StaticResource infoPairToStringValueConverter}}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</StackPanel>

我正在尝试像这样访问 .xaml.cs 文件中的这些复选框。

foreach(var infoPairItem in infoPairItems.Items)
{                
    ContentPresenter container = infoPairItems.ItemContainerGenerator.ContainerFromItem(infoPairItem) as ContentPresenter;
    if(container == null)
    {
        DebugLog.Log("container is null ");
    }

    DataTemplate dataTemplate = container.ContentTemplate;                                
    CheckBox checkBox = (CheckBox)dataTemplate.LoadContent();
    if (checkBox == null)
    {
        DebugLog.Log("checkBox is null !!!");
        return;
    }
    if (checkBox.IsChecked.HasValue)
    {
        if (checkBox.IsChecked.Value)
        {
            DebugLog.Log("checkbox value true");
        }
        else
        {
            DebugLog.Log("checkbox value false");
        }
    }
}

日志 'checkbox value false' 始终为所有复选框打印,即使其中一些已 selected。我尝试使用调试器。看起来变量 container 正在加载正确的值。要么方法 LoadContent() 不起作用,要么我使用了错误的方法。 如果这是一个重复的问题,我事先表示歉意。我试图查看之前关于 Whosebug 的问题,但找不到任何答案。请指导我正确的方向。

我将解释会发生什么以及如何解决:

1.- 您获得的是数据模板而不是数据模板的实例,如果您想管理实例,您可以通过使用加载事件将项目添加到列表来创建和更新列表等实例。

2.- 是什么让所有这些事件成为一个非常复杂的代码来管理,如果您创建以下代码:

2.1 一个 class 实例,它有一个 bool 和一个 string 用于 INotifyPropertyChanged 的​​内容:

public class InfoSelection : Model
{
 Property bool for Selected 
 Property string for Info, or whatever and the converter
}

2.2 包含您需要的项目的列表 class 在 DataContext

public List<InfoSelection> {get;set;}

(例如,如果您在构造函数中只初始化一次,则不需要实现 INotiyPropertyChanged,只需清除或删除项,永远不要重新分配)

2.3 在Xaml 绑定中更改为以下内容:

<CheckBox Grid.Column="0" 
          Name="infoPairSelectBox" 
          IsEnabled="True" 
          IsThreeState="False" 
          Margin="0,5" 
          FontSize="12" 
          IsChecked="{Binding Selected, Mode=TwoWay}" 
          Content="{Binding Info}"/>

I don't know how many number of checkboxes will be there and therefore I can't use bindings.

不正确。

要直观地显示一般的两个级别的数据,可以将 ItemsControl 与用于父项及其子项的单独 DataTemplate 一起使用。

然后允许编辑你的删除操作)需要从子节点中识别父节点,以及获取复选框的状态。

该识别要求我们将初始数据投影到包装器中 class 以促进 binding/identification。

让我解释一下。


假设我们的数据显示顶级 姓氏 以及与姓氏关联的所有名字。

以上模拟了从数据库中检索到的以下数据 class 的顶级复选框(删除所有)和子复选框(删除单个项目):

public class VectorStrings
{
    public int          Id         { get; set; }
    public string       LastName   { get; set; }
    public List<string> FirstNames { get; set; }
}

这样加载模拟数据:

LastNames = new List<VectorStrings>()
{
    new VectorStrings() { Id=9,  LastName="Smith", FirstNames = new List<string>() { "Bob", "Victoria" } },
    new VectorStrings() { Id=12, LastName="Jones", FirstNames = new List<string>() { "Peter", "Paul", "Mary" } },
};

现在为了显示我可以一般地显示这些项目到上面的数据,但是因为我们需要对子数据进行操作,所以我们需要project 将信息放入保存包装器 class。

public class VectorCheckbox
{
    public int                  Id          { get; set; }
    public int                  ParentId    { get; set; }
    public string               DisplayName { get; set; }
    public List<VectorCheckbox> Children    { get; set; }
    public object Tag { get; set; } // Same concept as a visual control property 'Tag'
}

所以我们投影原始数据的代码如下所示:

CheckBoxData = 
    LastNames.Select(ln => new VectorCheckbox()
           {
               DisplayName = ln.LastName,
               Id = ln.Id,
               Tag = ln,
               Children = ln.FirstNames.Select((fn, index) => new VectorCheckbox()
               {
                   Id          = index,
                   ParentId    = ln.Id,
                   DisplayName = fn,
                   Tag         = ln.Id // Hold the parent Id for identification
               }).ToList()

         })
            .ToList();

太棒了!现在我们只需要嵌套 ItemControl classes 来显示我们的数据:

<ItemsControl  ItemsSource="{Binding CheckBoxData}">
    <ItemsControl.Resources>
        <system:String x:Key="Format">Delete All for ID: '{0}' </system:String>
    </ItemsControl.Resources>
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel Orientation="Vertical" Margin="10" />
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
    <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="6,0,0,0">
                <CheckBox Tag="{Binding Id}" Margin="0,0,0,10"  Click="DeleteAll_Click">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Id, StringFormat={StaticResource Format}}"/>
                        <TextBlock Text="{Binding DisplayName}" Margin="4,0,6,0"/>
                        <ItemsControl ItemsSource="{Binding Children}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <CheckBox Tag="{Binding Tag}" 
                                              Content="{Binding DisplayName}" 
                                              Click="DeleteIndividual_Click"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </StackPanel>
                </CheckBox>
            </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>

现在在后面的代码中我们订阅了点击事件。我使用一个消息框来表明我已经确定了正确的项目。例如,如果单击删除所有复选框,它会标识子项和复选框的状态,如果我单击一个子项,它会标识其父项和自身。

父点击

private void DeleteAll_Click(object sender, RoutedEventArgs e)
{
    var cb = sender as CheckBox;

    if (cb != null)
    {
        var id = (int)cb.Tag;

        var nameInstance = ViewModel.LastNames.FirstOrDefault(nm => nm.Id == id);

        if (nameInstance != null)
            MessageBox.Show(string.Format("Delete all for {0} of names {1} (Status {2})",
            nameInstance.LastName,
            string.Join(",", nameInstance.FirstNames),
            ((cb.IsChecked ?? false) ? "Checked" : "UnChecked")
            ));
    }
}

子点击

private void DeleteIndividual_Click(object sender, RoutedEventArgs e)
{
    var cb = sender as CheckBox;

    if (cb != null)
    {
        var parentId = (int)cb.Tag; // Parent Id

        var nameInstance = ViewModel.LastNames.FirstOrDefault(nm => nm.Id == parentId);

        if (nameInstance != null)
            MessageBox.Show(string.Format("Delete individual for {0} of name {1} (Status {2})",
            nameInstance.LastName,
            cb.Content,
            ((cb.IsChecked ?? false) ? "Checked" : "UnChecked")
            ));

    }
}

因此,我已经确定了复选框状态以及目标原始项目。这段代码最终模拟了您想要做的事情。我将可观察集合移除项目的实际管道留给你。但这让这个想法得到了理解。

我建议您在 WPF 中进行试验,然后将其带到 Silverlight,因为概念是相同的,但 easier/faster 在 WPF 中进行测试。