如何动态链接CheckBox以在C#(WPF)中启用TextBox?

时间:2015-10-28 02:35:29

标签: c# wpf checkbox

我在网格中有一行有5个文本框,其中2个是通过复选框启用的。我想在单击按钮时动态地向网格添加其他行。我添加的eventhandler只会启用第一行中的文本框,但不会启用当前行(2nd)中的文本框。还有另一个事件处理程序处理第一行中的框,这是一个新的。 (顺便说一下,我只编写了第二行的一部分)。不确定我是否应该尝试为复选框创建模板,然后使用绑定到文本框?如果是这样的话,我读到的关于连接绑定的说明是模糊和混乱的。或者我可以直接进行绑定吗?还是?

 public partial class Window2 : Window
{
    int currentColumn = 0;
    int currentRow = 1;
    int timesCalled = 1;
    public Window2()
    {
        InitializeComponent();
    }               
    private void AddLevelButton_Click(object sender, RoutedEventArgs e)
    {
        string level = this.Level.Content.ToString();  //label for the row
        string[] splitLevel = level.Split(' ');
        int levelNum = int.Parse(splitLevel[1]);
        levelNum = timesCalled + 1;            
        int nextRow = currentRow + 1;           
        int nextColumn = currentColumn + 1;
        Label levelLabel = new Label();
        levelLabel.Content = "Level " + levelNum.ToString();          
        Grid.SetRow(levelLabel, nextRow);
        Grid.SetColumn(levelLabel, currentColumn);
        FlowGrid.Children.Add(levelLabel);
        currentColumn++;
        CheckBox antesBox = new CheckBox();   //the checkbox to enable the
        antesBox.Name = "AntesBox";           //textbox which follows
        antesBox.VerticalAlignment = VerticalAlignment.Bottom;
        antesBox.HorizontalAlignment = HorizontalAlignment.Right;
        antesBox.FontSize = 16;
        antesBox.Width = 20;
        antesBox.Height = 20;
        antesBox.Checked += AntesBox_Checked1;      //eventhandler
        Grid.SetRow(antesBox, nextRow);
        Grid.SetColumn(antesBox, currentColumn);
        FlowGrid.Children.Add(antesBox);
        nextColumn = ++currentColumn;
        TextBox enterAntes = new TextBox();      //the textbox to be enabled
        enterAntes.Name = "EnterAntes";
        enterAntes.Margin = new Thickness(5, 0, 5, 0);
        enterAntes.FontSize = 16;
        enterAntes.FontFamily = new FontFamily("Verdana");
        enterAntes.IsEnabled = false;       
        enterAntes.KeyDown += EnterAntes_KeyDown1;    //tested; this works
        Grid.SetRow(EnterAntes, nextRow);
        Grid.SetColumn(EnterAntes, nextColumn);
        FlowGrid.Children.Add(EnterAntes);
        nextColumn = ++currentColumn;

    }

    private void enterAntes_KeyDown1(object sender, KeyEventArgs e)
    {
        int key = (int)e.Key;
        e.Handled = !(key >= 34 && key <= 43 ||
         key >= 74 && key <= 83 || key == 2); 
    }

    private void AntesBox_Checked1(object sender, RoutedEventArgs e)
    {
        EnterAntes.IsEnabled = true;
    }

2 个答案:

答案 0 :(得分:2)

您需要添加以下代码才能启用文本框。

以下是datagrid的xaml视图。

<DataGrid x:Name="gvTest" AutoGenerateColumns="False" ItemsSource="{Binding}" HorizontalAlignment="Left" Margin="86,204,0,0" VerticalAlignment="Top" Height="132" Width="436">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="TextBox 01">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBox x:Name="txt01" Width="50" Text="{Binding TxtBox01}"></TextBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="TextBox 02">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBox x:Name="txtbox02" Width="50" Text="{Binding TxtBox02}"></TextBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="TextBox 03">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBox x:Name="txtbox03" Width="50" Text="{Binding TxtBox03}"></TextBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="TextBox 04">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBox x:Name="txtbox04" Width="50" IsEnabled="False"  Text="{Binding TxtBox04}" Loaded="txtbox04_Loaded"></TextBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="TextBox 05">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBox x:Name="txtbox05" Text="{Binding TxtBox05}" Loaded="txtbox05_Loaded"></TextBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Enable" >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox x:Name="chk01" Checked="chk01_Checked" IsChecked="{Binding IsActive}"></CheckBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

添加以下代码以声明所需文本框的实例并声明可观察集合。

TextBox txt04;
TextBox txt05;
ObservableCollection<TestItem> TestItemList = new ObservableCollection<TestItem>();

将以下代码添加到所需文本框的已加载事件中。

private void txtbox04_Loaded(object sender, RoutedEventArgs e)
        {
            txt04 = (sender as TextBox);
            //txt04.IsEnabled = false;
        }

        private void txtbox05_Loaded(object sender, RoutedEventArgs e)
        {
            txt05 = (sender as TextBox);
        }

现在,创建一个包含以下代码段的模型类,以便将值绑定到数据网格。

public class TestItem
    {
        public string TxtBox01 { get; set; }
        public string TxtBox02 { get; set; }
        public string TxtBox03 { get; set; }
        public string TxtBox04 { get; set; }
        public string TxtBox05 { get; set; }
        public bool IsActive { get; set; }
        public TestItem()
        {
            IsActive = false;
        }
    }

我使用了一个按钮向数据网格添加新行。将以下代码添加到按钮单击以添加行。

private void btnAdd_Click(object sender, RoutedEventArgs e)
        {
            TestItemList.Add(new TestItem());
            gvTest.ItemsSource = TestItemList;
        }

最后,将以下代码添加到复选框选中的事件

CheckBox c = (sender as CheckBox);
            if (c.IsChecked==true)
            {
                txt04.IsEnabled = true;
                txt05.IsEnabled = true;
            }

希望这可以帮助您满足您的要求。

答案 1 :(得分:1)

冒着使错误方法永久化的风险,在我看来,解决特定需要的最直接方法是修复事件处理程序,使其始终特定于文本框这对应于相关的复选框。通过将事件处理程序订阅移动到局部变量enterAntes的声明之下,然后在事件处理程序中使用该变量(即,以便通过匿名方法捕获它),可以轻松完成此操作。事件处理程序)。例如:

    TextBox enterAntes = new TextBox();      //the textbox to be enabled
    antesBox.Checked += (sender, e) => enterAntes.IsEnabled = true;

现在,我说,我全心全意地同意评论员Mark Feldman的观点,他建议您编写的代码不是在WPF中实现目标的正确方法。

我不确定我是否同意这种特征&#34;更难&#34;。这是一个如此负载和主观的术语,在很大程度上取决于找到的容易或困难。作为WPF的新手,您几乎可以肯定地找到数据绑定和基于声明XAML的编码概念&#34; hard&#34;以及直接的过程代码,例如在您的示例中&#34; easy&#34; (或至少&#34;更容易&#34; :))。

但他绝对正确的是,从长远来看,你会更好地通过做事和#34; WPF方式&#34;。您可能会或可能不会使用更少的代码,但WPF API旨在尽可能地从XAML中使用,并且最低限度地使用代码隐藏(当然不是为了构建UI)。


那么对你的代码来说意味着什么呢?好吧,我絮絮叨叨,这将超出一个好的,简洁的Stack Overflow的范围,让我尝试从头开始重写你的整个代码以适应WPF范例。但我会就如何处理这个问题提出一些建议。

  1. 首先,暂时忘记UI对象本身。编写描述 UI的关键特性的类,而不是UI本身。在此示例中,这可能意味着您应该有一个行列表。还应该有一个类来定义单行的外观,例如:使用bool属性(以反映复选框状态)和string属性(以反映文本框值)。这是你的&#34;型号&#34 ;;即每个类都是一个单独的模型类,同时您可以将整个类集合视为UI的模型。
  2. 现在,返回到您的UI并在XAML中定义它,而不是在代码中。有几种不同的方式来表示UI中的列表。像ListBoxListViewDataGrid或甚至ItemsControl这样的类(许多面向列表的控件的基类)。将列表控件的来源绑定到您在上一步中创建的模型列表。
  3. 为列表中包含的类类型定义DataTemplate(同样,在XAML中)。这将声明列表中单行的UI。您的模板可能如下所示:
  4. <!-- Make sure you defined the "local" XML namespace for your project using the
         xmlns declaration -->
    <DataTemplate DataType="{x:Type local:MyRowModel}">
      <StackPanel Orientation="Horizontal">
        <TextBox Text="{Binding Text}" IsEnabled={Binding IsEnabled}"/>
        <Checkbox Checked="{Binding IsEnabled}"/>
      </StackPanel>  
    </DataTemplate>
    

    DataTemplate元素中的所有XAML都会在呈现行模型的控件中告诉WPF您希望单行看起来像什么。该控件将为模板定义的列表项设置DataContext,以便{Binding...}声明可以直接按名称引用行模型的属性。

    该行模型反过来可能如下所示:

    class MyRowModel : INotifyPropertyChanged
    {
        private string _text;
        private bool _isEnabled;
    
        public string Text
        {
            get { return _text; }
            set
            {
                if (_text != value)
                {
                    _text = value;
                    OnPropertyChanged();
                }
            }
        }
    
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                if (_isEnabled != value)
                {
                    _isEnabled = value;
                    OnPropertyChanged();
                }
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged([CallerMemberName]string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
    
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    1. 点击添加新项目的按钮时,请不要直接弄乱用户界面。而是将新元素添加到行列表中。让WPF完成更新UI以匹配的工作。

    2. 备注:

      • 为方便起见,上面使用StackPanel作为数据模板。如果您想要在列中排列的内容,您可能希望使用Grid并使用SharedSizeGroup声明其列。
      • 或者更好的是,也许你可以使用DataGrid,假设它的默认值是可以接受的,它可以简单而自动地处理这种类型的布局。
      • 上述内容并不意味着接近如何使用数据模板的完整解释。它应该让你指向正确的方向。模板是WPF更强大的功能之一,但是它也有可能相当复杂。
      • 要使所有这些工作正常,您的类型需要在更改时提供通知。对于行模型,您可以看到它实现了INotifyPropertyChanged。还有一个接口INotifyCollectionChanged;通常你不必自己实现这个,因为WPF的类型ObservableCollection<T>可以像List<T>一样使用,来存储数据列表但是有一种方法可以通知更改向WPF汇报。


      我知道要立刻采取这一切是很重要的。不幸的是,尝试在一个答案中解释所有你需要学习的东西是不可行的。坦率地说,即使是上述内容也在限制Stack Overflow答案范围内的限制。但我希望我能够找到正确的亮点,让您了解WPF文档的正确部分,并了解WPF API的基本原理。