2011-05-26 109 views
12

我有一個具有許多屬性的Activity對象。其中之一是:使用MVVM的WPF組合框的雙向綁定問題

public ActivityStatus Status 
{ 
    get { return status; } 
    set { status = value; NotifyPropertyChanged("Status"); } 
} 

ActivityStatus類只有兩個屬性:

public Guid Guid 
{ 
    get { return guid; } 
    set { guid = value; NotifyPropertyChanged("Guid"); } 
} 
public string Name 
{ 
    get { return name; } 
    set { name = value; NotifyPropertyChanged("Name"); } 
} 

Equals方法:

public override bool Equals(object otherObject) 
{ 
    if (!(otherObject is ActivityStatus)) return false; 
    return Equals(otherObject as ActivityStatus); 
} 
public bool Equals(ActivityStatus otherStatus) 
{ 
    if (!(otherStatus is ActivityStatus) || otherStatus == null) return false; 
    return Guid == otherStatus.Guid && Name == otherStatus.Name; 
} 

我有一個ActivityViewModel類爲一體的DataContext一個類。 ActivityViewModelActivity類型Activity財產,其中ActivityStatuses類型ObservableCollection<ActivityStatus>類型的財產。在我有一個ComboBox聲明如下:

<ComboBox ItemsSource="{Binding ActivityStatuses}" 
      SelectedItem="{Binding Activity.Status, Mode=TwoWay}" 
      DisplayMemberPath="Name" /> 

這讓我選擇從ComboBoxActivityStatus,這在視圖模型的Activity屬性正確更新ActivityStatus財產。問題在於雙向綁定...當加載新的Activity時,ComboBox.SelectedItem不會更新以顯示Activity.Status屬性值。

使用ComboBox的該聲明,SelectedItem勢必在ActivityActivityStatus對象,這是一個不同的目的是在一個與所述視圖模型ActivityStatuses屬性相同的值。因此,WPF框架不認爲這些項目是相同的,並且不會選擇ComboBox中的項目。

如果我每次加載後Activity從分配與到Activity.Status屬性相同的值集合中的項目,則ComboBox找到匹配它ItemsSource收集並正確設置SelectedItem屬性顯示的值。我不想這樣做,但因爲我在Activity類中有許多其他類似的屬性,所以我將不得不在任何地方重複此代碼,以便雙向綁定到ComboBox es。

所以我也試過如下結合ActivityStatus.Guid屬性:

<ComboBox ItemsSource="{Binding ActivityStatuses}" 
      SelectedValue="{Binding Activity.Status.Guid, Mode=TwoWay}" 
      SelectedValuePath="Guid" 
      DisplayMemberPath="Name" /> 

加載不同Activity對象時,這正確選擇的對象具有相同Guid作爲一個從ComboBox.ItemsSource集合Activity.Status財產。此方法的問題是SelectedValue綁定到ActivityStatus對象中的ActivityStatus.Guid屬性,因此當更改UI中的值時,只有ActivityStatus對象的'Guid'屬性會更新,而名稱保持不變。除Guid屬性的值外,Activity.Status屬性中的對象不會更改。

正如你所看到的,我也嘗試過實施Equals方法,因爲我認爲ComboBox會使用它來比較對象,但它沒有任何區別。所以最後,我不知所措,急於找到一個簡單的方法來解決這個問題......希望有一個簡單的屬性,我錯過了ComboBox

我只是希望能夠選擇在ComboBox一個項目,因此有Activity.Status對象的變化和改變從代碼Activity.Status屬性的值,並有ComboBox.SelectedItem也相應更新。我會很感激任何建議。

UPDATE >>>

讀數的響應後,我盡了代碼樣本中的新的解決方案,並看到它的工作如預期。然後我仔細檢查了他的代碼,發現它和我的代碼一樣,所以再次運行我自己的解決方案(這是本文以來的第一次)。令我驚訝的是,它沒有改變任何代碼就按預期工作!

這讓我很困惑,我花了一些時間來了解發生了什麼事。事實證明,問題在於Visual Studio 2010!作爲最後階段,我已將Equals方法添加到我的數據類型中。出於某種原因,Visual Studio在運行應用程序時沒有構建數據類型項目。

因此,應用程序必須使用一個較舊的dll文件,我的更改沒有被使用......我確實想知道爲什麼我的斷點在Equals方法從未被擊中。這導致了我的假設,即實施Equals方法無濟於事。 Visual Studio今天有同樣的行爲,這就是我發現發生了什麼。

我在我的解決方案中檢查了項目構建順序,但是在順序中的正確位置列出了數據類型項目。雖然在運行應用程序時,Visual Studio中的「輸出」窗口顯示了以不同順序加載的項目dll。我不確定爲什麼運行應用程序不再執行完整的構建,但至少我知道在運行應用程序之前必須先修改該項目。

最後更新>>>

我剛剛發現,爲什麼我的數據類型項目沒有建設......我看着在配置管理器窗口,只見那莫名其妙的平臺是不正確的這一項目,並生成複選框已經變得無法檢查!我不知道這是怎麼發生的,但是我終於明白了問題的根源讓我感到寬慰。

+0

這裏有什麼是經典的「項目參考/文件參考」問題。您應該刪除對解決方案中其他項目的所有引用,並將它們重新添加爲**項目引用**。在構建或更改構建類型(如調試/發佈)時,文件引用不會自動更新。 *不要瀏覽到/ bin目錄以添加引用!*有關** Project References ** [請檢查此msdn文章]的更多信息(http://msdn.microsoft.com/zh-cn/library/ez524kew.aspx ) – Will 2011-05-30 17:42:54

+1

謝謝@威爾,但我剛剛發現爲什麼我的數據類型項目不在構建中......我查看了Configuration Manager窗口,發現該平臺對於該項目來說不正確,並且Build複選框未被選中!我不知道這是怎麼發生的,但是我終於明白了問題的根源讓我感到寬慰。 – Sheridan 2011-06-04 00:04:02

回答

12

我有一些壞消息給你。它應該工作。在其他地方有一個錯誤/意外的副作用導致您的問題。

我把一個快速項目放在一起,做你想做的事情。喜歡在這裏看到它。

創建一個名爲NestedProperties的新WPF項目。添加一個新類到根並粘貼以下代碼(我已刪除了很多東西,所以這是一個有點難看):

public sealed class ViewModel : DependencyObject 
{ 
    public ObservableCollection<Activity> Activities 
      { get; private set; } 
    public ObservableCollection<ActivityStatus> Statuses 
      { get; private set; } 

    public static readonly DependencyProperty 
     SelectedActivityProperty = 
     DependencyProperty.Register(
      "SelectedActivity", 
      typeof(Activity), 
      typeof(ViewModel), 
      new UIPropertyMetadata(null)); 
    public Activity SelectedActivity 
    { 
     get { return (Activity)GetValue(SelectedActivityProperty); } 
     set { SetValue(SelectedActivityProperty, value); } 
    } 

    public ViewModel() 
    { 
     Activities = new ObservableCollection<Activity>(); 
     Statuses = new ObservableCollection<ActivityStatus>(); 

     // NOTE! Each Activity has its own ActivityStatus instance. 
     // They have the same Guid and name as the instances in 
     // Statuses!! 
     for (int i = 1; i <= 4; i++) 
     { 
      var id = Guid.NewGuid(); 
      var aname = "Activity " + i; 
      var sname = "Status " + i; 
      Activities.Add(new Activity 
      { 
       Name = aname, 
       Status = new ActivityStatus 
       { 
        Name = sname, 
        Id = id, 
        InstanceType = "Activity" 
       } 
      }); 
      Statuses.Add(new ActivityStatus 
      { 
       Name = sname, 
       Id = id, 
       InstanceType = "Collection" 
      }); 
     } 
    } 
} 

public sealed class Activity : DependencyObject 
{ 
    public static readonly DependencyProperty NameProperty = 
     DependencyProperty.Register(
      "Name", 
      typeof(string), 
      typeof(Activity), 
      new UIPropertyMetadata(null)); 
    public string Name 
    { 
     get { return (string)GetValue(NameProperty); } 
     set { SetValue(NameProperty, value); } 
    } 
    public static readonly DependencyProperty StatusProperty = 
     DependencyProperty.Register(
      "Status", 
      typeof(ActivityStatus), 
      typeof(Activity), 
      new UIPropertyMetadata(null)); 
    public ActivityStatus Status 
    { 
     get { return (ActivityStatus)GetValue(StatusProperty); } 
     set { SetValue(StatusProperty, value); } 
    } 
} 
public sealed class ActivityStatus 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    /// <summary> 
    /// indicates if this instance came from 
    /// the ComboBox or from the Activity 
    /// </summary> 
    public string InstanceType { get; set; } 
    public ActivityStatus() 
    { 
     Id = Guid.NewGuid(); 
    } 
    public override bool Equals(object otherObject) 
    { 
     if (!(otherObject is ActivityStatus)) return false; 
     return Equals(otherObject as ActivityStatus); 
    } 
    public bool Equals(ActivityStatus otherStatus) 
    { 
     if (!(otherStatus is ActivityStatus) || 
      otherStatus == null) return false; 
     return Id == otherStatus.Id && 
      Name == otherStatus.Name; 
    } 
} 

現在打開主窗口並粘貼此在:

<Window 
    x:Class="NestedProperties.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" 
    xmlns:t="clr-namespace:NestedProperties" 
    SizeToContent="Height" 
    MaxHeight="350" 
    Width="525"> 
    <Window.DataContext> 
     <t:ViewModel /> 
    </Window.DataContext> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition 
       Height="auto" /> 
      <RowDefinition 
       Height="auto" /> 
      <RowDefinition /> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition /> 
      <ColumnDefinition /> 
     </Grid.ColumnDefinitions> 
     <Label>Select an Activity:</Label> 
     <ComboBox 
      Grid.Row="1" 
      ItemsSource="{Binding Activities}" 
      SelectedItem="{Binding SelectedActivity}" 
      DisplayMemberPath="Name" /> 
     <Label 
      Grid.Column="1">Select a Status</Label> 
     <ComboBox 
      Grid.Row="1" 
      Grid.Column="1" 
      ItemsSource="{Binding Statuses}" 
      SelectedItem="{Binding SelectedActivity.Status}" 
      DisplayMemberPath="Name" /> 
     <ContentControl 
      Grid.Row="2" 
      Grid.ColumnSpan="2" 
      Content="{Binding SelectedActivity}"> 
      <ContentControl.ContentTemplate> 
       <DataTemplate> 
        <StackPanel> 
         <Label>Selected Activity:</Label> 
         <TextBlock 
          Text="{Binding Name}" /> 
         <Label>Activity Status</Label> 
         <TextBlock 
          Text="{Binding Status.Name}" /> 
         <Label>Status Id</Label> 
         <TextBlock 
          Text="{Binding Status.Id}" /> 
         <Label>Status came from</Label> 
         <TextBlock 
          Text="{Binding Status.InstanceType}" /> 
        </StackPanel> 
       </DataTemplate> 
      </ContentControl.ContentTemplate> 
     </ContentControl> 
    </Grid> 
</Window> 

當你運行這個,你會發現你有四個活動和四個狀態。如果您翻閱活動組合,您會看到每個狀態標記爲活動,這意味着它是在ViewModel的構造函數中爲活動提供的實例。 您還將看到狀態組合框隨着活動更改而改變,這意味着Equals方法正在工作。

接下來,更改每個活動的狀態。您將看到狀態更改的類型爲Collection,這意味着此實例已創建並添加到構造函數中的Statuses集合中。

那麼爲什麼這個工作,但你的代碼不是?我不確定。你的問題在於你的代碼的其他地方。

-1

人,如果我跟着你的問題正是我不知道,但是當你說

加載一個新的活動時,

你添加新的ActivityActivityStatuses收藏?因爲如果你不是那麼我很確定綁定不起作用,因爲SelectedItem需要在ItemsSource中。

只是一個想法。