5

ứng dụng Silverlight 3 với TabControl được ràng buộc với ObservableCollection bằng cách sử dụng một IValueConverter. Ban đầu các công trình liên kết (chuyển đổi được gọi) khi khởi động ứng dụng. Thay đổi, Xóa() hoặc Thêm(), vào bộ sưu tập bị ràng buộc không được phản ánh trong trình chuyển đổi TabControl ... không được gọi.Silverlight TabControl bị ràng buộc với ObservableCollection <string> không cập nhật khi bộ sưu tập thay đổi

lưu ý: Hộp danh sách bị ràng buộc phản ánh các thay đổi đối với bộ sưu tập bị ràng buộc trong khi TabControl thì không.

Ý tưởng?

/JHD


Các XAML ràng buộc ...

<UserControl.Resources> 
    <local:ViewModel x:Key="TheViewModel"/> 
    <local:TabConverter x:Key="TabConverter" /> 
</UserControl.Resources> 
<StackPanel DataContext="{StaticResource TheViewModel}"> 
    <ListBox ItemsSource="{Binding Classnames}" /> 
    <controls:TabControl x:Name="TheTabControl" 
     ItemsSource="{Binding Classnames, Converter={StaticResource TabConverter}, ConverterParameter=SomeParameter}"/> 
    <Button Click="Button_Click" Content="Change ObservableCollection" /> 
</StackPanel> 

ViewModel ...

namespace DatabindingSpike 
{ 
    public class ViewModel 
    { 
     private ObservableCollection<string> _classnames = new ObservableCollection<string>(); 

     public ViewModel() 
     { 
      _classnames.Add("default 1 of 2"); 
      _classnames.Add("default 2 of 2"); 
     } 

     public ObservableCollection<string> Classnames 
     { 
      get { return _classnames; } 
      set { _classnames = value; } 
     } 
    } 
} 

Bộ chuyển đổi (cho đầy đủ) ...

namespace DatabindingSpike 
{ 
    public class TabConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      var source = value as ObservableCollection<string>; 
      if (source == null) 
       return null; 

      var param = parameter as string; 
      if (string.IsNullOrEmpty(param) || param != "SomeParameter") 
       throw new NotImplementedException("Null or unknow parameter pasased to the tab converter"); 

      var tabItems = new List<TabItem>(); 
      foreach (string classname in source) 
      { 
       var tabItem = new TabItem 
            { 
             Header = classname, 
             Content = new Button {Content = classname} 
            }; 
       tabItems.Add(tabItem); 
      } 

      return tabItems; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
} 
+0

Bất kỳ cơ hội chế độ mặc định là OneTime? Sẽ thử thiết lập chế độ một cách rõ ràng. /jhd –

+0

Đã thử Chế độ rõ ràng = OneWay, không có niềm vui. Tôi sẽ sử dụng sự kiện CollectionChanged và thiết lập lại TabControl.ItemsSource cho đến khi tôi tìm ra cách tốt hơn./jhd –

+0

Tôi đã tạo điều khiển tab mở rộng hoạt động chính xác với lớp ObservableCollection. http://vortexwolf.wordpress.com/2011/04/09/silverlight-tabcontrol-with-data-binding/ – vorrtex

Trả lời

0

Expose

public ObservableCollection<TabItem> Classnames 
{ 
    get { return _classnames; } 
    set { _classnames = value; } 
} 

Nếu bạn gỡ lỗi ValueConverter bạn sẽ thấy nó không được gọi là thường xuyên như bạn nghĩ rằng đó là .

+0

Bạn đang nói ValueConverter không phải là nghĩa vụ phải được gọi khi nguồn ràng buộc thay đổi? Điều này có thể đúng nhưng phản đối trực tiếp với tôi. Với ViewModel hiện đang trong dự án Data/namespace, tôi không muốn tham chiếu đến S.W.C để có được TabItem, nhưng nó là một giải pháp tốt hơn so với bây giờ. Cảm ơn./jhd –

+0

Hi Graeme, các ValueConverter đã không được gọi là (sau khi ban đầu ràng buộc) bởi vì tôi đã không thực hiện INotifyPropertyChanged trên ViewModel. Xem câu trả lời của tôi dưới đây. –

0

Sự cố có thể là ValueConverter của bạn trả về một List<TabItem> thay vì ObservableCollection<TabItem>. Hãy thử thay đổi một dòng đó và xem nó có giúp ích gì không.

+0

Bắt tốt, thay đổi nó không có hiệu ứng [rõ ràng]. Không phải là databinding đăng ký với nguồn, Classnames trong trường hợp? –

3

Update 8/19

Câu trả lời ngắn gọn là bạn phải thực hiện INotifyPropertyChanged trên mô hình điểm và thông báo cho người nghe khi tài sản/Bộ sưu tập được thay đổi.

Thực hiện INotifyPropertyChanged trên ViewModel

* implement the interface INotifyPropertyChanged 
* define the event (public event PropertyChangedEventHandler PropertyChanged) 
* subscribe to the CollectionChanged event (Classnames.CollectionChanged += ...) 
* fire the event for listeners 

nhất,

/JHD


ViewModel cập nhật mỗi trên ... ValueConverter nay là trên tất cả các thay đổi đối với tài sản/Bộ sưu tập

public class ViewModel : INotifyPropertyChanged 
{ 
    private readonly ObservableCollection<string> _classnames = new ObservableCollection<string>(); 

    public ViewModel() 
    { 
     Classnames.CollectionChanged += Classnames_CollectionChanged; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void Classnames_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     NotifyPropertyChanged("Classnames"); 
    } 

    private void NotifyPropertyChanged(string info) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      foreach (PropertyChangedEventHandler d in handler.GetInvocationList()) 
      { 
        d(this, new PropertyChangedEventArgs(info)); 
      } 
     } 
    } 

    public ObservableCollection<string> Classnames 
    { 
     get { return _classnames; } 
    } 
} 

XAML ràng buộc ...

<UserControl.Resources> 
    <local:ViewModel x:Key="TheViewModel"/> 
    <local:TabConverter x:Key="TabConverter" /> 
</UserControl.Resources> 

<StackPanel DataContext="{StaticResource TheViewModel}"> 
    <ListBox ItemsSource="{Binding Classnames}" /> 
    <controls:TabControl x:Name="TheTabControl" 
     ItemsSource="{Binding Classnames, Converter={StaticResource TabConverter}, ConverterParameter={StaticResource TheViewModel}}"/> 
    <Button Click="Button_Click" Content="Change Classnames" /> 
</StackPanel> 

Các ValueConverter (về cơ bản không thay đổi

public class TabConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      var source = value as ObservableCollection<string>; 
      if (source == null) 
       return null; 

      //also sorted out the binding syntax to pass the ViewModel as a parameter 
      var viewModel = parameter as ViewModel; 
      if (viewModel == null) 
       throw new ArgumentException("ConverterParameter must be ViewModel (e.g. ConverterParameter={StaticResource TheViewModel}"); 

      var tabItems = new List<TabItem>(); 
      foreach (string classname in source) 
      { 
       // real code dynamically loads controls by name 
       var tabItem = new TabItem 
            { 
             Header = "Tab " + classname, 
             Content = new Button {Content = "Content " + classname} 
            }; 
       tabItems.Add(tabItem); 
      } 

      return tabItems; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
+0

lưu ý: Bạn không muốn gửi sự kiện PropertyChanged trên mỗi sự kiện CollectionChanged trong mã thực ... bạn nên tăng một sự kiện PropertyChanged sau khi bạn đã thực hiện thao tác thu thập nguồn./jhd –

3

Tôi nhận ra đây là một câu hỏi hơi cũ vào thời điểm này, nhưng tôi không biết rằng bất cứ ai đã giải thích lý do tại sao bạn cần để thực hiện INotifyPropertyChanged trên thuộc tính bị ràng buộc trên mô hình chế độ xem của bạn

Bản thân ItemsControl cần phải ràng buộc với một ObservableCollection cho các sự kiện thay đổi bộ sưu tập để khiến cho ItemsControl đánh giá lại. Trình biến đổi của bạn sẽ trả về một bộ sưu tập Danh sách (hoặc Quan sát) riêng biệt mỗi khi nó được gọi thay vì giữ một ObservableCollection duy nhất và thêm các mục vào nó. Do đó, những bộ sưu tập này không bao giờ có bất kỳ sự kiện thay đổi bộ sưu tập nào được nâng lên trên chúng ... chúng luôn luôn mới, mỗi khi ràng buộc được thực hiện lại.

Tăng thuộc tínhThay đổi lực ràng buộc để được đánh giá lại và chạy lại trình biến đổi của bạn, trả về bộ sưu tập riêng và phản ánh thay đổi của bạn.

Tôi cảm thấy một cách tiếp cận tốt hơn có thể là thực hiện chuyển đổi trong ViewModel của bạn thay vì trong Trình chuyển đổi. Phơi bày một ObservableCollection của TabItem mà bạn liên kết trực tiếp và bạn sửa đổi tại chỗ. TabControl sau đó sẽ thấy những thay đổi được thực hiện trực tiếp cho bộ sưu tập của bạn mà không cần tăng PropertyChanged và đánh giá lại toàn bộ ràng buộc.

[Edit - Added cách tiếp cận của tôi] ViewModel: public class TabSampleViewModel { _tabItems ObservableCollection tin = new ObservableCollection();

public TabSampleViewModel() 
    { 
     AddTabItem("Alpba"); 
     AddTabItem("Beta"); 
    } 

    public ObservableCollection<TabItem> TabItems 
    { 
     get 
     { 
      return _tabItems; 
     } 
    } 

    public void AddTabItem(string newTabItemName) 
    { 
     TabItem newTabItem = new TabItem(); 

     newTabItem.Header = newTabItemName; 
     newTabItem.Content = newTabItemName; 

     TabItems.Add(newTabItem); 
    } 
} 

Xem: < điều khiển: TabControl ItemsSource = "{Binding TabItems}"/>

Các vấn đề liên quan