2017-09-18 32 views
-1

語境:WPF MVVM視圖時的ViewModels性能

我有一個單獨的細節的樹狀視圖通過PRISM庫注射時,我對我的樹型視圖的一個點擊(我可以更新我的項目的所有屬性它)。我的所有物品都有啓用屬性。

問題:

  • 當我更新我的編程產權的ViewModels,我的對象被更新。如果我點擊其他treeviewitem並返回到第一個,我會看到該屬性已更新。
  • 當我啓用/禁用使用我的詳細信息視圖的項目(前景變灰,屬性發生變化)時,所有更新都很好
  • 但在我的情況下,當我嘗試通過由一個contextMenu它不會觸發視圖和所有更新...但我的viewmodel屬性更新...

我該怎麼去錯了?

我在我的treeview中使用ObservableCollection,也許我需要更改我的集合的類型?

我有我的BaseViewModel誰實現NotifyPropertyChanged

public abstract class NotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(Expression<Func<object>> propertyExpression) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(GetPropertyName(propertyExpression))); 
    } 

    private string GetPropertyName(Expression<Func<object>> propertyExpression) 
    { 
     var unaryExpression = propertyExpression.Body as UnaryExpression; 
     var memberExpression = unaryExpression == null ? (MemberExpression)propertyExpression.Body : (MemberExpression)unaryExpression.Operand; 
     var propertyName = memberExpression.Member.Name; 
     return propertyName; 
    } 
} 

因此,我調用屬性變化的方法,但爲什麼我的看法是沒有,那麼更新?

[DefaultValue(true)] 
    [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 
    public bool Enabled 
    { 
     get 
     { 
      return Model.Enabled; 
     } 
     set 
     { 
      if (value != Model.Enabled) 
      { 
       Model.Enabled = value; 
       OnPropertyChanged(() => Model.Enabled); 
      } 

     } 
    } 

這是我的看法的代碼(命令)

<MenuItem Header="Enable/Disable this equipment" Command="{Binding PlacementTarget.Tag.DataContext.ToogleEquipmentCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" 
          CommandParameter="{Binding}" InputGestureText="CTRL+D"/> 

這裏是我認爲的代碼(分層數據模板從我的樹視圖)

<!-- ModuleItems > IP/Name --> 
      <HierarchicalDataTemplate DataType="{x:Type siemens:ModuleItemSiemensViewModel}" > 
       <StackPanel Orientation="Horizontal"> 
        <TextBlock Name="ItemIp" 
         Text="{Binding Path=Ip}" ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"> 
         <TextBlock.Style> 
          <Style TargetType="TextBlock"> 
           <Style.Triggers> 
            <DataTrigger Binding="{Binding Enabled}" Value="False"> 
             <Setter Property="Background" Value="LightGray"/> 
             <Setter Property="Foreground" Value="Black"/> 
            </DataTrigger> 
            <DataTrigger Binding="{Binding Enabled}" Value="True"> 
             <Setter Property="Background" Value="Transparent"/> 
             <Setter Property="Foreground" Value="Red"/> 
            </DataTrigger> 
           </Style.Triggers> 
          </Style> 
         </TextBlock.Style> 
        </TextBlock> 
        <TextBlock Text="/" ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"> 
         <TextBlock.Style> 
          <Style TargetType="TextBlock"> 
           <Style.Triggers> 
            <DataTrigger Binding="{Binding Enabled}" Value="False"> 
             <Setter Property="Background" Value="LightGray"/> 
            </DataTrigger> 
            <DataTrigger Binding="{Binding Enabled}" Value="True"> 
             <Setter Property="Background" Value="Transparent"/> 
            </DataTrigger> 
           </Style.Triggers> 
          </Style> 
         </TextBlock.Style> 
        </TextBlock> 
        <TextBlock Name="ItemName" ContextMenu="{StaticResource ContextMenuEquipment}" Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" 
         Text="{Binding Path=Name}"> 
         <TextBlock.Style> 
          <Style TargetType="TextBlock"> 
           <Style.Triggers> 
            <DataTrigger Binding="{Binding Enabled}" Value="False"> 
             <Setter Property="Background" Value="LightGray"/> 
             <Setter Property="Foreground" Value="Black"/> 
            </DataTrigger> 
            <DataTrigger Binding="{Binding Enabled}" Value="True"> 
             <Setter Property="Background" Value="Transparent"/> 
             <Setter Property="Foreground" Value="Blue"/> 
            </DataTrigger> 
           </Style.Triggers> 
          </Style> 
         </TextBlock.Style> 
        </TextBlock> 
       </StackPanel> 
      </HierarchicalDataTemplate> 

編輯:

這是我的viewmodel和模型: 我真正的問題是當我更新一個項目(與我的屬性啓用)它更新項目,但我的列表(ModuleItems)沒有更新,我需要做什麼來正確實現MVVM,並使我的領域自動更新?

public class ModuleParamSiemensViewModel : ModuleParamBaseViewModel 
{ 
    #region Attributes 

    private ObservableCollection<ModuleItemSiemensViewModel> _moduleItems; 
    private ModuleParamSiemens _model; 
    private string _moduleType; 
    #endregion 

    #region Constructor 

    public ModuleParamSiemensViewModel(ModuleParamSiemens moduleParam) : base(moduleParam) 
    { 
     this.Model = moduleParam; 
     this.ModuleType = "Siemens"; 
     ModuleItems = new ObservableCollection<ModuleItemSiemensViewModel>(); 
     Initialize(); 
    } 

    #endregion 

    #region Properties 
    public new ModuleParamSiemens Model 
    { 
     get 
     { 
      return _model; 
     } 

     set 
     { 
      if (value != _model) 
      { 
       _model = value; 
       OnPropertyChanged(() => Model); 
      } 

     } 
    } 
    public new ObservableCollection<ModuleItemSiemensViewModel> ModuleItems 
    { 
     get 
     { 
      return _moduleItems; 
     } 
     set 
     { 
      this._moduleItems = value; 
      OnPropertyChanged(() => ModuleItems); 
     } 
    } 
    public override string ModuleType 
    { 
     get 
     { 
      return _moduleType; 
     } 

     set 
     { 
      this._moduleType = value; 
      OnPropertyChanged(() => ModuleType); 
     } 
    } 
    #endregion 

    #region Public Methods 

    public void Initialize() 
    { 
     foreach (ModuleItemSiemens item in this.Model.ModuleItems) 
     { 
      Add(new ModuleItemSiemensViewModel(item)); 
     } 
    } 

    public void Add(ModuleItemSiemensViewModel item) 
    { 
     ModuleItems.Add(item); 

    } 


    #endregion 
} 

型號:

public class ModuleParamSiemens : ModuleParam 
{ 
    public new ObservableCollection<ModuleItemSiemens> ModuleItems { get; set; } 

    public ModuleParamSiemens() 
    { 
     ModuleItems = new ObservableCollection<ModuleItemSiemens>(); 
    } 


} 

編輯2:

添加ItemSiemensViewModel

public class ItemSiemensViewModel : ItemBaseViewModel 
{ 
    #region Attributes 
    private ItemSiemens _model; 
    #endregion 

    #region Constructor 

    public ItemSiemensViewModel(ItemSiemens item) 
    { 
     this.Model = item; 
    } 

    #endregion 

    #region Properties 

    public new ItemSiemens Model 
    { 
     get 
     { 
      return _model; 
     } 

     set 
     { 
      if (value != _model) 
      { 
       _model = value; 
       OnPropertyChanged(() => Model); 
      } 

     } 
    } 
    public new OPCInfo Opc 
    { 
     get 
     { 
      return Model.Opc; 
     } 
     set 
     { 

      if (value != Model.Opc) 
      { 
       Model.Opc = value; 
       OnPropertyChanged(() => Model.Opc); 
      } 

     } 
    } 

    public ProtocolInfoSiemens Protocol 
    { 
     get 
     { 
      return Model.Protocol; 
     } 
     set 
     { 

      if (value != Model.Protocol) 
      { 
       Model.Protocol = value; 
       OnPropertyChanged(() => Model.Protocol); 
      } 

     } 
    } 

    #endregion 

    #region Public Methods 

    #endregion 
} 

ItemSiemens:

public class ItemSiemens : Item 
{ 
    public ProtocolInfoSiemens Protocol { get; set; } 


} 

ItemBaseViewModel

public abstract class ItemBaseViewModel : BaseViewModel 
{ 
    public OPCInfoBaseViewModel Opc { get; set; } 

    public ItemBaseViewModel() 
    { 
    } 
} 

項目

public abstract class Item 
{ 

    public OPCInfo Opc { get; set; } 


} 
+0

沒有一個好的[mcve]可以可靠地重現問題,所以不可能知道所有可能的錯誤。也就是說,使用'Expression'來獲取屬性名稱是毫無意義的。你應該使用'nameof'運算符或([IMHO好得多]'[CallerMemberName]'屬性來獲取被更新的屬性的名稱。不是說這裏有任何跡象表明這會改變你的問題,但你應該修正它(也許它會解決你的問題)。 –

回答

0

我找到了答案。

我的綁定是正確的(或至少它的工作原理)

的問題是,我用的ObservableCollection集合,當一個產品更新這個集合中它甚至沒有發出一個事件地說,事情已經改變(其確實爲添加和刪除項目)

所以我已經實現了我自己的ItemsChangeObservableCollection(你可以看看這個答案:https://stackoverflow.com/a/33866549/8237280

現在所有我在我所有的應用程序問題都解決了!

0

你對你的ModuleItemSiemensViewModel發送INotifyPropertyChanged的該屬性Model.Enabled。這並沒有太大的意義,因爲沒有人在虛擬機(ModuleItemSiemensViewModel)上列出此更改。 INPC接口不允許這種更新。每個控件監聽與其綁定屬性相同的對象。這意味着您只能發送屬性PropertyChanged,該屬性位於聲明該接口的同一個類/實例中。

您必須將NotifyPropertyChanged移動到「模型」實例並在那裏調用它。像這樣:

[DefaultValue(true)] 
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 
public bool Enabled 
{ 
    get 
    { 
     return Model.Enabled; 
    } 
    set 
    { 
     if (value != Model.Enabled) 
     { 
      Model.Enabled = value; 
      Model.OnPropertyChanged(() => Enabled); 
     } 

    } 
} 
+0

'OnPropertyChanged()'所做的就是使用傳遞表達式中屬性的名稱。 「Model.Enabled」屬性的名稱與「Enabled」屬性的名稱相同,因此儘管我同意實現不正確,但似乎並不能解釋缺少更新。 –

+0

感謝您的評論,我不明白爲什麼我的實施不正確?當我更改ViewModel時,我需要反映我的模型中的更改嗎?在這裏,我發現我的錯誤,當我使用集合時,viewmodel類型與模型類型不同,我不知道如何在我的模型中反映它(我從我的視圖更新視圖模型,但我不知道如何做到這一點...)我會更新我的帖子與集合。 – Karakayn

+0

@PeterDuniho是的,你是對的。發送的「名稱」是相同的,但是在OnPropertyChange方法被調用的對象上非常重要。但你對OP的帖子的評論也是正確的。Xaml代碼沒有意義,因爲所有綁定都指向:他沒有包含在代碼示例中的:sIiemens:ModuleItemSiemensViewModel.Enabled 。 – JPVenson