2017-09-18 220 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