The KanbanBoard shows a table's records as cards in lanes side by side, one lane for each value of a column: an opportunity's rating, a task's status reason, the account a record belongs to. Each lane is read on its own through a view, a page at a time, with the same query and security as a grid, and shows its count and totals in its header. In an Editable board a card moves to another lane by dragging it, from the keyboard or through its Move to menu, and the move is saved at once; with a rank column, cards are put in order by hand as well. It is drawn in your Fluent UI theme.
The open opportunities of the demo's sample data, through Dataverse's own Open Opportunities view: a lane for each rating, with the lane's count and estimated revenue in its header. Lanes leaves out the lane of opportunities without a rating. The Kanban Board demo shows the same board with moves, views, filters and saved views.
React example
Blazor example
React TypeScript
Razor
LaneColumn (React: laneColumn) names the column of the view's table the lanes come from. A choice gives a lane for each of its options, a status reason one for each reason, a Yes/No column one for each of its two values, a lookup one for each record it names, and a date the lanes of when it falls, as Date lanes below describes. Any other column shows a message in place of the board. The lanes follow Dataverse's order and leave out hidden options, and an option's color shows as a dot beside its lane's name. A column a record may leave empty adds a last lane, (No value), for the cards without one.
Lanes (React: lanes) chooses which lanes are shown, and in what order, by key: an option's value, 1 and 0 for Yes and No, a record's id, or an empty key for the lane without a value. A lookup's lanes are the records the view's cards name, read in one grouped query; name them in LookupLanes (React: lookupLanes) instead to fix them and their order, a lane with no cards included.
React
Blazor
{/* Tasks from Not Started to Completed, without Deferred and Canceled. */}
<KanbanBoard defaultViewId={allTasksViewId} laneColumn="statuscode" lanes={['2', '3', '4', '5']} />
{/* A lane for each key account, in the order given. */}
<KanbanBoard defaultViewId={openOpportunitiesViewId} laneColumn="parentaccountid" lookupLanes={keyAccounts} />
<!-- Tasks from Not Started to Completed, without Deferred and Canceled. -->
<KanbanBoard DefaultViewId="allTasksViewId"
LaneColumn="statuscode"
Lanes="@(["2", "3", "4", "5"])" />
<!-- A lane for each key account, in the order given. -->
<KanbanBoard DefaultViewId="openOpportunitiesViewId"
LaneColumn="parentaccountid"
LookupLanes="@keyAccounts" />
A lane with no cards is shown, so a card can be dropped in it. Set ShowEmptyLanes (React: showEmptyLanes) to false to hide it once its count is known. Users can collapse a lane to a narrow strip of its name and count with the button in its header, and hide lanes from the toolbar's Filter menu, which lists them under Show.
A date column gives lanes of when its date falls, counted in days of the user's own time zone, with the week starting on their language's first day, and worked out again each time the board reads its lanes. With none named, they are Overdue, Today, Tomorrow, This week — from the day after tomorrow to the week's end, so empty near the end of a week — Next week, Later and No date.
DateLanes (React: dateLanes) names the lanes instead, in date order. A preset sets a lane up from the current date: Yesterday, Today and Tomorrow; LastWeek, ThisWeek and NextWeek; LastMonth, ThisMonth and NextMonth; the calendar's quarters and years, such as ThisCalendarQuarter and NextCalendarYear, and the fiscal ones, such as ThisFiscalQuarter and LastFiscalYear, which follow the organization's fiscal year as Dataverse keeps it; Overdue, before today; Earlier and Later, which take whatever falls before and after the lanes around them; and NoDate. A lane of your own holds exact days, or days counted from today's periods, under a title of its own: Between, Before and Starting (React: between, before and starting). A preset takes a title of its own too. Try it on the Close dates demo.
Each card lands in one lane, as a lane starts no earlier than where the one before it ends: listed after ThisWeek, ThisMonth holds the rest of the month, and a lane the ones before it already cover shows empty and takes no card. A date no lane holds — between two lanes of exact days — puts its card on none, so start with Earlier and end with Later to keep every card.
React
Blazor
{/* Fiscal quarters around today, and everything before and after them */}
<KanbanBoard
defaultViewId={openOpportunitiesViewId}
laneColumn="estimatedclosedate"
dateLanes={[
KanbanDateLane.Earlier,
KanbanDateLane.LastFiscalQuarter,
KanbanDateLane.ThisFiscalQuarter,
KanbanDateLane.NextFiscalQuarter,
KanbanDateLane.Later,
KanbanDateLane.NoDate,
]}
/>
{/* Lanes of exact days with titles of their own: before, during and after a launch week */}
<KanbanBoard
defaultViewId={openTasksViewId}
laneColumn="scheduledend"
dateLanes={[
KanbanDateLane.before('before', 'Before the launch', { year: 2027, month: 1, day: 4 }),
KanbanDateLane.between('launch', 'Launch week', { year: 2027, month: 1, day: 4 }, { year: 2027, month: 1, day: 11 }),
{ ...KanbanDateLane.Later, title: 'After the launch' },
KanbanDateLane.NoDate,
]}
/>
<!-- Fiscal quarters around today, and everything before and after them -->
<KanbanBoard DefaultViewId="openOpportunitiesViewId"
LaneColumn="estimatedclosedate"
DateLanes="@([
KanbanDateLane.Earlier,
KanbanDateLane.LastFiscalQuarter,
KanbanDateLane.ThisFiscalQuarter,
KanbanDateLane.NextFiscalQuarter,
KanbanDateLane.Later,
KanbanDateLane.NoDate,
])" />
<!-- Lanes of exact days with titles of their own: before, during and after a launch week -->
<KanbanBoard DefaultViewId="openTasksViewId"
LaneColumn="scheduledend"
DateLanes="@([
KanbanDateLane.Before("before", "Before the launch", new DateTime(2027, 1, 4)),
KanbanDateLane.Between("launch", "Launch week", new DateTime(2027, 1, 4), new DateTime(2027, 1, 11)),
KanbanDateLane.Later with { Title = "After the launch" },
KanbanDateLane.NoDate,
])" />
A card moved to another date lane is given that lane's first day, keeping its time of day — a lane behind today too, which gives it a past date — and moved to No date, it loses its date. A lane without a first day, such as Overdue, a first Earlier or an empty lane, takes no card. Set DateLaneDrop (React: dateLaneDrop) to None and the lanes take no card from one another: they only show when cards fall.
DateLaneOrder (React: dateLaneOrder) orders a date lane's cards. Rank, the default, orders them by the rank column, as every other lane: a card moved within its lane changes its rank alone. Date orders them by the date instead, with no rank column: a card dropped below another takes that card's day, keeping its own time, and one dropped at the top takes the lane's first day; cards of the same day stay in the order of their times. Try it on the Due dates demo.
React
Blazor
{/* Tasks by when they're due, each lane in due-date order */}
<KanbanBoard
defaultViewId={openTasksViewId}
laneColumn="scheduledend"
dateLaneOrder={KanbanDateLaneOrder.Date}
editable
/>
<!-- Tasks by when they're due, each lane in due-date order -->
<KanbanBoard DefaultViewId="openTasksViewId"
LaneColumn="scheduledend"
DateLaneOrder="KanbanDateLaneOrder.Date"
Editable="true" />
Counts and totals
Each lane's header shows how many cards it holds — 5000+ past 5,000 — and the totals named in LaneAggregates (React: laneAggregates): the sum, average, least or most of a number, money or date column, or a count of any column, over every card of the lane, not only those loaded. LaneAggregateFormats (React: laneAggregateFormats) gives each column's figures a format, such as C0 or N1. These are the totals the board starts with: the Totals button lets users choose their own of the columns the cards show, and a saved view keeps them. Counts and totals are read again after every move the board saves.
LaneLimits (React: laneLimits) gives lanes, by key, the most cards they should hold. A limited lane's header shows its count against its limit, as 7 / 5, marked once the count passes it. The count is of all the lane's cards, whatever the user searches for or filters by. A limit only warns unless EnforceLaneLimits (React: enforceLaneLimits) is set: then a full lane takes no card from another, isn't offered under Move to, and has no Add button. It is the board's rule, not the server's: keep a limit for every way a record is saved with an interceptor.
React
Blazor
{/* At most three tasks in progress at once */}
<KanbanBoard
defaultViewId={allTasksViewId}
laneColumn="statuscode"
laneLimits={{ '3': 3 }}
enforceLaneLimits
editable
/>
<!-- At most three tasks in progress at once -->
<KanbanBoard DefaultViewId="allTasksViewId"
LaneColumn="statuscode"
LaneLimits="@(new Dictionary<string, int> { ["3"] = 3 })"
EnforceLaneLimits="true"
Editable="true" />
Cards
A card shows the view's first column as its title and the view's other columns under their titles, as a grid's cards do, leaving out the lane column and the rank column. CardImageColumn (React: cardImageColumn) adds an image column as an avatar beside the title. Each lane loads PageSize (React: pageSize) cards at a time, 20 by default, with Load more at its foot. From Settings, users change the fields the cards show, how a lane's cards are sorted and how many each lane loads.
Draw the cards your own way with CardTemplate (React: cardRenderer). It is handed each card's record and lane; Field, Label and DisplayValue (React: field, label and displayValue) show a column as the default card does, and OpenAsync and MoveToAsync (React: open and moveTo) open the record and move the card, where CanOpen and CanMove say they can. A click on the card still opens its record, and its Move to menu sits beside your content. The columns a template shows must be the view's, so the board reads them with each card.
The demo's tasks through Dataverse's own All Tasks view, a lane for each status reason from Not Started to Canceled, five cards at a time. A CardTemplate (React: cardRenderer) shows each task's subject, what it is about, when it is due and its priority, the high ones in red.
React example
Blazor example
React TypeScript
Razor
CSS
Show
Not Started
In Progress
Waiting on someone else
Deferred
Completed
Canceled
Columns
Sort
Cards per lane
5
10
20
50
100
Not Started155
Book the on-site visit
Ebert, Marks and Tillman
Book the on-site visit
Stanton - Jacobi
Book the on-site visit
Rippin, Connelly and Hills
Book the on-site visit
Altenwerth, D'Amore and Mante
Book the on-site visit
Sawayn - Kessler
Load more
In Progress42
Book the on-site visit
Marks - Dickinson
Book the on-site visit
Luettgen, Goldner and Rowe
Chase the signed contract
Schaden, Cronin and Daniel
Chase the signed contract
Dietrich and Sons
Chase the signed contract
Lind - Parisian
Load more
Waiting on someone else28
Book the on-site visit
Schmeler and Sons
Book the on-site visit
Schmeler and Sons
Book the on-site visit
Botsford - Baumbach
Book the on-site visit
Schmeler and Sons
Chase the signed contract
Boyer - Schaefer
Load more
Deferred18
Book the on-site visit
Adams - Torphy
Book the on-site visit
Sanford, Grimes and Satterfield
Chase the signed contract
Cole, Tillman and Langworth
Chase the signed contract
Boyle, Zulauf and Lakin
Chase the signed contract
Waelchi, Murazik and Considine
Load more
Completed57
Book the on-site visit
Waelchi, Murazik and Considine
Book the on-site visit
Bartoletti LLC
Book the on-site visit
Cole, Tillman and Langworth
Book the on-site visit
Effertz Group
Book the on-site visit
O'Hara, Streich and Conroy
Load more
Canceled16
Book the on-site visit
Kemmer LLC
Confirm the training dates
Schmeler and Sons
Draft the statement of work
Raynor - Lakin
Draft the statement of work
Willms, Heathcote and Veum
Draft the statement of work
Rempel - Parisian
Load more
Where the board's table has a page registered for its records (see Record Pages) and the board has no Form (React: form), a card's title links to it, and opening the card goes there; ShowRecordLinks (React: showRecordLinks) turns that off. Otherwise, clicking a card opens its record in a dialog in the center or, with FormLocation (React: formLocation), in a side panel on the left or the right. The form is KanbanDefaultForm — the view's columns of the board's table and the lane column, each with the editor its type takes — or your own component of editors in Form (React: form). The record is read as a record page reads it, through the same table access and column security, and can be changed only in an Editable board, by a user who may change it.
Moving cards
In an Editable board (React: editable), users move a card to another lane by dragging it, from the keyboard or through the Move to menu on the card. The card moves at once, and the board saves what changed: the lane column — for a status reason, the reason alone, as the server moves the record's state with it — and the card's rank, where the board keeps one. A save that fails puts the card back and says so.
A card moves only where the user may change it: Write on its record, through the portal's table access, and on its lane column, which must be one an update can set and that column security doesn't withhold. Putting cards in order needs Write on the rank column too. A card the user can't move stays where it is, with no Move to menu.
Status reason lanes follow their states, and the changes of state the table allows, which the server registers with AddStateTransitions (see RecordContext). Opportunities, cases, quotes and orders have them by default: a card isn't taken by a lane its record can't reach from its own, and Move to lists only the lanes it can. A change of status an update makes — an opportunity reopened, a case canceled or reactivated, a quote activated, an order submitted — asks the user to confirm it first, and one Dataverse makes with an action of its own opens the close dialog, as Closing records below describes. The cards of a closed status aren't put in another order among themselves. A table without transitions moves freely: a task moved to Completed is completed, and moved back, open again.
Each lane's header in an Editable board has an Add button that opens a new record already in that lane, in the board's form: the page's Form (React: form) or KanbanDefaultForm. It shows only to users who may create the table's records and set its lane column, and never in a lane of closed records, as a record is created open. Once saved, the card appears at the top of its lane. Turn it off with AllowNew (React: allowNew). Handle PrepareNewRecord (React: prepareNewRecord) to give the new record more values before its form opens: it is given the record, the lane's value already in it, and the lane. A toolbar button can add a card too: CanCreate and NewAsync on the KanbanBoardContext (React: canCreate and newCard) say whether a lane takes a new card and open its form — with no lane named, in the first that takes one.
React
Blazor
<KanbanBoard defaultViewId={allTasksViewId} laneColumn="statuscode" editable>
{/* A toolbar button that adds a card as a lane's Add button does */}
<KanbanBoardButton label="New task" icon={<AddRegular />} onClick={(board) => board.newCard()} />
</KanbanBoard>
<KanbanBoard DefaultViewId="allTasksViewId" LaneColumn="statuscode" Editable="true">
<ToolbarItems>
<!-- A toolbar button that adds a card as a lane's Add button does -->
<KanbanBoardButton Label="New task"
Icon="@(new Icons.Regular.Size20.Add())"
OnClick="@((KanbanBoardContext board) => board.NewAsync())" />
</ToolbarItems>
</KanbanBoard>
React
Blazor
{/* Every task added on the board starts with a high priority */}
<KanbanBoard
defaultViewId={allTasksViewId}
laneColumn="statuscode"
editable
prepareNewRecord={(record) => ({
...record,
properties: { ...record.properties, prioritycode: { $type: ColumnType.Choice, value: 2 } },
})}
/>
<!-- Every task added on the board starts with a high priority -->
<KanbanBoard DefaultViewId="allTasksViewId" LaneColumn="statuscode" Editable="true"
PrepareNewRecord="@(card => { card.Record["prioritycode"] = new ChoiceValue { Value = 2 }; return Task.CompletedTask; })" />
Closing records
On a board by status reason, a card dropped in a lane that Dataverse closes records into with an action of its own — an opportunity's Won or Lost, a case's Resolved, a quote's Won or Closed, an order's Fulfilled or Canceled — opens a dialog for the status reason, the lane's to start with, and what the close records: the actual revenue and close date of a won opportunity; the close date of a lost one, and the competitor it was lost to where the user may pick competitors; the resolution and billable time of a case; the close date of a quote or an order; and a description. Confirmed, the record is closed through Dataverse's own message, such as WinOpportunity or CloseIncident; canceled, the card goes back. Dragged back to an open lane, an opportunity is reopened and a case reactivated once the user confirms. Try it on the Pipeline demo. The same dialog is behind the close buttons on a record's form, such as WinOpportunityButton: see RecordContext.
A close is judged as a change of the record's status: the user must be allowed to change the record and its status columns, and to point at the competitor they name. Dataverse files the close under the record as an activity of its own (opportunityclose, incidentresolution, quoteclose, orderclose), which needs no table access of yours. The portal's application user needs AppendTo on the record's table, Create and Append on activities, and for an opportunity AppendTo on competitors: see Dataverse Connection.
React
Blazor
{/* Dropped on Won, Lost or Resolved, a card opens the close dialog */}
<KanbanBoard defaultViewId={myOpportunitiesViewId} laneColumn="statuscode" editable />
<!-- Dropped on Won, Lost or Resolved, a card opens the close dialog -->
<KanbanBoard DefaultViewId="myOpportunitiesViewId" LaneColumn="statuscode" Editable="true" />
Putting cards in order
Name a RankColumn (React: rankColumn) and users put each lane's cards in order by hand. A lane is sorted by the rank, then by the view's sort, and a card dropped between two others is given a rank between theirs. Usually that rank is all a drop writes. Cards with no rank sort first, and are ranked in turn when a card is dropped among them or just below them; a lane whose ranks have no room left between two cards is ranked afresh, in the order it shows. Those ranks are saved with the move, in one transaction. A lane of more than 5,000 cards can't be put in order by hand, nor can a card be dropped where it would rank one the user may not change. Without a rank column, a lane follows the view's sort, and a card moves only to another lane.
The rank column is a Decimal column of the board's table whose range allows negative numbers, since a card dropped above the first is ranked below it. Its precision decides how often a lane is ranked afresh: cards start a million apart, and each drop between two halves the room between them, so a column of ten decimal places takes some fifty drops in one place before its lane is ranked again, and one of none about twenty.
Handle SaveChange (React: onCardMove) to save a move your own way, in place of the board: through a service of your own, after asking the user, or not at all. It is given a KanbanCardMove: the card's Record, the lanes it came From and went To, its Index there and its Rank, the Update the board would write, and the OtherUpdates that belong in the same transaction, written after it — other cards' ranks, or, for a card leaving a closed lane, its own rank once its status has reopened it — and, for a card dropped in a lane that closes it, the Close (React: close) the user confirmed, to send after them. Return true to keep the card where it was dropped, or false to put it back. The board reads nothing again after a move it didn't save, so a page can keep a move on the page alone.
React
Blazor
<KanbanBoard
defaultViewId={openOpportunitiesViewId}
laneColumn="opportunityratingcode"
editable
// Saved through the page's own service; false puts the card back.
onCardMove={(move) => pipeline.changeRating(move.record.id, move.to.key)}
/>
<KanbanBoard DefaultViewId="openOpportunitiesViewId"
LaneColumn="opportunityratingcode"
Editable="true"
SaveChange="SaveMoveAsync" />
@code {
// Saved through the page's own service; false puts the card back.
private Task<bool> SaveMoveAsync(KanbanCardMove move)
=> _pipeline.ChangeRatingAsync(move.Record.Id, move.To.Key);
}
Keyboard, touch and screen readers
Every card that can move has a … button with a Move to menu of the lanes it can go to, which works the same from the keyboard, by touch and with a screen reader. A card can also be moved from the keyboard directly: focus it, press Space to pick it up, the arrow keys to carry it to another lane or another place in its own, and Space again to put it down. On a touch screen a card is dragged by its grip, so the lanes still scroll under a finger. Screen readers hear each move — Card moved to Lane — and a move that fails.
Toolbar
The toolbar is laid out as a grid's: the view picker on the left — or the Title (React: title), when there is only one view — and on the right your own controls, Totals, the search box, Filter, Refresh and Settings. Add your controls with ToolbarItems (React: the board's children). A KanbanBoardButton is handed the board when clicked: its table, the view it shows, its lanes in their order, and RefreshAsync (React: refresh) to read every lane again.
React
Blazor
<KanbanBoard defaultViewId={openOpportunitiesViewId} laneColumn="opportunityratingcode">
{/* Your own button, handed the board when clicked */}
<KanbanBoardButton
label="Export"
icon={<ArrowDownloadRegular />}
onClick={(board) => exportAsync(board.viewId, board.lanes)}
/>
</KanbanBoard>
<KanbanBoard DefaultViewId="openOpportunitiesViewId" LaneColumn="opportunityratingcode">
<ToolbarItems>
<!-- Your own button, handed the board when clicked -->
<KanbanBoardButton Label="Export"
Icon="@(new Icons.Regular.Size20.ArrowDownload())"
OnClick="@((KanbanBoardContext board) => ExportAsync(board.ViewId, board.Lanes))" />
</ToolbarItems>
</KanbanBoard>
Views, search and filters
A board shows the records of a view — one of Dataverse's or one the page defines — and its table is the view's. DefaultViewId (React: defaultViewId) is the view it opens on, and ViewIds (React: viewIds) the others the view picker offers, of the same table. The search box finds cards as a grid's search finds rows: from the start of a search column's value, or anywhere after a leading *. It looks in the columns the cards show, names them in its tooltip, and marks the matches on the cards. Turn it off with AllowSearch (React: allowSearch).
With AllowFiltering (React: allowFiltering), the toolbar's Filter menu offers Edit filters, the dialog a grid's Filter button opens, whose filters take the place of the view's own until the user picks another view. It is off by default, because the user can take away every filter the view has.
With AllowSavedViews (React: allowSavedViews), a signed-in user can save the board as they've arranged it — the view with their filters, the cards' fields and sort, the lanes' totals, the cards each lane loads, and the lanes collapsed or hidden — as a view of their own, and choose their default view and hide the ones they don't use, from the Filter menu. Without a SavedViewsKey (React: savedViewsKey), a board shares its views with the grids, calendars and boards of its table that have none, each keeping its own layout of them. How views are saved, changed and shared is on the Saved Views page.
A page can define views of its own in code with CustomViewDefinitions (React: customViewDefinitions), the grid's view definitions. They are offered in the picker beside ViewIds without being listed there, DefaultViewId can name one, and a board can have these alone. Each is read by its own FetchXML, within the user's table access, and lays the board out as it says: its PageSize is the cards a lane loads, a column's Hidden and DisplayLabel hide or retitle its card field, and its Aggregates are the lanes' totals. Build the list once, in a field: a new list starts the board again.
<KanbanBoard CustomViewDefinitions="_views" LaneColumn="statuscode" Editable="true" />
@code {
// The open opportunities, ten cards a lane, with each lane's estimated revenue
private readonly List<ViewDefinition> _views =
[
new()
{
Id = new Guid("4c1e7a92-5d3b-4f8e-9a6c-2b0d8e4f1a37"),
TableName = "opportunity",
DisplayName = "Pipeline",
PageSize = 10,
FetchXml = """
<fetch>
<entity name="opportunity">
<attribute name="name" />
<attribute name="estimatedvalue" />
<attribute name="estimatedclosedate" />
<filter><condition attribute="statecode" operator="eq" value="0" /></filter>
</entity>
</fetch>
""",
Columns =
[
new ViewColumn { ColumnName = "name" },
new ViewColumn { ColumnName = "estimatedvalue", Aggregates = [new(QueryAggregate.Sum, "C0")] },
new ViewColumn { ColumnName = "estimatedclosedate" },
],
},
];
}
Keeping its place in the URL
Name a query parameter in PersistedStateQueryParameter (React: persistedStateQueryParameter), and the board keeps the view picked and the lanes collapsed there, so a reload or a shared link opens it as it was left. What it would open on anyway is left out. The search is kept too with IncludeSearchInPersistedState; it is left out by default, since what users search for can be sensitive. Each change replaces the browser's history entry, and both stacks write the same value, so a link made on one opens the same board on the other.
The board is drawn with Fluent UI's own controls and design tokens, so it follows your theme, light or dark, and your accent color, and a lane's dot takes its option's color from Dataverse. Lanes are 280 pixels wide, side by side, and scroll sideways when they don't fit; on a phone each takes most of the screen and snaps into place. Height (React: height) sets the board's height, toolbar included — 600 pixels by default — and each lane scrolls its cards within it. To restyle it, the board, its lanes and its cards carry classes of their own, among them ppp-kanban, ppp-kanban-lane, ppp-kanban-lane-header and ppp-kanban-card; Class and Style (React: className and style) add your own.
React
Blazor
KanbanBoard Class
Parameters
Name
Type
Default
Description
AllowFiltering
bool
False
Whether the user can change the view's filters, from the Filter menu, as on a grid. Off by default: the user can take away every filter the view has.
AllowNew
bool
True
Whether each lane of an KanbanBoard.Editable board offers a new card, with an Add button in its header: one the user may create, in the lane's value — none in a lane of closed cards, as a record is created open. Defaults to true.
AllowSavedViews
bool
False
Lets signed-in users save the board as they've arranged it — the view it shows with their filters, its cards' fields and sort, the lanes' totals and the lanes collapsed or hidden — as views of their own, offered in the view picker beside the page's, and choose their default view and hide the views they don't use. Off by default. Needs the Power Portals Pro Core solution's saved-view tables; an internal user also needs the Power Portals Pro Saved Views security role.
AllowSearch
bool
True
Whether the toolbar has a search box. Defaults to true.
CardImageColumn
string?
An image column each default card shows as an avatar beside its title, as a grid's cards do.
CardTemplate
RenderFragment<KanbanCardContext>?
The content of each card, in place of the default: its title — the view's first column — and the view's other columns under their titles.
CustomViewDefinitions
List<ViewDefinition>?
Views the page defines — the grid's Models.ViewDefinitions — offered in the picker beside KanbanBoard.ViewIds without being listed there; KanbanBoard.DefaultViewId may name one. A board may have these alone. Each is read by its own FetchXML, and lays the board out as it says (Models.ViewDefinition)): the cards a lane reads at a time, its columns' card fields hidden or titled, and the lanes' totals. One of another table than the board's is left out. Build the list once — in a field, not inline in the markup — as a new list starts the board again.
DateLaneDrop
KanbanDateLaneDrop
ChangeDate
What a card dropped in another date lane does, where the KanbanBoard.LaneColumn is a date: it takes the lane's first day (KanbanDateLaneDrop.ChangeDate, the default) — keeping its time of day; no date in No date; none is dropped in Overdue — or date lanes take no card from one another (KanbanDateLaneDrop.None). No effect on other lanes.
DateLaneOrder
KanbanDateLaneOrder
Rank
How the cards of date lanes are ordered, where the KanbanBoard.LaneColumn is a date: by the KanbanBoard.RankColumn as other lanes are (KanbanDateLaneOrder.Rank, the default), or by the date itself (KanbanDateLaneOrder.Date) — a card dropped below another takes its day, keeping its own time, one dropped at the top the lane's first day, and the rank column isn't used. No effect on other lanes.
DateLanes
IReadOnlyList<KanbanDateLane>?
The lanes a date KanbanBoard.LaneColumn gives, in date order: presets such as KanbanDateLane.Today, KanbanDateLane.ThisMonth or KanbanDateLane.ThisFiscalQuarter — worked out at each read for today in the user's time zone, the culture's week and the organization's fiscal year — or lanes of your own (DateTime,System.DateTime) and the like). Each starts where the one before it ends, so every card lands in one lane at most; a card whose date none holds isn't on the board. Without them, the default seven (KanbanDateLane.Defaults): Overdue, Today, Tomorrow, This week, Next week, Later and No date. A lane is titled by its KanbanDateLane.Title, else a preset's name in the user's language, else its key. A new list of the same lanes — one written inline in the markup — doesn't start the board again; other lanes do.
DefaultViewId
Guid?
The view the board opens on. The board's table is the view's.
Editable
bool
False
Whether cards can be moved. A card moves when the user may change its record and its lane column, and — on a status reason's lanes — the table's state transitions allow the move (Models.KanbanLane)): a lane offers only the moves they make, and a drop they refuse goes back. A card dropped in a lane its transitions close it into — an opportunity's Won or Lost, a case's Resolved, a quote's Won or Closed, an order's Fulfilled or Canceled — is closed through Dataverse's own message: the board asks how — the reason, the lane's to begin with, and what the close records. Any other change of state they make — reopening an opportunity, canceling a case, activating a quote — is asked to be confirmed first. Off by default.
EnforceLaneLimits
bool
False
Whether the board keeps to KanbanBoard.LaneLimits: a full lane takes no card from another — a drop there goes back, and Move to leaves it out — and offers no new card. Cards still move within it. Off by default: a lane over its limit is only marked.
Form
Type?
The form a card's record opens in: a component of editors, as a grid's record dialog takes. Without it, a card opens its record's page where its table has one (KanbanBoard.ShowRecordLinks), else Components.KanbanDefaultForm: the view's columns and the lane column.
FormLocation
DialogLocation
Center
Where a card's record opens: centered, or in a side panel on the left or right. Defaults to centered.
Height
string
600px
Height of the whole board, toolbar included, as any CSS length. Defaults to 600px.
IncludeSearchInPersistedState
bool
False
Whether the search is kept with the rest of the state. Has no effect without KanbanBoard.PersistedStateQueryParameter.
LaneAggregateFormats
IReadOnlyDictionary<string, string>?
The format each column's totals in KanbanBoard.LaneAggregates are shown in ('N0'), by its name.
LaneAggregates
IReadOnlyDictionary<string, QueryAggregate>?
The totals each lane's header shows to start with — a sum, an average, the least, the most or a count of a column, by its name. The user changes them with the Totals button, and a saved view keeps them.
LaneColumn*
string
The column the lanes come from: a choice, a status reason, a Yes/No, a lookup or a date column of the board's table. Any other shows a message in place of the board. A date column's lanes are when it falls, in days of the user's time zone: Overdue, Today, Tomorrow, This week, Next week, Later and No date, or the page's KanbanBoard.DateLanes (KanbanBoard.DateLaneDrop, KanbanBoard.DateLaneOrder).
LaneLimits
IReadOnlyDictionary<string, int>?
The most cards each lane should hold, by KanbanLane.Key: a limited lane's header shows its count against its limit ('4 / 5'), and marks it when the lane holds more. The count is the whole lane's, whatever the search and the user's filters show. A lane not named has no limit.
Lanes
IEnumerable<string>?
Which lanes the board shows, and in what order, by KanbanLane.Key: an option's value ('1'), Yes/No's '1' and '0', a record's id, a date lane's key (KanbanDateLane.Key: 'overdue', 'today', …), or an empty key for the lane of cards without a value. Every lane, in Dataverse's order, when not given. For date lanes it picks among the KanbanBoard.DateLanes, whose days stay as those define them.
LookupLanes
IEnumerable<LookupValue>?
A lookup's lanes: one for each record, in the order given. Without them, the board shows a lane for each record the view's cards name.
PageSize
int
20
The cards a lane reads at a time; the next come with its Load more. Defaults to 20.
PersistedStateQueryParameter
string?
The name of a URL query parameter to keep the board's state in — the view picked, the lanes collapsed and (with KanbanBoard.IncludeSearchInPersistedState) the search — so a reload or a link opens it where it was left. Each change replaces the browser's history entry.
PrepareNewRecord
Func<KanbanNewRecord, Task>?
Prepares a new card's record before its form opens — from a lane's Add button or Models.KanbanLane) — once the board has given it the lane's value: the host sets defaults through the record's indexer (card.Record['column'] = value), and each value it sets is saved with the record. The form doesn't open if it throws, and the error is shown as a failed create's is.
RankColumn
string?
A decimal column the board keeps the cards' order in, by hand: each lane is sorted by it, and a card dropped between two others is given a rank between theirs. Without it the cards follow the view's sort, and a card moves only to another lane.
SaveChange
Func<KanbanCardMove, Task<bool>>?
Saves a move in place of the board; returning false puts the card back, as when the save is refused. Without it, the board saves the move itself. A move that closes the card is given once the user has said how, with the request to send in KanbanCardMove.Close; one that changes a state the table's transitions govern, once the user has confirmed it.
SavedViewsKey
string?
Names this board among the controls of its table, so the views saved on it and the user's default and hidden views are its own. Without one, the board shares its views with every grid, calendar and board of the table that has none. Letters, digits, -, _ and ..
ShowEmptyLanes
bool
True
Whether a lane with no cards is shown. Defaults to true.
ShowRecordLinks
bool
True
Whether cards link to their records' pages, registered with AddRecordPage, as a grid's rows and lookups do. Where the board's table has a page and the board is given no KanbanBoard.Form, a card's title links to its record's page and opening the card goes there; a lookup on a card links to its record's page where that table has one. A table with no page opens its cards in the board's dialog. A new card is always made in the dialog. Default true.
SignInUrl
string
Account/Login
Where a user who isn't signed in is sent to sign in, from the saved views' menu; they come back to this page. Relative to the app's base address. Defaults to Account/Login.
Title
string?
The board's heading, shown in place of the view picker when there is only one view.
ToolbarItems
RenderFragment?
Controls for the toolbar — Components.KanbanBoardButtons, or any other markup — ahead of the search box.
ViewIds
IEnumerable<Guid>?
Other views of the same table the user can switch to, in the view picker.
ViewSort
ViewSort
NameAscending
The order of the views in the picker. Defaults to ViewSort.NameAscending, as a grid's.
Name:AllowFiltering
Type:bool
Default:False
Description:Whether the user can change the view's filters, from the Filter menu, as on a grid. Off by default: the user can take away every filter the view has.
Name:AllowNew
Type:bool
Default:True
Description:Whether each lane of an KanbanBoard.Editable board offers a new card, with an Add button in its header: one the user may create, in the lane's value — none in a lane of closed cards, as a record is created open. Defaults to true.
Name:AllowSavedViews
Type:bool
Default:False
Description:Lets signed-in users save the board as they've arranged it — the view it shows with their filters, its cards' fields and sort, the lanes' totals and the lanes collapsed or hidden — as views of their own, offered in the view picker beside the page's, and choose their default view and hide the views they don't use. Off by default. Needs the Power Portals Pro Core solution's saved-view tables; an internal user also needs the Power Portals Pro Saved Views security role.
Name:AllowSearch
Type:bool
Default:True
Description:Whether the toolbar has a search box. Defaults to true.
Name:CardImageColumn
Type:string?
Description:An image column each default card shows as an avatar beside its title, as a grid's cards do.
Name:CardTemplate
Type:RenderFragment<KanbanCardContext>?
Description:The content of each card, in place of the default: its title — the view's first column — and the view's other columns under their titles.
Name:CustomViewDefinitions
Type:List<ViewDefinition>?
Description:Views the page defines — the grid's Models.ViewDefinitions — offered in the picker beside KanbanBoard.ViewIds without being listed there; KanbanBoard.DefaultViewId may name one. A board may have these alone. Each is read by its own FetchXML, and lays the board out as it says (Models.ViewDefinition)): the cards a lane reads at a time, its columns' card fields hidden or titled, and the lanes' totals. One of another table than the board's is left out. Build the list once — in a field, not inline in the markup — as a new list starts the board again.
Name:DateLaneDrop
Type:KanbanDateLaneDrop
Default:ChangeDate
Description:What a card dropped in another date lane does, where the KanbanBoard.LaneColumn is a date: it takes the lane's first day (KanbanDateLaneDrop.ChangeDate, the default) — keeping its time of day; no date in No date; none is dropped in Overdue — or date lanes take no card from one another (KanbanDateLaneDrop.None). No effect on other lanes.
Name:DateLaneOrder
Type:KanbanDateLaneOrder
Default:Rank
Description:How the cards of date lanes are ordered, where the KanbanBoard.LaneColumn is a date: by the KanbanBoard.RankColumn as other lanes are (KanbanDateLaneOrder.Rank, the default), or by the date itself (KanbanDateLaneOrder.Date) — a card dropped below another takes its day, keeping its own time, one dropped at the top the lane's first day, and the rank column isn't used. No effect on other lanes.
Name:DateLanes
Type:IReadOnlyList<KanbanDateLane>?
Description:The lanes a date KanbanBoard.LaneColumn gives, in date order: presets such as KanbanDateLane.Today, KanbanDateLane.ThisMonth or KanbanDateLane.ThisFiscalQuarter — worked out at each read for today in the user's time zone, the culture's week and the organization's fiscal year — or lanes of your own (DateTime,System.DateTime) and the like). Each starts where the one before it ends, so every card lands in one lane at most; a card whose date none holds isn't on the board. Without them, the default seven (KanbanDateLane.Defaults): Overdue, Today, Tomorrow, This week, Next week, Later and No date. A lane is titled by its KanbanDateLane.Title, else a preset's name in the user's language, else its key. A new list of the same lanes — one written inline in the markup — doesn't start the board again; other lanes do.
Name:DefaultViewId
Type:Guid?
Description:The view the board opens on. The board's table is the view's.
Name:Editable
Type:bool
Default:False
Description:Whether cards can be moved. A card moves when the user may change its record and its lane column, and — on a status reason's lanes — the table's state transitions allow the move (Models.KanbanLane)): a lane offers only the moves they make, and a drop they refuse goes back. A card dropped in a lane its transitions close it into — an opportunity's Won or Lost, a case's Resolved, a quote's Won or Closed, an order's Fulfilled or Canceled — is closed through Dataverse's own message: the board asks how — the reason, the lane's to begin with, and what the close records. Any other change of state they make — reopening an opportunity, canceling a case, activating a quote — is asked to be confirmed first. Off by default.
Name:EnforceLaneLimits
Type:bool
Default:False
Description:Whether the board keeps to KanbanBoard.LaneLimits: a full lane takes no card from another — a drop there goes back, and Move to leaves it out — and offers no new card. Cards still move within it. Off by default: a lane over its limit is only marked.
Name:Form
Type:Type?
Description:The form a card's record opens in: a component of editors, as a grid's record dialog takes. Without it, a card opens its record's page where its table has one (KanbanBoard.ShowRecordLinks), else Components.KanbanDefaultForm: the view's columns and the lane column.
Name:FormLocation
Type:DialogLocation
Default:Center
Description:Where a card's record opens: centered, or in a side panel on the left or right. Defaults to centered.
Name:Height
Type:string
Default:600px
Description:Height of the whole board, toolbar included, as any CSS length. Defaults to 600px.
Name:IncludeSearchInPersistedState
Type:bool
Default:False
Description:Whether the search is kept with the rest of the state. Has no effect without KanbanBoard.PersistedStateQueryParameter.
Name:LaneAggregateFormats
Type:IReadOnlyDictionary<string, string>?
Description:The format each column's totals in KanbanBoard.LaneAggregates are shown in ('N0'), by its name.
Name:LaneAggregates
Type:IReadOnlyDictionary<string, QueryAggregate>?
Description:The totals each lane's header shows to start with — a sum, an average, the least, the most or a count of a column, by its name. The user changes them with the Totals button, and a saved view keeps them.
Name:LaneColumn*
Type:string
Description:The column the lanes come from: a choice, a status reason, a Yes/No, a lookup or a date column of the board's table. Any other shows a message in place of the board. A date column's lanes are when it falls, in days of the user's time zone: Overdue, Today, Tomorrow, This week, Next week, Later and No date, or the page's KanbanBoard.DateLanes (KanbanBoard.DateLaneDrop, KanbanBoard.DateLaneOrder).
Name:LaneLimits
Type:IReadOnlyDictionary<string, int>?
Description:The most cards each lane should hold, by KanbanLane.Key: a limited lane's header shows its count against its limit ('4 / 5'), and marks it when the lane holds more. The count is the whole lane's, whatever the search and the user's filters show. A lane not named has no limit.
Name:Lanes
Type:IEnumerable<string>?
Description:Which lanes the board shows, and in what order, by KanbanLane.Key: an option's value ('1'), Yes/No's '1' and '0', a record's id, a date lane's key (KanbanDateLane.Key: 'overdue', 'today', …), or an empty key for the lane of cards without a value. Every lane, in Dataverse's order, when not given. For date lanes it picks among the KanbanBoard.DateLanes, whose days stay as those define them.
Name:LookupLanes
Type:IEnumerable<LookupValue>?
Description:A lookup's lanes: one for each record, in the order given. Without them, the board shows a lane for each record the view's cards name.
Name:PageSize
Type:int
Default:20
Description:The cards a lane reads at a time; the next come with its Load more. Defaults to 20.
Name:PersistedStateQueryParameter
Type:string?
Description:The name of a URL query parameter to keep the board's state in — the view picked, the lanes collapsed and (with KanbanBoard.IncludeSearchInPersistedState) the search — so a reload or a link opens it where it was left. Each change replaces the browser's history entry.
Name:PrepareNewRecord
Type:Func<KanbanNewRecord, Task>?
Description:Prepares a new card's record before its form opens — from a lane's Add button or Models.KanbanLane) — once the board has given it the lane's value: the host sets defaults through the record's indexer (card.Record['column'] = value), and each value it sets is saved with the record. The form doesn't open if it throws, and the error is shown as a failed create's is.
Name:RankColumn
Type:string?
Description:A decimal column the board keeps the cards' order in, by hand: each lane is sorted by it, and a card dropped between two others is given a rank between theirs. Without it the cards follow the view's sort, and a card moves only to another lane.
Name:SaveChange
Type:Func<KanbanCardMove, Task<bool>>?
Description:Saves a move in place of the board; returning false puts the card back, as when the save is refused. Without it, the board saves the move itself. A move that closes the card is given once the user has said how, with the request to send in KanbanCardMove.Close; one that changes a state the table's transitions govern, once the user has confirmed it.
Name:SavedViewsKey
Type:string?
Description:Names this board among the controls of its table, so the views saved on it and the user's default and hidden views are its own. Without one, the board shares its views with every grid, calendar and board of the table that has none. Letters, digits, -, _ and ..
Name:ShowEmptyLanes
Type:bool
Default:True
Description:Whether a lane with no cards is shown. Defaults to true.
Name:ShowRecordLinks
Type:bool
Default:True
Description:Whether cards link to their records' pages, registered with AddRecordPage, as a grid's rows and lookups do. Where the board's table has a page and the board is given no KanbanBoard.Form, a card's title links to its record's page and opening the card goes there; a lookup on a card links to its record's page where that table has one. A table with no page opens its cards in the board's dialog. A new card is always made in the dialog. Default true.
Name:SignInUrl
Type:string
Default:Account/Login
Description:Where a user who isn't signed in is sent to sign in, from the saved views' menu; they come back to this page. Relative to the app's base address. Defaults to Account/Login.
Name:Title
Type:string?
Description:The board's heading, shown in place of the view picker when there is only one view.
Name:ToolbarItems
Type:RenderFragment?
Description:Controls for the toolbar — Components.KanbanBoardButtons, or any other markup — ahead of the search box.
Name:ViewIds
Type:IEnumerable<Guid>?
Description:Other views of the same table the user can switch to, in the view picker.
Name:ViewSort
Type:ViewSort
Default:NameAscending
Description:The order of the views in the picker. Defaults to ViewSort.NameAscending, as a grid's.
Methods
Name
Parameters
Type
Description
RefreshAsync
Task
Reads every lane shown again.
Name:RefreshAsync
Type:Task
Description:Reads every lane shown again.
Apply begins with filter on the following columns:
Account
Contact
Email
Potential Customer
Topic
Uses number comparisons on the following columns:
Est. revenue
Probability
Uses date or year comparisons on the following columns:
Est. close date
Center Right Left Rank Date
Apply begins with filter on the following columns:
Owner
Priority
Regarding
Status Reason
Subject
Uses date or year comparisons on the following columns:
Due Date
Apply begins with filter on the following columns:
Account
Contact
Email
Potential Customer
Topic
Uses number comparisons on the following columns:
Est. revenue
Probability
Uses date or year comparisons on the following columns: