2009-03-19 34 views
37

Cập nhậtWPF MVVM ComboBox SelectedItem hoặc SelectedValue không làm việc

Sau một chút điều tra. Điều có vẻ là vấn đề là SelectedValue/SelectedItem đang xảy ra trước khi nguồn Item được tải xong. Nếu tôi ngồi nghỉ ngơi và chờ một vài giây, nó sẽ hoạt động như mong đợi. Không biết làm thế nào tôi sẽ nhận được xung quanh này.

End Cập nhật

Tôi có một ứng dụng sử dụng trong WPF sử dụng MVVM với một ComboBox. Dưới đây là ví dụ ViewModel. Vấn đề tôi gặp phải là khi chúng tôi rời khỏi trang của chúng tôi và di chuyển trở lại ComboBox không chọn giá trị hiện tại được chọn.

Xem Mẫu

public class MyViewModel 
{ 
    private MyObject _selectedObject; 
    private Collection<Object2> _objects; 
    private IModel _model; 

    public MyViewModel(IModel model) 
    { 
     _model = model; 
     _objects = _model.GetObjects(); 
    } 

    public Collection<MyObject> Objects 
    { 
     get 
     { 
       return _objects; 
     } 
     private set 
     { 
       _objects = value; 
     } 
    } 

    public MyObject SelectedObject 
    { 
      get 
      { 
       return _selectedObject; 
      } 
      set 
      { 
       _selectedObject = value; 
      } 
     } 
} 

Vì lợi ích của ví dụ này cho phép nói MyObject có hai thuộc tính (văn bản và Id). XAML của tôi cho ComboBox trông như thế này.

XAML

<ComboBox Name="MyComboBox" Height="23" Width="auto" 
    SelectedItem="{Binding Path=SelectedObject,Mode=TwoWay}" 
    ItemsSource="{Binding Objects}" 
    DisplayMemberPath="Text" 
    SelectedValuePath="Id"> 

Không có vấn đề mà cách nào để cấu hình này khi tôi quay trở lại trang và các đối tượng được tập hợp lại ComboBox sẽ không chọn giá trị. Đối tượng đang trả về đối tượng đúng thông qua việc lấy thuộc tính mặc dù.

Tôi không chắc liệu đây có phải là vấn đề với cách mà mô hình ComboBox và MVVM hoạt động hay không. Các ràng buộc hộp văn bản chúng tôi đang làm công trình một cách chính xác.

Trả lời

24

Bạn đã thử triển khai INotifyPropertyChanged trong chế độ xem của mình và sau đó tăng sự kiện PropertyChanged khi đặt số SelectedItem?

Nếu chính điều này không khắc phục được, thì bạn có thể tự nâng cao sự kiện PropertyChanged khi điều hướng trở lại trang và đủ để WPF tự vẽ lại và hiển thị đúng mục đã chọn .

+0

thực hiện thực tế của chúng tôi kế thừa từ một lớp ViewBase mà thực hiện INotifyPropertyChanged.Tôi đang nâng PropertyChange nhưng hộp combo vẫn trống. – cjibo

+0

Thậm chí tốt hơn nếu tôi đặt điểm ngắt và chờ một chút nó hoạt động. Tôi nghĩ rằng selecteditem của tôi là nhận được thiết lập trước khi ràng buộc của combobox được thực hiện. Urgh. – cjibo

+1

Tôi quên tăng sự kiện PorpertyChanged trên điều hướng trở lại trang. – cjibo

9

Tôi đã gặp sự cố tương tự và nó đã được giải quyết bằng cách đảm bảo rằng tôi đã triển khai IEquatable đúng cách. Khi ràng buộc xảy ra, nó đang cố gắng để xem nếu các đối tượng phù hợp để đảm bảo rằng bạn đang thực hiện đúng kiểm tra bình đẳng của bạn.

+0

Tôi hiện đã thử tính năng này. Tôi đã thực hiện IEquatable trên MyObject và khớp với các trường .id và trả về true. Điều đó cũng không có hiệu lực. Nếu tôi thêm một Textbox bị ràng buộc vào Property thì nó sẽ hiển thị thông tin chính xác khi trở về trang. – cjibo

3

Tôi đã nhận thấy hành vi này trước đây. Tôi đã nhận thấy rằng các tài sản SelectedIndex không gây ra lỗi tương tự. Nếu bạn có thể tái cấu trúc ViewModel của mình để hiển thị chỉ mục của mục đã chọn và liên kết với mục đó, bạn nên làm tốt.

0

Đó có thể là cách bạn đang áp dụng DataContext cho Trang. Trong WPF, mỗi khi bạn điều hướng đến một trang Tất cả mọi thứ được tái khởi tạo, constructor được gọi, phương thức nạp, tất cả mọi thứ. vì vậy nếu bạn đang thiết lập DataContext của bạn bên trong View của bạn, bạn sẽ không nghi ngờ gì được thổi đi mà SelectedItem mà người dùng đã chọn. Để tránh điều đó, hãy sử dụng thuộc tính KeepAlive của các trang của bạn.

<Page KeepAlive="True" ...> 
    ... 
</Page> 

Điều này sẽ chỉ dẫn đến sự kiện được nạp khi điều hướng trở lại trang bạn đã truy cập. Vì vậy, bạn sẽ cần phải đảm bảo rằng bạn đang thiết lập DataContext trên khởi tạo (hoặc bên ngoài hoặc bên trong constructor) chứ không phải là Load.

Tuy nhiên, điều này sẽ chỉ hoạt động cho trường hợp đó của Trang. Nếu bạn điều hướng đến một cá thể mới của trang đó thì hàm tạo sẽ được gọi lại.

+0

Vì tôi đang sử dụng mẫu MVVM nên không có mã nào cho các trang XAML. Không có sự kiện tải. Mọi thứ đang diễn ra thông qua databinding. Đối với vấn đề đó mẫu được chọn bởi một DataTemplateSelector dựa trên một thuộc tính trong Model View. – cjibo

2

Tôi gặp vấn đề tương tự. Vấn đề là. Mục được chọn không biết đối tượng nào cần sử dụng từ bộ sưu tập. Vì vậy, bạn phải nói với mục đã chọn để sử dụng mục từ bộ sưu tập.

public MyObject SelectedObject 
{ 
     get 
     { 
      Objects.find(x => x.id == _selectedObject.id) 
      return _selectedObject; 
     } 
     set 
     { 
      _selectedObject = value; 
     } 
} 

Tôi hy vọng điều này sẽ hữu ích.

0

ComboBox.SelectionBoxItem.ToString()

0

IsSyncronizedWithCurrent = False sẽ làm cho nó hoạt động.

20

Bạn cần đặt thuộc tính ItemsSource TRƯỚC KHI thuộc tính SelectedItem. Tôi đã gặp một blog cách đây vài ngày đề cập đến vấn đề này.

+0

Vâng, điều này đã giải quyết nó cho tôi .. – Rogier

+0

Cảm ơn rất nhiều vì điều này, tôi đã rách tóc ra cố gắng tìm ra những gì tôi đã làm sai với XAML của tôi hoặc Xem Model, khi nó đã được tất cả cùng. – ShadowLiberal

+0

Bosh, câu trả lời tuyệt vời! – dibs487

0

Sử dụng Loaded sự kiện:

private void cmb_Loaded(object sender, RoutedEventArgs e) { 
    if (cmb.Items.Count > 0) cmb.SelectedIndex = 0;   
} 

Nó làm việc cho tôi.

1

Tôi đã chiến đấu với vấn đề này trong một thời gian. Trong trường hợp của tôi, tôi đã sử dụng loại phức tạp (Danh sách) làm Nguồn mục và đang sử dụng một KeyType làm giá trị được chọn. Trên sự kiện tải, KeyType đã được đặt thành rỗng. Điều này gây ra mọi thứ để phá vỡ. Không có phần tử con nào được cập nhật khi khóa đã thay đổi. Hóa ra là khi tôi thêm một kiểm tra để đảm bảo giá trị được đề xuất cho KeyType không phải là null, mọi thứ hoạt động như mong đợi.

#region Property: SelectedKey 
    // s.Append(string.Format("SelectedKey : {0} " + Environment.NewLine, SelectedKey.ToString())); 

    private KeyType _SelectedKey = new KeyType(); 
    public KeyType SelectedKey 
    { 
     get { return _SelectedKey; } 
     set 
     { 
      if(value != null) 
       if (!_SelectedKey.Equals(value)) 
       { 
        _SelectedKey = value; 
        OnPropertyChanged("SelectedKey"); 
       } 
     } 
    } 
    #endregion SelectedKey 
4

Khi rời khỏi trang hiện tại, CollectionView gắn liền với ItemsSource tài sản của ComboBox được thanh lọc. Và vì thuộc tính ComboBoxIsSyncronizedWithCurrent là đúng theo mặc định, các thuộc tính SelectedItemSelectedValue được đặt lại.
Điều này có vẻ là một vấn đề kiểu dữ liệu nội bộ trong ràng buộc. Như những người khác đã đề xuất ở trên, nếu bạn sử dụng SelectedValue thay thế bằng cách liên kết với thuộc tính int trên viewmodel, nó sẽ hoạt động. Phím tắt cho bạn sẽ ghi đè toán tử Equals trên MyObject để khi so sánh hai MyObject, thuộc tính thực tế Id được so sánh.

Một gợi ý khác: Nếu bạn tái cấu trúc chế độ xem và sử dụng SelectedValue, chỉ sử dụng khi SelectedValuePath=Id nơi Idint. Nếu sử dụng khóa chuỗi, hãy liên kết với thuộc tính Text của ComboBox thay vì SelectedValue.

+1

Tôi đã có một vấn đề rất giống nhau. Lý do của vấn đề là các ItemsSource đã được ràng buộc với một RelativeSource và SelectedItem/SelectedValue đã bị ràng buộc trực tiếp vào DataContext. Tôi đã có thể giải quyết nó nhưng lý do của vấn đề là không rõ ràng. Nó được mô tả ở đây cho bất cứ ai có cùng một vấn đề: http://stackoverflow.com/questions/33739513/wpf-mvvm-combobox-selectedvalue-is-cleared-when-navigating-away –

1

Loại SelectedValuePathSelectedValue phải CHÍNH XÁC giống nhau.

Nếu ví dụ: loại SelectedValuePathInt16 và loại thuộc tính liên kết với SelectedValueint, nó sẽ không hoạt động.

Tôi dành hàng giờ để tìm điều đó, và đó là lý do tại sao tôi trả lời ở đây sau rất nhiều thời gian khi được hỏi. Có lẽ một người đàn ông tội nghiệp như tôi với cùng một vấn đề có thể nhìn thấy nó.

2

Tôi có câu trả lời rất đơn giản cho vấn đề này. Trước tiên, thêm mã sau vào View IsSynchronizedWithCurrentItem = "True".

Tiếp theo khi bạn gán một đối tượng mới trong ViewModel cho thuộc tính SelectedObject đó sẽ được lưu vào Thuộc tính đó chứ không phải thành viên riêng tư.

ViewModel Proptery sẽ trông như thế này

public Role SelectedObject 
    { 
     get { return object; } 
     set 
     { 
      if (value != null) 
      { 
       if (!object.Equals(value)) 
       { 
        object = value; 
        OnPropertyChanged(() => SelectedObject); 
       } 
      } 
     } 
    } 

này cần khắc phục vấn đề này.

1

Tôi gặp sự cố này với ComboBox hiển thị danh sách các màu (Danh sách < Cọ >).
Lựa chọn một màu sắc là có thể nhưng nó wasnt hiển thị khi chọn đóng (mặc dù tài sản đã được thay đổi!)

Việc sửa chữa được ghi đè Equals (object obj) phương pháp cho các loại hình được chọn trong ComboBox (Brush) , không đơn giản vì Brush được niêm phong. Vì vậy, tôi đã viết một lớp EqualityBrush chứa một bàn chải và thực hiện Equals:

public class EqualityBrush 
{ 
    public SolidColorBrush Brush { get; set; } 

    public override bool Equals(object o) 
    { 
     if (o is EqualityBrush) 
     { 
      SolidColorBrush b = ((EqualityBrush)o).Brush; 
      return b.Color.R == this.Brush.Color.R && b.Color.G == this.Brush.Color.G && b.Color.B == this.Brush.Color.B; 
     } 
     else 
      return false; 
    } 
} 

Sử dụng một danh sách của lớp EqualityBrush mới của tôi thay vì lớp Cọ bình thường cố định được vấn đề!

Combobox My XAML trông như thế này:

<ComboBox ItemsSource="{Binding BuerkertBrushes}" SelectedItem="{Binding Brush, Mode=TwoWay}" Width="40"> 
    <ComboBox.Resources> 
     <DataTemplate DataType="{x:Type tree:EqualityBrush}"> 
      <Rectangle Width="20" Height="12" Fill="{Binding Brush}"/> 
     </DataTemplate> 
    </ComboBox.Resources> 
</ComboBox> 

Hãy nhớ rằng tôi "Brush" -Property trong ViewModel bây giờ đã là của Loại EqualityBrush!

23

Đặt IsSynchronizedWithCurrentItem="True" có hiệu quả đối với tôi!

+0

có nó hoạt động Cảm ơn rất nhiều. –

+0

Cảm ơn Wim. Đã đấu tranh với điều này trong 40 phút. – sander

+0

Đã đấu tranh với điều này. Điều này làm việc hoàn hảo cho tôi. Cảm ơn :) –

9

Trong trường hợp này, liên kết selecteditem không hoạt động, vì id băm của các đối tượng khác nhau.

Một giải pháp khả thi là:

Dựa trên id mục được lựa chọn, phục hồi các đối tượng trên bộ sưu tập itemsource và thiết lập thuộc tính sản phẩm được chọn để với nó.

Ví dụ:

<ctrls:ComboBoxControlBase SelectedItem="{Binding Path=SelectedProfile, Mode=TwoWay}" ItemsSource="{Binding Path=Profiles, Mode=OneWay}" IsEditable="False" DisplayMemberPath="Name" /> 

Các tài sản binded đến ItemSource là:

public ObservableCollection<Profile> Profiles 
{ 
    get { return this.profiles; } 
    private set { profiles = value; RaisePropertyChanged("Profiles"); } 
} 

Thuộc tính binded đến SelectedItem là:

public Profile SelectedProfile 
{ 
    get { return selectedProfile; } 
    set 
    { 
     if (this.SelectedUser != null) 
     { 
      this.SelectedUser.Profile = value; 
      RaisePropertyChanged("SelectedProfile"); 
     } 
    } 
} 

Mã phục hồi là:

[Command("SelectionChanged")] 
    public void SelectionChanged(User selectedUser) 
    { 
     if (selectedUser != null) 
     { 
      if (selectedUser is User) 
      { 
       if (selectedUser.Profile != null) 
       { 
        this.SelectedUser = selectedUser; 
        this.selectedProfile = this.Profiles.Where(p => p.Id == this.SelectedUser.Profile.Id).FirstOrDefault(); 
        MessageBroker.Instance.NotifyColleagues("ShowItemDetails"); 
       } 
      } 
     }    
    } 

Tôi hy vọng nó sẽ giúp bạn. Tôi đã dành rất nhiều thời gian để tìm kiếm câu trả lời, nhưng tôi không thể tìm thấy.

+0

Điều này thật tuyệt vời! Cảm ơn –

0

tôi giải quyết vấn đề bằng cách thêm điều phối trong UserControl_Loaded kiện

Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => 
{ 
    combobox.SelectedIndex = 0; 
})); 
Các vấn đề liên quan