2010-02-09 25 views
6

aşağıdaki XAML düşünün değiştirir:WPF ComboBox sıfırlar madde kaynaklı

<ComboBox Name="CompanyComboBox" 
    HorizontalAlignment="Stretch" 
    ItemsSource="{Binding Path=GlobalData.Companies}" 
    SelectedValuePath="Id" 
    SelectedValue="{Binding Customer.CompanyId, ValidatesOnDataErrors=True}" 
    DisplayMemberPath="Name" /> 

GlobalData.Companies şirketlerinin bir koleksiyon (IEnumerable<Company>) 'dir; Bu koleksiyon arka plan üzerinde yeniden yüklenebilir (bir web servisinden indirilir). Bu olduğunda, ComboBox doğru olarak öğeleri bağlayıcı yoluyla yeniden yükler. Ancak bir yan etki olarak, seçilen öğeyi de sıfırlar!

Combo-box kaynaklarını denetlemek için Reflector'ı kullandım ve görünüşe göre bu amaçlanan bir davranıştır.

Bunun için nasıl "güzel" bir yol var? Kazanmak istediğim şey, eğer kullanıcı "A Şirketi" ni seçerse ve daha sonra şirketlerin listesini yeniden yüklerse, "A Şirketi" nin (yeni listede olduğu varsayılarak) seçildiği durumdur. Bir "güzel" yolu ise

cevap

4

Belki yerine IEnumerable<Company> ait ObservableCollection<Company> kullanabilir? Ardından, arka planda değişiklik yaparak, yalnızca yeni listeden yeni/eksik olan Öğeleri Ekle/Kaldır, seçilen öğe, değişiklikten kaldırılmadıkça kalmalıdır.

update your observable collection in a separate thread with a small hack-around. Yacoder belirttiği gibi

+0

Bu, aşağıdaki gibi güzel/kolay olmamakla birlikte çözüm olabilir: - | –

+0

Koleksiyonun arka plan iş parçacığından güncellenmesi gerekmeyebilir. Geçerli koleksiyonu web hizmetinden arka planda alın ve sonra (bir BackgroundWorker kullandığınızı varsayarak), RunWorkerCompleted olayındaki 'ObservableCollection ' güncelleştirin. –

0

hmm bilmiyorum ama yeniden oluşmadan önce seçilen öğeye erişebilir eğer, bunu (veya anahtar falan) kaydedebilir ve programlama tekrar seçin yeniden yükleme bittikten sonra.

hızlı mockup:

var selectedItem = myCombo.SelectedItem; 
DoReload(); 
myCombo.SelectedItem = selectedItem; 

Ama etrafta bu el emeği daha başka bir yol demek varsayıyorum? Bu zaten yardımcı
Umut ...
Tamam ben bir arka plan iplikten, bkz

GÜNCELLEME.
Açılan kutunuzu bağlamak için bir ICollectionView kullanıyor musunuz? Eğer öyleyse, referans tutmak için CurrentItem özelliğini kullanabilirsiniz. Hızlı bir mockup yaptım ve bu benim kurulumum üzerinde çalışıyor.

<Grid VerticalAlignment="Top"> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition /> 
     <ColumnDefinition /> 
    </Grid.ColumnDefinitions> 
    <ComboBox ItemsSource="{Binding Items}" IsSynchronizedWithCurrentItem="True" Grid.Column="0" Grid.Row="0" DisplayMemberPath="Name"/> 
    <Button Command="{Binding UpdateCommand}" Grid.Column="1" Grid.Row="0">Update</Button> 
</Grid> 

Görünüm/ViewModel

public partial class Window1 : Window { 
    public Window1() { 
     InitializeComponent(); 
     this.DataContext = new ViewModel(this); 
    } 
} 

public class ViewModel 
{ 
    private readonly Window1 window; 
    private ObservableCollection<Item> items; 
    private ICollectionView view; 

    public ViewModel(Window1 window) { 
     this.window = window; 
     items = new ObservableCollection<Item> 
      { 
       new Item("qwerty"), 
       new Item("hello"), 
       new Item("world"), 
      }; 

     view = CollectionViewSource.GetDefaultView(items); 
    } 

    public ObservableCollection<Item> Items { get { return items; } } 

    public ICommand UpdateCommand { 
     get { return new RelayCommand(DoUpdate); } 
    } 

    public Item SelectedItem { get; set; } 

    private void DoUpdate(object obj) { 
     var act = new Func<List<Item>>(DoUpdateAsync); 
     act.BeginInvoke(CallBack, act); 
    } 

    private List<Item> DoUpdateAsync() { 
     return new List<Item> { 
       new Item("hello"), 
       new Item("world"), 
       new Item("qwerty"), 
      }; 
    } 

    private void CallBack(IAsyncResult result) { 
     try { 
      var act = (Func<List<Item>>)result.AsyncState; 
      var list = act.EndInvoke(result); 

      window.Dispatcher.Invoke(new Action<List<Item>>(delegate(List<Item> lst) { 
                    var current = lst.Single(i => i.Name == ((Item)view.CurrentItem).Name); 
                    Items.Clear(); 
                    lst.ForEach(Items.Add); 
                    view.MoveCurrentTo(current); 
                   }), list); 

     } catch(Exception exc){ Debug.WriteLine(exc); } 
    } 
} 

public class Item { 
    public Item(string name) { 
     Name = name; 
    } 
    public string Name { get; set; } 
} 

Sen durumda bazı işleme yapmak gerekir Seçilen öğe hayır

XAML: Bu sizin UI bir başvuru var varsayar listede daha uzun.
IsSynchronizedWithCurrentItem özelliği burada önemlidir, aksi halde çalışmaz!
Ayrıca, ana pencereye yapılan referansın bir DI-çerçevesiyle olması gerekir.

+0

sorun DoReload() GUI hakkında bir fikri olmayan sınıfın arka kısımdaki gerçekleştirilir olmasıdır. GUI, Binding & INotifyPropertyChanged aracılığıyla yeni değerler hakkında bilgilendirilir. –

0

bu nesne eşitlik ile ilgisi var. SelectedValue yerine SelectedItem'i bağladığınız sürece, ItemsSource'u anonim bir tür koleksiyonu olarak tanımlayabilirsiniz. Sonra bu sorun oluşmaz (ve bir veritabanından değerleri okumanız gerekirse daha hızlıdır).