Jonny
Jonny

Reputation: 2519

Conditional formatting based on two view models

Is there a way to apply a style based on conditions in multiple view models.

For example, I have a list of items that can be conditionally formatted at the item level, but I also want to be able to turn the formatting on or off at the whole list level:

Main view model

class ParentViewModel : ViewModelBase
{
    public ParentViewModel ()
    {
        Items = new ObservableCollection<ChildViewModel> {
            new ChildViewModel{ Display = "I am red", Format=1 },
            new ChildViewModel{ Display = "I am red", Format=1 },
            new ChildViewModel{ Display = "I am blue", Format=2 },
            new ChildViewModel{ Display = "I am blue", Format=2 },
        };
        ShowFormatting = false;
    }

    public ObservableCollection<ChildViewModel> Items { get ... }

    // I would like to use this property to turn formatting off for the whole list
    public bool ShowFormatting { get ... } 
}

Contains many sub-view models

class ChildViewModel: ViewModelBase
{
    public string Display { get ... }

    public int Format { get ... }
}

Displayed in a window

<Window ...>
  <Grid>

    <Grid.Resources>
      <Style x:Key="MyStyle" TargetType="{x:Type TextBlock}">
        <Style.Triggers>
<!-- How can I access ShowFormatting from the main view model here? --> 
          <DataTrigger Binding="{Binding Format}" Value="1">
            <Setter Property="Foreground" Value="Red"></Setter>
          </DataTrigger>
          <DataTrigger Binding="{Binding Format}" Value="2">
            <Setter Property="Foreground" Value="Blue"></Setter>
          </DataTrigger>
        </Style.Triggers>
      </Style>
    </Grid.Resources>

    <ListView ItemsSource="{Binding Items}">
      <ListView.View>
        <GridView>
          <GridViewColumn>
            <GridViewColumn.CellTemplate>
<!-- Or can I apply the conditional formatting here? -->
              <DataTemplate DataType="{x:Type local:ChildViewModel}">
                <TextBlock Text="{Binding Display}" Style="{StaticResource MyStyle}"></TextBlock>
              </DataTemplate>
            </GridViewColumn.CellTemplate>
          </GridViewColumn>
        </GridView>
      </ListView.View>
    </ListView>

  </Grid>
</Window>

The problem that I see is that the style is targeting a ChildViewModel and the ShowFormatting property is not accessible. The best solution that I can come up with is to have a backlink from the ChildViewModel to the ParentViewModel, but having backlinks generally seems bad to me. Is there a better way to do this?

Upvotes: 1

Views: 78

Answers (2)

Phillip Ngan
Phillip Ngan

Reputation: 16106

One way of doing this is to have two different child view models, ChildViewModel1 and ChildViewModel2. The ParentViewModel would populate Items with one or both of these types: you could use an ObservableCollection<object> if you wish. When you define a datatemplate for ChildViewModel1 and another for ChildViewModel2, then depending which child view model was resident in the ObservableCollection, you'd get the respective rendering.

Upvotes: 1

mbger
mbger

Reputation: 114

How about using a RelativeSource for your Binding? (I'm assuming here that the DataContext of your Window is of Type ParentViewModel)

<Grid.Resources>
  <Style x:Key="MyStyle" TargetType="{x:Type TextBlock}">
    <Style.Triggers>
      <DataTrigger Binding="{Binding Format}" Value="1">
        <Setter Property="Foreground" Value="Red"></Setter>
      </DataTrigger>
      <DataTrigger Binding="{Binding Format}" Value="2">
        <Setter Property="Foreground" Value="Blue"></Setter>
      </DataTrigger>
      <DataTrigger Binding="{Binding Path=DataContext.ShowFormatting, RelativeSource={RelativeSource AncestorType=Window}}" Value="SomeValue">
        <Setter Property="SomeProperty" Value="SetToThisValue"></Setter>
      </DataTrigger>
    </Style.Triggers>
  </Style>
</Grid.Resources>

Upvotes: 1

Related Questions