当viewmodels属性为时,WPF MVVM视图未更新

时间:2017-09-18 14:21:16

标签: c# wpf mvvm data-binding

背景:

当我点击我的一个treeviewitem时,我有一个树形视图,其中有一个单独的详细信息视图,由PRISM库注入(我可以使用它更新项目的所有属性)。我的所有商品都有已启用属性。

问题:

  • 当我以编程方式更新我的viewmodels属性时,我的对象会更新。如果我点击另一个treeviewitem然后回到第一个,我看到该属性已更新。
  • 当我使用详细信息视图启用/禁用项目时,所有更新都很好(前景变灰并且属性发生变化)
  • 但在我的情况下,当我尝试通过contextMenu触发的命令更新它时,它不会触发视图和所有更新......但我的viewmodel属性已更新......

我出错了什么?

我在树视图中使用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和模型中的代码: 我真正的问题是当我更新一个Item(启用了我的属性)时它会更新项目,但是我的列表(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; }


}

2 个答案:

答案 0 :(得分:0)

您正在ModuleItemSiemensViewModel上为属性Model.Enabled发送INotifyPropertyChanged。这没有多大意义,因为没有人在VM(ModuleItemSiemensViewModel)上监听此更改。 INPC接口不允许这种更新。每个控件都侦听它绑定属性的同一对象。这意味着您只能为声明接口的同一类/实例中的属性发送PropertyChanged。

您必须将NotifyPropertyChanged移至&#34;模型&#34;实例并在此处调用它:

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

    }
}

答案 1 :(得分:0)

我找到了答案。

我的绑定是正确的(或者至少它是有效的)

问题是我使用了ObservableCollection集合,当一个项目在这个集合中更新时,它甚至不会触发一个事件来说某些东西已经改变(它用于添加和删除项目)

所以我实现了自己的 ItemsChangeObservableCollection (你可以看看这个答案:https://stackoverflow.com/a/33866549/8237280

现在我所有应用中的所有问题都解决了!