首页 > 解决方案 > 具有动态孩子的 MVVM 可能吗?

问题描述

我有一个简单的表单,它作为一组动态的父标签(可能是 1 -10 个标签)在计时器上更新,如果一切正常,这些标签保持绿色,如:

在此处输入图像描述

但是,如果其中一位父母的状态发生了变化,我会尝试显示有问题的孩子或孩子,并导致以下结果:

在此处输入图像描述

然后一旦状态恢复正常恢复到原始布局(如上)

所以目前我有这样的看法:

<Grid> 
    <ItemsControl ItemsSource = "{Binding Path = CIs}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                <Grid>
                    <Label Content = "{Binding Path = NodeName, Mode = OneWay}" 
                        Background = "{Binding Path = NodeStatus, Mode = OneWay}"
                        Tag="{Binding Path = Nodeid, Mode = OneWay}"
                        Foreground="White"
                        FontFamily="Arial Black"
                        HorizontalContentAlignment="Center"                            
                        BorderBrush="Black" 
                        BorderThickness="1,1,1,1"/>                        
                </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
    </ItemsControl> 
</Grid>

使用简单的添加填充视图的视图模型:

if (Node.level == 3)
{
    CIs.Add(new CI { NodeName = Node.name, NodeStatus = Node.status, Nodeid = Node.id });
}

和基本模型:

public class CIModel {}

public class CI : INotifyPropertyChanged {
  private string nodeName;
  private string nodeStatus;
  private string nodeid;

    public string NodeName { 
     get { 
        return nodeName; 
     }

     set { 
        if (nodeName != value) { 
           nodeName = value; 
           RaisePropertyChanged("NodeName"); 
        } 
     } 
  }

 public string Nodeid
    {
        get
        {
            return nodeid;
        }

        set
        {
            if (nodeid != value)
            {
                nodeid = value;
                RaisePropertyChanged("Nodeid");
            }
        }
    }

    public string NodeStatus
    {
        get
        {
            return nodeStatus;
        }

        set
        {
            if (nodeStatus != value)
            {
                nodeStatus = value;
                RaisePropertyChanged("NodeStatus");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

  private void RaisePropertyChanged(string property) {
     if (PropertyChanged != null) {
        PropertyChanged(this, new PropertyChangedEventArgs(property));
     } 
  } 

以某种方式在父状态更改上创建子视图是一种可能的方式吗?或者我可以创建所有的父母和孩子并在状态发生变化时切换那里的可见性?

谢谢

标签: c#wpfmvvm

解决方案


你的意思是这样的吗?

在此处输入图像描述

// Put this in MainWindow()
public MainWindow()
{
    InitializeComponent();
    new Demo(this);
}
// Demo code
public class Demo
{
    public Demo(FrameworkElement view)
    {
        View = view;
        View.DataContext = this;
        StartDemo();
    }

    private FrameworkElement View { get; }

    public ObservableCollection<Parent> Parents { get; } = new ObservableCollection<Parent>();

    public async void StartDemo()
    {
        var delay = 500;
        foreach (var index in Enumerable.Range(0, 5))
        {
            var item = new Parent { Name = $"Parent {index + 1}" };
            Parents.Add(item);
            await Task.Delay(delay);
        }

        // Add errors
        for (var i = 0; i < 3; i++)
        {
            Parents[1].Errors.Add(new Child { Name = $"Child {i + 1}" });
            await Task.Delay(delay);
        }

        // Remove errors
        while (Parents[1].Errors.Any())
        {
            Parents[1].Errors.RemoveAt(Parents[1].Errors.Count - 1);
            await Task.Delay(delay);
        }

        // Remove parents
        while (Parents.Any())
        {
            Parents.RemoveAt(Parents.Count-1);
            await Task.Delay(delay);
        }
    }
}

/// <summary>
/// Child (error item)
/// </summary>
public class Child
{
    public string Name { get; set; }
}

/// <summary>
/// Parent
/// </summary>
public class Parent : INotifyPropertyChanged
{
    public Parent()
    {
        System.Collections.Specialized.CollectionChangedEventManager.AddHandler(Errors,
            delegate
            {
                OnPropertyChanged(nameof(Status));
            });
    }

    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged(); }
    }

    public string Status { get { return Errors.Any() ? "ERROR" : "OK"; } }

    /// <summary>
    /// Children/errors
    /// </summary>
    public ObservableCollection<Child> Errors { get; } = new ObservableCollection<Child>();

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
<!-- Xaml -->
<ItemsControl ItemsSource="{Binding Path=Parents}"
              Grid.IsSharedSizeScope="True">
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="{x:Type local:Parent}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" SharedSizeGroup="ParentColumn" />
                    <ColumnDefinition  />
                </Grid.ColumnDefinitions>

                <!-- Parent label -->
                <Label Content="{Binding Path=Name}"
                       x:Name="Label"/>

                <!-- Errors -->
                <ItemsControl ItemsSource="{Binding Path=Errors}"
                              Grid.Column="1">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate DataType="{x:Type local:Child}">
                            <Label Content="{Binding Path=Name}"
                                   Background="Red" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </Grid>
            <DataTemplate.Triggers>
                <!-- Parent is ok -->
                <DataTrigger Binding="{Binding Path=Status}"
                             Value="OK">
                    <Setter TargetName="Label" Property="Background" Value="Green" />
                </DataTrigger>


                <!-- Parent is not ok -->
                <DataTrigger Binding="{Binding Path=Status}"
                             Value="ERROR">
                    <Setter TargetName="Label" Property="Background" Value="Red" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

推荐阅读