2015-10-28 23 views
1

我在有5個文本框的網格中有一行,其中2個是通過複選框啓用的。我試圖動態添加額外的行到單擊按鈕時的網格。我添加的事件處理程序只會啓用第一行中的文本框,但不會在當前行(第2行)中啓用。還有另一個事件處理程序處理第一行中的框,這是一個新的事件處理程序。 (順便說一下,我只有第二行的一部分編碼)。不知道我是否應該嘗試爲複選框製作模板,然後使用綁定到文本框?如果是這樣,我讀過的有關連接綁定的說明是模糊和混亂的。或者我可以直接進行綁定嗎?要麼 ?如何動態鏈接一個CheckBox以啓用C#中的文本框(WPF)?

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

我的建議是刪除所有代碼並使用數據綁定。你在做事情,對自己來說比自己想要的更難。 –

回答

1

在延續錯誤的做法的風險,在我看來,最直接的方式來解決您的具體需要在這裏解決您的事件處理程序,以便它始終是特定於文本框對應於相關複選框。通過將事件處理程序訂閱移動到本地變量enterAntes的聲明之下,然後在事件處理程序中使用該變量(即通過用作事件處理程序的匿名方法捕獲該變量),可以輕鬆完成此操作。例如:

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

現在,這麼說,我全情投入與評議馬克費爾德曼誰建議你寫的代碼是不是爲了實現在WPF你的目標的正確方法達成一致。

我不確定我是否同意「更難」的特徵。這是一個負載和主觀的術語,這取決於您在找到簡單或困難的部分。作爲WPF的新手,您幾乎可以肯定地找到數據綁定和聲明式基於XAML的編碼「硬」的概念,以及直接的程序代碼,例如在您的示例中「easy」(或至少「更容易」:))。

但他絕對正確,從長遠來看,通過「WPF方式」做事情會更好。您可能會也可能不會得到更少的代碼,但WPF API旨在儘可能多地從XAML中利用,並最小化地使用代碼(當然不是爲了構建UI)。


那麼代碼的含義是什麼?那麼,我漫不經心,它將超出一個好的,簡潔的堆棧溢出答案的範圍,我嘗試從頭開始重寫整個代碼以適應WPF範例。但我會提供一些關於如何處理這個問題的建議。

  1. 首先,忘記UI對象自己一會兒。編寫類,描述 UI的關鍵特徵,因爲你想它,而不是UI本身。在這個例子中,這可能意味着你應該有一個行列表。還應該有一個類來定義單個行的外觀,例如與一個bool屬性(以反映複選框狀態)和一個string屬性(以反映文本框的值)。這是你的「模型」;即每個類都是一個單獨的模型類,同時您可以將整個類的集合視爲您的UI的模型。
  2. 現在,返回到您的用戶界面並在XAML中定義它,而不是在代碼中。在UI中有幾種不同的方式來表示列表。類如ListBox,ListView,DataGrid或甚至ItemsControl(許多列表導向控件的基類)。將列表控件的來源綁定到您在上一步中創建的模型列表。
  3. 爲列表中包含的類的類型定義DataTemplate(同樣,在XAML中)。這將爲您的列表中的單個行聲明UI。你的模板可能是這個樣子:
<!-- 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)); 
     } 
    } 
} 
  • 當用戶點擊您的按鈕,添加一個新的項目,不與UI混亂直接
      。相反,請將新元素添加到行列表中。讓WPF做更新UI的工作。


    NOTES:

    • 上述用途StackPanel用於方便數據模板。如果你想要列中排列的東西,你可能會想要使用Grid並使用SharedSizeGroup來聲明它的列。
    • 或者更好的是,也許你可以使用DataGrid,假設它的默認值是可以接受的,它提供了簡單和自動處理這種類型的佈局。
    • 上述內容並不意味着對如何使用數據模板的完整說明。這只是讓你指出正確的方向。模板是WPF更強大的特性之一,但是它也有可能相當複雜。
    • 對於所有這些工作,您的類型需要在它們發生變化時提供通知。在行模型的情況下,您可以看到它實現了INotifyPropertyChanged。還有一個接口INotifyCollectionChanged;通常你不必自己實現,因爲WPF的類型爲ObservableCollection<T>,你可以像使用List<T>一樣使用ObservableCollection<T>來存儲數據列表,但是可以將更改通知報告給WPF。


    我知道這是很多,一次採取一切。不幸的是,試圖用一個答案解釋所有你需要學習的東西來解決這個問題是不現實的。坦率地說,即使是上面的內容也在推動堆棧溢出回答的範圍內的限制。但我希望我已經達到了正確的亮點,讓您看看WPF文檔的正確部分,並瞭解WPF API的基本原理。

  • +0

    我真的很感謝你的答案的深度。出於某種原因,我需要很長時間才能露面?另外,不幸的是,我一直在嘗試約2周來掌握如何進行數據綁定的概念。我理解這個概念,但似乎無法做到。我有一個數據網格,我想從行中添加數據,但無法弄清楚。我也做了一個INotifyPropertyChange類,把所有的東西放在一起很麻煩。將不得不更多地考慮這一點。 –

    +0

    需要付出很多,兩週時間不多。是的,這個概念很簡單,但WPF不僅僅是數據綁定。它有很多其他功能,並且試圖一次全部學習是困難的(也許是不可能的)。就學習任何東西而言,最好從小而簡單的開始。在任何時候關注API的非常狹窄的部分;一次只學習和練習一個功能。這將使得學習該功能變得更加容易,從長遠來看召回將會更好。 –

    +0

    感謝您的鼓勵。問題是我應該有一個測試版,並在4周內運行。這是我們所有其他工作的重中之重。所以沒時間慢慢來。 –

    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; 
          } 
    

    希望這可以幫助你實現你的要求。

    +0

    謝謝你。我不知道爲什麼當我早點檢查時沒有出現答案?這有所幫助,特別是與前面的迴應相結合。我有很多工作要做:) –

    +0

    我把你的代碼放到了一個程序中,並且還留下了如何將任何數據放入數據網格的問題,這就是爲什麼我要爲用戶輸入數據。如果數據網格爲空,則啓用按鈕不執行任何操作。如何將信息放入ObservableCollection以放入數據網格?實際上,我怎樣才能獲得任何數據到收藏中?我知道,你不應該給我所有的答案。我在做這個工作。 –

    +0

    這在技術上符合我的要求,而我正在使用此代碼,所以我將其標記爲答案。我確實有一些問題,因爲即使方法顛倒,取消勾選複選框也不會禁用文本框。我試圖爲用戶輸入動態添加空白行,所以這個按鈕對我不起作用,但是通過將CanAddRows設置爲true來允許附加行。雖然它有一些奇怪的行爲,但對它並不滿意。我更喜歡我的其他表單,但仍然無法讓這些複選框正常工作。 –

    相關問題