当datacontext是列表时,为什么我的文本框没有正确初始化?

时间:2016-03-30 16:59:24

标签: c# wpf

我有一个英雄课。它只有一个属性Name,它实现了有关更改的接口。

public class Hero : INotifyPropertyChanged, INotifyCollectionChanged
 {
     public string Name { get { return _name; } 
         set
         {
             _name = value;
             if (PropertyChanged != null)
             {
                 PropertyChanged(this, new PropertyChangedEventArgs("Name"));
             }
             if (CollectionChanged != null)
             {
                 CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace));
             }
         }
     }
    private string _name = "";
}

我的xaml如下。我将文本的datacontext绑定到名为Heros的集合中,该集合在后面的代码中定义。

<Window x:Class="Chap21_2.TestCollection"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="TestCollection" Height="640" Width="480"
     DataContext="{Binding RelativeSource={RelativeSource Self}}">
  <Grid>
       <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
       </Grid.RowDefinitions>
       <TextBox Grid.Row="1" Text="{Binding Name}" DataContext="{Binding Heros}"></TextBox>
       <Button Grid.Row="2" Content="aa" ></Button>
  </Grid>
 </Window>

这里是我的后代码。它初始化集合,问题是当Init()顺序改变时,结果是不同的。

public partial class TestCollection : Window
 {
     public ObservableCollection<Hero> Heros { get { return _heros; } set { _heros = value; } }

    private ObservableCollection<Hero> _heros = new ObservableCollection<Hero>();


     public TestCollection()
     {
         // If move Init() here, it'll works.
         InitializeComponent();
         Init();   
     }

    void Init()
     {
         Hero hero = new Hero("Bu Lv", 100, 88, 100, 30);

        _heros.Add(hero);
         hero.HP = 88;

        hero = new Hero("Fei Zhang", 100, 88, 100, 30);
         hero.HP = 90;
         _heros.Add(hero);
     }
 }

当我启动代码时,文本框不显示&#34; Bu Lv&#34;我期望。  但是如果我在InitializedComponent()之前移动Init(),它就可以工作。  为什么呢?

1 个答案:

答案 0 :(得分:0)

这可能是由于将集合绑定到单个文本框。如果我将其更改为使用ItemsControl,则Init可以在InitializeComponent之后。这是我做的事情。

Hero.cs

public class Hero : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name = string.Empty;

    public string Name
    {
       get { return _name; }
       set
       {
          _name = value;
          if (PropertyChanged != null)
          {
             PropertyChanged(this, new PropertyChangedEventArgs("Name"));
          }
       }
    }
}

TestCollection.xaml

这包括您的原件以及用于说明目的的一些更改。

<Window x:Class="Chap21_2.TestCollection"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="TestCollection" Height="640" Width="480"
     DataContext="{Binding RelativeSource={RelativeSource Self}}">
   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="Auto"></RowDefinition>
         <RowDefinition Height="*"></RowDefinition>
         <RowDefinition Height="Auto"></RowDefinition>
      </Grid.RowDefinitions>
      <!-- Your original binding -->
      <TextBox Grid.Row="0" Text="{Binding Name}" DataContext="{Binding Heros}" />
      <!-- My added ItemsControl for example purposes -->
      <ItemsControl Grid.Row="1" ItemsSource="{Binding Heros}">
         <ItemsControl.ItemTemplate>
            <DataTemplate>
               <TextBox Text="{Binding Name}" />
            </DataTemplate>
         </ItemsControl.ItemTemplate>
      </ItemsControl>

      <Button Grid.Row="2" Content="aa" ></Button>
   </Grid>
</Window>

TestCollection.xaml.cs

您的示例并未包含所有其他属性,因此我为此示例的目的对其进行了评论。

public partial class TestCollection : Window
{
  public ObservableCollection<Hero> Heros { get { return _heros; } set { _heros = value; } }
  private ObservableCollection<Hero> _heros = new ObservableCollection<Hero>();

  public TestCollection()
  {
     // Putting this here allows the collection to populate BEFORE the UI is initialized.
     // Init(); 
     InitializeComponent();
     // Putting it here is normal. The ItemsControl works, but the single TextBox binding will not.
     Init();
  }

  private void Init()
  {
     Hero hero;
     //Hero hero = new Hero("Bu Lv", 100, 88, 100, 30);
     //hero.HP = 88;
     hero = new Hero();
     hero.Name = "Bu Lv";
     _heros.Add(hero);

     //hero = new Hero("Fei Zhang", 100, 88, 100, 30);
     //hero.HP = 90;
     hero = new Hero();
     hero.Name = "Fei Zhang";
     _heros.Add(hero);
  }
}

我的建议是,如果您想要从集合中显示单个模型,您可能需要添加一个&#34; SelectedHero&#34;或仅包含单个Hero实例的类似属性。绑定到某个东西的集合通常用于渲染集合中的所有实例。

无论你怎样看待它,你都可以在IntializeComponent()之前或之后放置Init(),因为你没有直接与UI交互。