Create Dynamic Measures in Power BI Using a Role Toggle

 By Akash Rajendran Nair

Introduction

Have you ever wondered if you could display two entirely different calculations in the same visual dynamically by selecting a value in a slicer? Imagine a scenario where you’re building a demographics dashboard for club enrolment. You want to display both Student and Mentor demographic breakdowns within the same visuals, and switch between them effortlessly with a single slicer selection.

In this blog, I’ll walk you through exactly how to make that happen in Power BI. We’ll build a dynamic measure using a disconnected table that lets users toggle views, keeping your report streamlined and intuitive.


Understanding the Data Model

The model is centred around a fact table called Club Membership, which captures each student’s enrolment into various clubs. This table connects to three key dimension tables:

  • Student Details – contains demographic info for each student
  • Mentor Details – holds demographic info for each mentor
  • Clubs – a simple lookup of club names

Demographics are provided via three shared lookup tables: Age, Gender, and Ethnicity, which are:

  • Actively related to Student Details, enabling straightforward filtering of student data
  • Inactively related to Mentor Details, requiring USERELATIONSHIP to activate when needed

Finally, we add a disconnected Role Selector table with two values: “Student” and “Mentor.” This table doesn’t participate in any relationships—it simply powers a slicer that drives our dynamic logic.



Step 1: Create a Role Selector Table

To create a dynamic toggle between Student and Mentor views, we need a disconnected table, a table with no physical relationships to the rest of the data model. Its sole purpose is to act as a driver for dynamic logic in our measures.

Here’s how you can set it up:

 1. Open Power BI Desktop

Launch your report and go to the Modelling tab.

 2. Use New Table to Create the Role Selector

Click on “New Table”, and enter the following DAX:

RoleSelector = DATATABLE("Role", STRING, {{"Student"}, {"Mentor"}})

This creates a simple two-row table with just one column Role containing the values Student and Mentor.


3. Add the Role Selector as a Slicer

Drag the Role column into a slicer visual on your report canvas. You can format it as a dropdown or buttons, depending on your design preference and set it to single select.



📌 Important: Do not connect this table to any other table in the data model. It remains intentionally disconnected so that we can use it purely in our DAX logic without introducing unwanted filters.

This step sets the foundation for building clean, controlled dynamic measures based on user selection. It keeps your logic flexible while ensuring your data relationships stay optimised.


Step 2: Create a Dynamic Measure for Counts

We now write a measure that evaluates different expressions based on which role is selected in the slicer:

Dynamic Count = 
VAR SelectedRole = SELECTEDVALUE(RoleSelector[Role])
RETURN
SWITCH(
    SelectedRole,
    "Student", DISTINCTCOUNT('Club Membership'[Student ID]),
    "Mentor", DISTINCTCOUNT('Club Membership'[Mentor ID]),
    BLANK()
)

Line-by-Line Breakdown:

VAR SelectedRole = SELECTEDVALUE(RoleSelector[Role])
  • This line captures the role selected by the user from the RoleSelector slicer.
  • SELECTEDVALUE() Returns the single selected value—either "Student" or "Mentor".
  • If nothing or multiple values are selected, it returns blank, which is handled later in the logic.

RETURN
SWITCH(
    SelectedRole,
  • SWITCH() is used to return different logic based on the value of SelectedRole.
  • It's similar to an IF...ELSE IF... structure, but cleaner and more readable for this use case.

"Student", DISTINCTCOUNT('Club Membership'[Student ID]),

  • If "Student" is selected, the measure returns a distinct count of Student ID from the Club Membership table.
  • This gives you the number of unique students enrolled in clubs.
 
"Mentor", DISTINCTCOUNT('Club Membership'[Mentor ID]),

  • If "Mentor" is selected, it instead returns the distinct count of Mentor ID from the same table.
  • So, same visual, different metric—switching based on role

BLANK()

  • This acts as a fallback—if nothing is selected (or an unexpected value somehow ends up in the slicer), the measure returns blank.
  • It keeps things tidy and avoids misleading results.

Click on RoleSelector table and select table Tools, click new measure and paste the DAX above to create the measure.


Drop this into a card, bar chart, or KPI it will respond immediately to the slicer toggle.









Step 3: Build Role-Aware Demographic Visuals

Let’s take a deeper look at how to build visuals that break down counts by demographics like Ethnicity and respond correctly for both Students and Mentors:

Dynamic Count by Ethnicity = 

VAR SelectedRole = SELECTEDVALUE(RoleSelector[Role])

RETURN

SWITCH(

    SelectedRole,

    "Student",

        CALCULATE(

            DISTINCTCOUNT('Club Membership'[Student ID]),

            VALUES('Ethnicity'[Ethnicity])

        ),

    "Mentor",

        CALCULATE(

            DISTINCTCOUNT('Club Membership'[Mentor ID]),

            USERELATIONSHIP('Ethnicity'[Ethnicity], 'Mentor Details'[Ethnicity]),

            VALUES('Ethnicity'[Ethnicity])

        )

)


Create the measure as discussed earlier and use a tree map visual from the visualisation pane, drag Ethnicity from the Ethnicity lookup table into the Category and the measure into the values.



What Each Piece Does:

  • VAR SelectedRole = SELECTEDVALUE(RoleSelector[Role])
    Captures the user’s selection from the disconnected slicer, either “Student” or “Mentor”.
  • SWITCH(...)
    Branches logic based on which role was selected.
  • "Student" Block:
    Filters Club Membership by each ethnicity in the visual (i.e., row context from the chart’s axis), and counts distinct Student IDs accordingly.
  • "Mentor" Block:
    Activates the inactive relationship between Ethnicity and Mentor Details using USERELATIONSHIP, applies the same ethnicity filtering, and returns the distinct count of Mentor IDs.
  • VALUES('Ethnicity'[Ethnicity])
    Ensures row-level calculation per ethnicity shown in the chart—even in the absence of a slicer—by respecting the row context of the visual axis.
Why VALUES('Ethnicity'[Ethnicity]) is Needed

Even though there’s no slicer, the Ethnicity field is used in the visual axis or legend (like in a bar chart or pie chart). That visual inherently produces multiple filter contexts, one for each Ethnicity group.

When you write:

VALUES('Ethnicity'[Ethnicity])

You’re telling Power BI:

“For each individual Ethnicity value in this visual, evaluate the distinct count of IDs within that Ethnicity context.”

Without that line, you’d get a single total for the entire visual, not a breakdown by group.

However, here in this case. Because we are using the Ethnicity field directly from the lookup table in the visual’s axis, and the lookup table is connected in the model (even if only via inactive relationship for mentors), Power BI will still generate a filter context for each ethnicity value automatically. That row context is applied implicitly by the visual, meaning your measure can still iterate per group even without VALUES('Ethnicity'[Ethnicity]).

So yes, functionally it should still work without explicitly adding the VALUES(...) line. Including it just makes that intent explicit; some developers prefer to define the row context within CALCULATE to avoid ambiguity, especially when more complex filters or layered conditions are involved. I am one of them.


Repeating the Pattern for Gender and Age

Dynamic Count by Gender:

Dynamic Count by Gender = 
VAR SelectedRole = SELECTEDVALUE(RoleSelector[Role])
RETURN
SWITCH(
    SelectedRole,
    "Student",
        CALCULATE(
            DISTINCTCOUNT('Club Membership'[Student ID]),
            VALUES('Gender'[Gender])
        ),
    "Mentor",
        CALCULATE(
            DISTINCTCOUNT('Club Membership'[Mentor ID]),
            USERELATIONSHIP('Gender'[Gender], 'Mentor Details'[Gender]),
            VALUES('Gender'[Gender])
        )
)


Dynamic Count by Age:

Dynamic Count by Age = 
VAR SelectedRole = SELECTEDVALUE(RoleSelector[Role])
RETURN
SWITCH(
    SelectedRole,
    "Student",
        CALCULATE(
            DISTINCTCOUNT('Club Membership'[Student ID]),
            VALUES('Age'[Age])
        ),
    "Mentor",
        CALCULATE(
            DISTINCTCOUNT('Club Membership'[Mentor ID]),
            USERELATIONSHIP('Age'[Age], 'Mentor Details'[Age]),
            VALUES('Age'[Age])
        )
)

Use your choice of visuals for displaying these measures.

Step 4: Testing It Out.

Try toggling between the two, Student and Mentor, in the Role Selector and see the magic work.





Now, what if you want to have a multiselect slicer and want to show both combined? Modify your measure as below.

Dynamic Count by Ethnicity = 
VAR SelectedRoles = VALUES(RoleSelector[Role])
VAR RoleCount = COUNTROWS(SelectedRoles)
RETURN
SWITCH(
    TRUE(),
    
    // Student only
    RoleCount = 1 && SELECTEDVALUE(RoleSelector[Role]) = "Student",
        CALCULATE(
            DISTINCTCOUNT('Club Membership'[Student ID]),
            VALUES('Ethnicity'[Ethnicity])
        ),
    
    // Mentor only
    RoleCount = 1 && SELECTEDVALUE(RoleSelector[Role]) = "Mentor",
        CALCULATE(
            DISTINCTCOUNT('Club Membership'[Mentor ID]),
            USERELATIONSHIP('Ethnicity'[Ethnicity], 'Mentor Details'[Ethnicity]),
            VALUES('Ethnicity'[Ethnicity])
        ),
    
    // Both selected – combined count
    RoleCount = 2,
        CALCULATE(
            DISTINCTCOUNT('Club Membership'[Student ID]),
            VALUES('Ethnicity'[Ethnicity])
        ) +
        CALCULATE(
            DISTINCTCOUNT('Club Membership'[Mentor ID]),
            USERELATIONSHIP('Ethnicity'[Ethnicity], 'Mentor Details'[Ethnicity]),
            VALUES('Ethnicity'[Ethnicity])
        ),

    BLANK()
)

Why SWITCH(TRUE())?

Normally, SWITCH() works like this:

SWITCH( <expression>, <value1>, <result1>, <value2>, <result2>, ... )

But that only works for matching exact values.

When you want to evaluate logical conditions (like RoleCount = 2 or a combo of conditions), SWITCH(TRUE(), ...) flips the pattern:

  • It says: “Find the first expression that evaluates to TRUE, and return its corresponding result.”
  • So it becomes like a cleaner, more readable IF...ELSE IF...ELSE cascade.
Let's pick this up in detail on another blog.

However, let's take this dynamic measure up a notch. What if we are returning a non-scalar value like a table, for example, if we are combining the mentor ID and student ID list and taking a count? This is not the perfect example or use case, but while we are here, let's address it. Switch would throw an error, so will If as you will have to return Blank() if the condition is false, and the union operation would have an issue. We use FILTER in this scenario.

Unified Dynamic Count = 
VAR SelectedRoles = VALUES(RoleSelector[Role])

VAR StudentIDs = 
    FILTER(
        SELECTCOLUMNS(
            'Club Membership',
            "ID", 'Club Membership'[Student ID]
        ),
        "Student" IN SelectedRoles
            && NOT(ISBLANK([ID]))
    )

VAR MentorIDs = 
    FILTER(
        SELECTCOLUMNS(
            'Club Membership',
            "ID", 'Club Membership'[Mentor ID]
        ),
        "Mentor" IN SelectedRoles
            && NOT(ISBLANK([ID]))
    )

VAR CombinedIDs = 
    UNION(StudentIDs, MentorIDs)

RETURN
    COUNTROWS( DISTINCT(CombinedIDs) )


Why This Works:

  • FILTER(...) ensures each variable always returns a valid table, even if zero rows match.
  • We wrap with DISTINCT(...) in the final count, just in case any IDs overlap.
  • We avoid IF() altogether, which simplifies logic and eliminates branching errors.

This gives you a clean, bulletproof way to dynamically merge distinct IDs and count them, based on any combination selected in the slicer.


Wrapping Up

Dynamic measures, when paired with a disconnected slicer, open up powerful and user-friendly ways to present data in Power BI. In this blog, we explored how to toggle between entirely different entities, Student and Mentor, within the same visuals, and even support combined views with unified logic. We walked through constructing a flexible DAX using SWITCH(TRUE()) for scalar values and transitioning into table logic with UNION() and FILTER() for more advanced cases.

Whether you’re working with demographic breakdowns or building adaptable dashboards for multi-role analysis, this pattern helps you stay lean with your visuals while creating a seamless user experience.


Comments

Popular Posts