Sunday, March 18, 2012

Cannot bind two types of data source to one UI target

I was also struggling the problem when I made binding between data source and UI ListBox target.
Today, I'd like to introduce the problem that we cannot bind "non-INotifyPropertyChanged property" and "INotifyPropertyChanged property" to same ListBox target.

First, I had written property binding like this. I had used "non-INotifyPropertyChanged property".

  • The xaml code is here.
 <ListBox Height="595" ItemsSource="{Binding}" HorizontalAlignment="Left" Margin="12,6,0,0" Name="listBox1" VerticalAlignment="Top" Width="438">  
   <ListBox.ItemTemplate>  
     <DataTemplate>  
       <StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">  
         <TextBlock x:Name="Name1" FontSize="35" Text="{Binding Text1}" Foreground="Black"/>  
         <TextBlock x:Name="Name2" FontSize="35" Text="{Binding Text2}" Foreground="Black"/>  
       </StackPanel>  
     </DataTemplate>  
   </ListBox.ItemTemplate>  
 </ListBox>  
  • The code behind is here.
 public partial class MainPage : PhoneApplicationPage  
 {  
   // Constructor  
   public MainPage()  
   {  
     InitializeComponent();  
     GetList();  
   }  

   public void GetList()  
   {  
     List<ItemProperties> items = new List<ItemProperties>();  
     items.Add(new ItemProperties { Text1 = "test01", Text2 = "test02" });  
     items.Add(new ItemProperties { Text1 = "test11", Text2 = "test12" });  
     listBox1.ItemsSource = items;  
   }  
 }  

 public class ItemProperties  
 {  
   private string m_Text1;  
   public string Text1  
   {  
     get { return m_Text1; }  
     set { m_Text1 = value; }  
   }  

   private string m_Text2;  
   public string Text2  
   {  
     get { return m_Text2; }  
     set { m_Text2 = value; }  
   }  
 }  


Second, I wanted to bind Foreground color in ListBox. So, I added "INotifyPropertyChanged property" as Foreground colors.
The code is like this. But I found I couldn't bind "INotifyPropertyChanged property" to Foreground color in this code.

  • The xaml code is here.
 <ListBox Height="595" ItemsSource="{Binding}" HorizontalAlignment="Left" Margin="12,6,0,0" Name="listBox1" VerticalAlignment="Top" Width="438">  
   <ListBox.ItemTemplate>  
     <DataTemplate>  
       <StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">  
         <TextBlock x:Name="Name1" FontSize="35" Text="{Binding Text1}" Foreground="{Binding Color1}"/>  
         <TextBlock x:Name="Name2" FontSize="35" Text="{Binding Text2}" Foreground="{Binding Color2}"/>  
       </StackPanel>  
     </DataTemplate>  
   </ListBox.ItemTemplate>  
 </ListBox>  
  • The code behind is here.
 public partial class MainPage : PhoneApplicationPage  
 {  
   public ObservableCollection<ItemProperties2> ItemCollection { get; private set; }  
   // Constructor  
   public MainPage()  
   {  
     InitializeComponent();  
     GetList();  
     GetList2();  
   }  

   public void GetList()  
   {  
     List<ItemProperties> items = new List<ItemProperties>();  
     items.Add(new ItemProperties { Text1 = "test01", Text2 = "test02" });  
     items.Add(new ItemProperties { Text1 = "test11", Text2 = "test12" });  
     listBox1.ItemsSource = items;  
   }  

   public void GetList2()  
   {  
     ItemCollection = new ObservableCollection<ItemProperties2>();  
     ItemCollection.Add(new ItemProperties2  
     {  
       Color1 = new SolidColorBrush(Colors.LightGray),  
       Color2 = new SolidColorBrush(Colors.Brown)  
     });  
     ItemCollection.Add(new ItemProperties2  
     {  
       Color1 = new SolidColorBrush(Colors.Cyan),  
       Color2 = new SolidColorBrush(Colors.Red)  
     });  
     DataContext = ItemCollection;  
   }  
 }  

 public class ItemProperties  
 {  
   private string m_Text1;  
   public string Text1  
   {  
     get { return m_Text1; }  
     set { m_Text1 = value; }  
   }  

   private string m_Text2;  
   public string Text2  
   {  
     get { return m_Text2; }  
     set { m_Text2 = value; }  
   }  
 }  

 public class ItemProperties2 : INotifyPropertyChanged  
 {  
   public event PropertyChangedEventHandler PropertyChanged;  
   public ItemProperties2() { }  

   private Brush m_Color1;  
   public Brush Color1  
   {  
     get { return m_Color1; }  
     set  
     {  
       m_Color1 = value;  
       OnPropertyChanged("Color1");  
     }  
   }  

   private Brush m_Color2;  
   public Brush Color2  
   {  
     get { return m_Color2; }  
     set  
     {  
       m_Color2 = value;  
       OnPropertyChanged("Color2");  
     }  
   }  

   protected void OnPropertyChanged(string name)  
   {  
     PropertyChangedEventHandler handler = this.PropertyChanged;  
     if (handler != null)  
       handler(this, new PropertyChangedEventArgs(name));  
   }  
 } 


It seems that "non-INotifyPropertyChanged property" is given priority over "INotifyPropertyChanged property".

I think the problem is a little foolish in now. But If your code is quite large amount, you may not be able to find the problem immediately.
I write previous blog post as the answer for the problem. Please check it also.

1 comment:

  1. GetList メソッドで ItemsSource を上書きしたために、XAML 上の ItemsSource="{Binding}" (つまり DataContext の適用) が無効になった、というのが主な原因です。
    今回はコレクション自体についての議論であって、中のアイテムが INotifyPropertyChanged かどうかは関係ありません。

    以下に、データバインディングについて補足を書いておきます。
    コントロールに DataContext が設定されているだけでは何も起こりません。
    ItemsSource="{Binding}" のように設定して初めて ItemsSource が DataContext にバインドされます。
    ちなみに、ItemsSource = items のように直接代入する行為はデータバインディングではありません。

    ReplyDelete