databinding - ¿Qué causa que los elementos de mi cuadro combinado de WPF tarden tanto en actualizarse cuando se cambia el origen de los elementos?
wpf combobox datacontext (1)
Tengo una cuadrícula de datos (llámalo dat1) que tiene un origen de elementos vinculado a una colección observable de un tipo personalizado, llámalo TypeA. Una de las propiedades de TypeA es una colección observable de otro tipo personalizado, llámalo TypeB. Luego tengo un cuadro combinado con una fuente de elementos vinculada a SelectedItem.TypeB de dat1.
Por lo tanto, cuando el usuario selecciona un TypeA en dat1, el cuadro combinado muestra los elementos en la colección observable TypeB del TypeA seleccionado. ¿Tener sentido?
El enlace SÍ funciona y SÍ actualiza. El problema es que cuando el presentador de elementos en el cuadro combinado ya ha mostrado elementos y el usuario selecciona un Tipo A diferente en dat1 e intenta ver los nuevos elementos en el cuadro combinado, hay una pausa larga mientras el presentador de elementos genera los nuevos elementos.
Para probar el problema, puedo simplificar el escenario.
Pasos para reproducir:
Crea un nuevo proyecto WPF usando .NET 4.0.
Corta y pega el código a continuación.
Para obtener el comportamiento de congelación, debe soltar el cuadro combinado para ver los elementos, luego hacer clic en el botón para que los elementos de origen cambien y luego soltar el cuadro combinado de nuevo. El cuadro combinado cae después de unos segundos, pero ¿por qué tan lento?
XAML
<Window x:Class="ComboBoxTest.MainWindow"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<ComboBox x:Name="cbo" DisplayMemberPath="Junk1"></ComboBox>
<Button Content="Click Me!" Click="btn_Click"></Button>
</StackPanel>
</Grid>
</Window>
Código
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.cbo.ItemsSource = junk1;
}
ObservableCollection<Junk> junk1 = new ObservableCollection<Junk>() {
new Junk() { Junk1 = "junk1 - 1" },
new Junk() { Junk1 = "junk1 - 2" } };
ObservableCollection<Junk> junk2 = new ObservableCollection<Junk>() {
new Junk() { Junk1 = "junk2 - 1" },
new Junk() { Junk1 = "junk2 - 2" },
new Junk() { Junk1 = "junk2 - 3" },
new Junk() { Junk1 = "junk2 - 4" } };
private void btn_Click(object sender, RoutedEventArgs e)
{
if (this.cbo.ItemsSource == junk1)
this.cbo.ItemsSource = junk2;
else
this.cbo.ItemsSource = junk1;
}
}
public class Junk
{
public string Junk1 { get; set; }
}
NOTA: Este es un problema de WPF. Escuché que Silverlight no tiene el mismo problema. No necesito saber si Silverlight funciona. Necesito una respuesta WPF.
PD. La demora es mayor cuando la fuente de elementos cambia a junk2, presumiblemente porque es más grande.
Retrasa lo suficiente que creo que puede ser causado por excepciones vinculantes, ya que las excepciones toman tiempo. ¿Hay alguna manera de ver si hay excepciones vinculantes lanzadas?
Observo este fenómeno también. Estoy usando Visual Studio 2010 (con ReSharper 6.0) en Windows 7 x64.
No se nota con solo cuatro elementos como en el ejemplo anterior, pero si lo hago, por ejemplo, 50 o más elementos, la congelación se nota mucho. Después de la reenlace, se bloqueará durante unos 15 segundos antes de que pueda interactuar de nuevo.
Otra cosa interesante es que esto solo ocurre mientras se depura en VS. Si ejecuto el exe de manera autónoma, es realmente ágil y rápido.
Aquí está el código de mi proyecto simple:
XAML
<Window x:Class="ComboBoxFreeze.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ComboBox x:Name="cbo" DisplayMemberPath="Junk1"></ComboBox>
<Button Content="Click Me!" Click="btn_Click"></Button>
</StackPanel>
</Window>
Código
using System.Collections.ObjectModel;
using System.Windows;
namespace ComboBoxFreeze
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
_junk1 = new ObservableCollection<Junk>();
for (int i = 0; i < 50; i++)
{
_junk1.Add(new Junk { Junk1 = "Prop1a-" + i, Junk2 = "Prop1b-" + i });
}
_junk2 = new ObservableCollection<Junk>();
for (int i = 0; i < 50; i++)
{
_junk2.Add(new Junk { Junk1 = "Prop2a-" + i, Junk2 = "Prop2b-" + i });
}
}
private readonly ObservableCollection<Junk> _junk1;
private readonly ObservableCollection<Junk> _junk2;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
cbo.ItemsSource = _junk1;
}
private void btn_Click(object sender, RoutedEventArgs e)
{
if (cbo.ItemsSource == _junk1)
{
cbo.ItemsSource = _junk2;
}
else
{
cbo.ItemsSource = _junk1;
}
}
}
public class Junk
{
public string Junk1 { get; set; }
public string Junk2 { get; set; }
}
}
Volveré a publicar aquí si encuentro una solución o una solución a esto.