2013-07-18 179 views
0

我在我的WPF/MVVM(帶MVVM Light框架)遊戲中綁定了一個TextBlock遊戲,該遊戲綁定到一個屬性,該屬性應該反映僱用員工的數量。我已確認綁定完好無損,但無法更新。WPF中的綁定屬性不更新

這裏是在我看來,TextBlock

<TextBlock x:Name="WorkersTextBlock" 
      FontFamily="Pericles" 
      DataContext="{Binding Guilds[0]}" 
      Text="{Binding Workers.Count, 
          StringFormat=Workers : {0}, 
          FallbackValue=Workers : 99}" /> 

在我的視圖模型屬性:

public ObservableCollection<Guild> Guilds 
{ 
    get { return DataManager.Data.Guilds; } 
} 

而且在我的視圖模型,該命令更改Worker的僱主財產:

private void ExecuteHireWorkerCommand() 
{ 
    if (SelectedWorker == null) 
     return; 

    SelectedWorker.Employer = DataManager.Data.Guilds[0]; 
    Gold -= SelectedWorker.Salary; 
    _workerCollectionView.Refresh(); 
} 

在DataManager中,它是一個包含我所有數據的單例類:

private ObservableCollection<Guild> _guilds = new ObservableCollection<Guild>(); 
public ObservableCollection<Guild> Guilds 
{ 
    get { return _guilds; } 
} 

private ObservableCollection<Worker> _workers = new ObservableCollection<Worker>(); 
public ObservableCollection<Worker> Workers 
{ 
    get { return _workers; } 
} 

Guild模式:

public ObservableCollection<Worker> Workers 
{ 
    get { return DataManager.Data.Workers.Where(w => w.Employer == this).ToObservableCollection(); } 
} 

Worker僱主屬性是:

public Guild Employer { get; set; } 

而在去年,我的擴展方法(我認爲這是問題的根源):

public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> source) 
{ 
    if (source == null) 
    { 
     throw new ArgumentNullException("source"); 
    } 
    return new ObservableCollection<T>(source); 
} 

消息框確認通過命令Worker s「僱主」屬性正在更新,但我沒有試過任何東西使得TextBlock更新。我已經嘗試在我在這裏列出的一切都沒有運氣的情況下實施RaisePropertyChanged

如果我在初始化數據時將構造函數中的Worker的Employment屬性設置爲正確的公會,則TextBlock中的數字將正確顯示,但在此之後不會更新。我的直覺是Workers屬性中的LINQ過濾和擴展方法導致了這個麻煩,但我可能是錯的。

如果任何人有任何想法如何使這個工作,我很樂意聽到他們。任何有關此事的建議都將不勝感激。如果您需要更多的代碼或信息,請詢問。

謝謝。

更新:我認爲羅恩是在正確的道路上;擴展方法可能會破壞綁定。如果是這樣的話,任何人都可以給我任何建議,如何在不破壞綁定的情況下過濾Guild中的Workers屬性?另外,就setter問題而言,我向Workers屬性添加了一個setter,但它實際上從未實際觸發。

+0

這個神奇的可能是「我已經嘗試在我沒有運氣列出的所有事情上實現RaisePropertyChanged」向我們展示該代碼。我的猜測是你在那裏犯了一些錯誤。 –

+0

我用它來通知:http://kindofmagic.codeplex.com/這很難搞砸了,它完美適用於其他屬性。 –

+1

我幾乎沒有見過更糟糕的命名屬性。它應該被稱爲NotifiesWhenPropertyChangesAttribute。魔術可以是任何東西。 –

回答

1

我認爲你需要重新設計底層數據結構。但儘管如此,你可以稍微改變它以使其工作。

更改Workers屬性ICollectionView像這樣:

public ICollectionView Workers { get; set; }

然後在公會模型的contstructor您可以從您的數據管理器填充工人集合,像這樣:

Workers = CollectionViewSource.GetDefaultView(DataManager.Data.Workers);

並添加一個過濾器到你的ICollectionView像這樣:

Workers.Filter = (worker) => { return (worker.Employer == this); };

並且每當Workers集合被更新時調用Workers.Refresh()

這樣你的綁定不會中斷,你的Workers集合將保留相同的實例。

哦,並將UpdateSourceTrigger=PropertyChanged添加到您的TextBox綁定。

就像我說過的,我會着重於完全重新設計支持數據結構,但不知道爲什麼或如何實現它,我不能說更多。

+0

我剛試過這種方法,並得到它的工作,它應該做得很好。謝謝你的幫助。就支持數據而言,它全部存儲在單一類中用於全局訪問。這是我採取的第一個真正的項目,我很缺乏經驗,所以我真的不知道有更好的方法去做。它目前包含的所有內容都是4個'ObservableCollection',這是我正在處理的每個主要類型的一個主列表。如果你能提出一個更好的方法去做,我很樂意去學習它。我知道我在做什麼並不完美。 –

+0

如果我沒有全部要求,那麼很難告訴你如何改進支持數據的結構,但是我會爲'Model'本身的每個類型保留一個私有的靜態'ObservableCollection'。這樣你就不必重定向到數據管理器,一切都包含在一個類中。所以'公會'將成爲'模型',並公開靜態字段/屬性'ObservableCollection Guilds'。 ANYWAY,這是我會這樣做的一種方式。 – Xtr

+0

我不確定我完全理解。也許我誤解了MVVM中模型的概念,但現在我有四種主要類型的模型。每個模型只包含所需的公共屬性和初始化數據的方法。每種類型都有多個實例,所以我使用單例類來將它們全部放在一箇中心位置,因此我們使用'ObservableCollection '。我不確定在模型中包含'ObservableCollection'如何工作,或者它如何更有效。 –

1
public ObservableCollection<Worker> Workers 
{ 
    get { return DataManager.Data.Workers.Where(w => w.Employer == this).ToObservableCollection(); } 
} 

當此屬性實際發生更改時,您不通知綁定系統。如果底層的集合本身發生了變化,你會沒事的。但是你甚至沒有保留對這個底層集合的引用 - 你只是將它返回。

正常模式會是這樣的(假設你在Guild實施INotifyPropertyChanged

private ObservableCollection<Worker> _Workers; 
public ObservableCollection<Worker> Workers 
{ 
    get { return _Workers; } 
    set 
    { 
     if (value != _Workers) 
     { 
      _Workers = value; 
      NotifyPropertyChanged("Workers") 
     } 
    } 
} 

但它一定程度上取決於你是如何你的對象設立。無論如何,你需要通知系統該集合以某種方式改變。

編輯:你在評論中提到你使用魔法種類。我去閱讀文檔。它說如何工作,它說

5)顯式或隱式地應用MagicAttribute轉換公共屬性的所有可用setter。

您沒有該屬性的setter,所以它不會修復它。

+0

將一個setter添加到'Workers'屬性並在'DataManager'中添加Notify屬性不會改變任何內容。就「工人」財產而言,我不能在setter中使用LINQ篩選。我設法通過使用CollectionView綁定到DataGrid時解決了這個問題,但我不知道在這種情況下該怎麼做。 –

+0

我在'Guild'中的Workers屬性中添加了一個佔位符setter,但即使'Worker'的Employer屬性中的setter也不會觸發。 –

+0

我認爲這裏的底線是如果您依靠LINQ篩選來創建屬性,那麼您將無法執行綁定。如果我是你,我會考慮設置一個ViewModel,將你的模型的部分(公會,所選公會的工作人員等)展示給視圖。它需要一點點設置,但它會減輕你的綁定困境一噸。 – Tim

0

我沒有測試你的代碼,但在Guild模型,返回new ObservableCollection(可以在您的擴展方法看到的)可能打破你的綁定,我建議一個小的重新設計,以便綁定的觀點是始終與ObservableCollection的原始實例關聯。儘管我沒有使用你的框架,當我實現MVVM模式時,我總是確保我的ViewModel的observables保持相同的實例,並且我使用Clear方法來替換我的OnModelChanged方法中的內容,即方式您不必處理通知需要通知這種變化的觀點,並且由您的ObservableCollection處理。

+0

那肯定是理想的。 LINQ過濾返回一個IEnumerable,所以這就是擴展方法的用處。但我相信你是正確的,並且返回一個新對象就是在這裏搞砸了什麼。你知道我怎麼能夠在屬性中使用某種過濾器而不破壞綁定? –

+0

我建議你將這兩個可觀察的集合分開:擁有一個始終擁有相關工作人員的集合(需要時填充它 - > OnModelChanged),視圖將綁定到該集合。這個集合將只實例化一次(如果你沒有綁定到'舊'集合,不妨將它作爲一個常規列表) –