Dwayne Dibbley
Dwayne Dibbley

Reputation: 355

MVVM with dynamic child's possible?

I have a simple form that as a dynamic set of Parent Labels ( could be 1 -10 labels ) that is updated on a timer, if all is good these labels stay green like:

enter image description here

If however one of the parents has a status change, i am trying to then display the offending child or childs and result in somewthing like this:

enter image description here

then once status returns to normal revert back to the original layout ( as above )

so currently i have a view like this :

<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>

a viewmodel populating the view with a simple add:

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

and the basic model :

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));
     } 
  } 

would creating a child view somehow on the parent status change be a possible way? or can i create all the parent and childs and toggle there visibility when there status changes?

Thanks

Upvotes: 0

Views: 229

Answers (1)

jayars
jayars

Reputation: 1357

Did you mean something like this?

enter image description here

// 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>

Upvotes: 1

Related Questions