5

Aplicación Silverlight 3 con un TabControl vinculado a un ObservableCollection utilizando un IValueConverter. Inicialmente funciona el enlace (convertidor llamado) al inicio de la aplicación. Los cambios, Borrar() o Agregar() a la colección encuadernada no se reflejan en el convertidor de TabControl ... no llamado.Silverlight TabControl vinculado a ObservableCollection <string> no se actualiza cuando se cambió la colección

nota: el ListBox enlazado refleja los cambios en la colección enlazada mientras que el TabControl no lo hace.

Ideas?

/JHD


El XAML unión ...

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

El modelo de vista ...

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

El convertidor (por completo) ...

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

Cualquier posibilidad de que el modo por defecto es un solo uso? Intentará configurar el modo explícitamente. /jhd –

+0

Probado explícitamente Modo = OneWay, no joy. Usaré el evento CollectionChanged y restableceré el TabControl.ItemsSource hasta que encuentre una mejor manera./jhd –

+0

He creado el control de pestañas extendido que funciona correctamente con la clase ObservableCollection. http://vortexwolf.wordpress.com/2011/04/09/silverlight-tabcontrol-with-data-binding/ – vorrtex

Respuesta

0

Expose

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

Si se depura el valueconverter verá que no está siendo llamado tan a menudo como usted piensa que es .

+0

¿Está diciendo que se supone que no se debe llamar al ValueConverter cuando cambia la fuente enlazada? Esto puede ser correcto, pero es contradictorio para mí. Dado que ViewModel se encuentra actualmente en el proyecto de datos/espacio de nombres, prefiero no hacer referencia a S.W.C para obtener TabItem, pero es una mejor solución que la que tengo ahora. Gracias./jhd –

+0

Hola Graeme, no se llamó al ValueConverter (después del enlace inicial) porque no implementé INotifyPropertyChanged en ViewModel. Ver mi respuesta a continuación. –

0

El problema podría ser que su ValueConverter devuelve List<TabItem> en lugar de ObservableCollection<TabItem>. Intenta cambiar esa línea y mira si te ayuda.

+0

Buena captura, por desgracia cambiando no tiene efecto [obvio]. ¿El enlace de datos no está suscrito a la fuente, nombres de clase en el caso? –

3

actualización 8/19

La respuesta concisa es que hay que poner en práctica INotifyPropertyChanged en el modelo de vista y notificar a los oyentes cuando se cambia la propiedad/Colección.

Implementar INotifyPropertyChanged en el modelo de vista

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

mejor,

/


actualización jhd modelo de vista por encima de ... ValueConverter ahora pidió a todos los cambios en la propiedad/Colección

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

vinculante El XAML ...

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

El ValueConverter (básicamente sin cambios

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

nota: No desea enviar el evento PropertyChanged en cada evento CollectionChanged en código real ... debe generar un evento PropertyChanged después de que haya terminado de manipular la colección de origen./jhd –

3

Comprendo que esto es una pregunta un poco pasada en este punto, pero no sé que nadie ha explicado por qué es necesario para hacer INotifyPropertyChanged en la propiedad enlazada en su modelo de vista.

El ItemsControl en sí necesita ser enlazado a un ObservableCollection para los eventos de cambio de colección para causar que ItemsControl reevalúe. Su convertidor devuelve una colección de Lista distinta (u Observable) cada vez que se llama en lugar de aferrarse a una sola ObservableCollection y agregarle elementos. Por lo tanto, estas colecciones nunca tienen ninguno de los eventos cambiados de colección generados en ellos ... siempre son nuevos, cada vez que se vuelve a hacer el enlace.

Al levantar PropertyChanged obliga a volver a evaluar el enlace y vuelve a ejecutar su convertidor, devolviendo una colección distinta y reflejando sus cambios.

Creo que un mejor enfoque puede ser hacer la conversión en su ViewModel en lugar de en un convertidor. Exponga una ObservableCollection de TabItem a la que se vincula directamente y que modifica en su lugar. El TabControl debería ver los cambios realizados directamente en su colección sin la necesidad de levantar PropertyChanged y volver a evaluar el enlace completo.

[Editar - Se ha añadido mi acercamiento] modelo de vista: public class TabSampleViewModel { _tabItems ObservableCollection privadas = 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); 
    } 
} 

Ver: < controles: TabControl ItemsSource = "{Binding} TabItems"/>

Cuestiones relacionadas