对象格式 API (预览版)

对象格式 允许用户通过直接选择要修改的元素来快速轻松地修改视觉对象的格式。 选择元素后,格式窗格会自动导航并展开所选元素的特定格式设置。 有关对象格式的详细信息,请参阅 Power BI Desktop中的 对象格式。

若要向视觉对象添加这些功能,每个视觉对象都需要为每个子选择区域提供一个子选择样式选项和快捷方式。

注意

  • 支持对象格式的视觉对象需要实现 getFormattingModel API,该 API 版本 5.1 提供。
  • 如果使用的是 powerbi-visuals-utils-formattingmodel,请至少使用版本 6.0.0。

创建对象上体验

当用户选择一个子选择元素以发送 Power BI 子选择时,请使用子选择服务。 使用 子选择 API提供子选择样式和快捷方式。 可以使用 子选择帮助程序 来简化该过程。

格式模式

格式模式是一种新模式,用户可以在创作模式下打开和关闭 onObject 格式。 视觉对象在更新选项中使用格式模式的状态进行更新。 更新选项还包括当前子选择的子Selection 作为 CustomVisualSubSelection

如何实现对象格式 API

功能文件

capabilites.json 文件中,添加以下属性以声明视觉对象支持对象格式:

{
  "supportsOnObjectFormatting": true,
  "enablePointerEventsFormatMode": true,
}

IVisual 接口

视觉对象需要实现 VisualOnObjectFormatting 接口作为 IVisual 接口的一部分。

VisualOnObjectFormatting 包含三种方法:

getSubSelectionStyles

每个视觉对象都需要实现 getSubSelectionStyles 方法,该方法在子选择元素被子选择时调用。 getSubSelectionStyles 方法以当前子选择的元素的形式作为 CustomVisualSubSelection 数组提供,应返回 SubSelectionStyles 对象或 undefined

有三类子选择样式涵盖大多数方案:

  • 发短信
  • 数字文本
  • 形状

每个 SubSelectionStyles 对象为用户提供修改元素样式的不同体验。

getSubSelectionShortcuts

若要为用户提供更多选项,视觉对象必须实现 getSubSelectionShortcuts 方法。 此方法返回 VisualSubSelectionShortcutsundefined。 此外,如果提供了 SubSelectionShortcuts,还必须提供 VisualNavigateSubSelectionShortcut,以便在用户子选择元素且格式窗格处于打开状态时,窗格会自动滚动到相应的卡片。

有几个子选择快捷方式可用于修改视觉状态。 每个菜单都使用相应的标签在上下文菜单中定义一个菜单项。

Sub-Selection 消除歧义菜单: On-Object 消除歧义菜单提供了一种方法,让用户在不清楚哪个视觉元素正在子选择时选择其所需的子选择。 当用户子选择视觉对象背景时,通常会发生这种情况。 若要使消除歧义菜单显示更多子选择,视觉对象必须通过 getSubSelectables 方法提供所有子选择。

getSubSelectables

若要为消除歧义菜单提供子选择,视觉对象需要实现 getSubSelectables 方法。 此方法提供可选的 filterType 参数,类型为 SubSelectionStylesType,并返回 CustomVisualSubSelectionundefined数组。 如果要使用 HTMLSubSelectionHelper 创建子选择,则可以使用 HTMLSubSelectionHelper.getSubSelectables() 方法从 DOM 收集可选择的元素。

Sub-Selection 直接文本编辑: 使用 On-Object 格式设置,可以双击可选择的子元素的文本以直接编辑它。 若要提供直接编辑功能,需要提供一个 RectangleSubSelectionOutline,其中包含使用 SubSelectableDirectEdit 对象填充的相应 cVDirectEdit 属性。 大纲可以作为自定义大纲提供,或者,如果使用 HTMLSubSelectionHelper 则可以使用 SubSelectableDirectEdit 属性。 (请参阅 HTMLSubSelectionHelper 提供的属性)

尚不支持为特定数据点(使用选择器)添加直接编辑。

FormattingId 接口

以下接口用于引用 subSelection 快捷方式和样式。

interface FormattingId {
            objectName: string;
            propertyName: string;
            selector?: powerbi.data.Selector;
        }
  • objectName:capabilities.json中声明的对象名称。
  • propertyName:在 capabilities.json中声明的对象的属性名称。
  • selector:如果 datapoint 具有 selectionId,请使用 selectionId.getSelector(),则此选择器必须与为格式模型切片提供的选项相同。

例子

在此示例中,我们生成一个自定义视觉对象,其中包含两个对象,colorSelectordirectEdit。 我们使用 onobjectFormatting utils 中的 HTMLSubSelectionHelper 来处理大部分子选择作业。 有关详细信息,请参阅 对象上的 utils

首先,为格式窗格生成卡片,并为每个子选择提供 子SelectionShortcuts样式

定义对象

定义对象并声明视觉对象支持 capabilities.json中的 OnObject 格式:

"objects": {
      "directEdit": {
      "properties": {
        "show": {
          "displayName": "Show",
          "type": {
            "bool": true
          }
        },
        "textProperty": {
          "displayName": "Text",
          "type": {
            "text": true
          }
        },
        "fontFamily": {
          "type": {
            "formatting": {
              "fontFamily": true
            }
          }
        },
        "fontSize": {
          "type": {
            "formatting": {
              "fontSize": true
            }
          }
        },
        "bold": {
          "type": {
            "bool": true
          }
        },
        "italic": {
          "type": {
            "bool": true
          }
        },
        "underline": {
          "type": {
            "bool": true
          }
        },
        "fontColor": {
          "displayName": "Font Color",
          "type": {
            "fill": {
              "solid": {
                "color": true
              }
            }
          }
        },
        "background": {
          "displayName": "Background",
          "type": {
            "fill": {
              "solid": {
                "color": true
              }
            }
          }
        },
        "position": {
          "displayName": "Position",
          "type": {
            "enumeration": [
              { "displayName": "Left", "value": "Left" }, { "displayName": "Right", "value": "Right" }
            ]
          }
        }
      }
    },
    "colorSelector": {
      "displayName": "Data Colors",
      "properties": {
        "fill": {
          "displayName": "Color",
          "type": {
            "fill": {
              "solid": {
                "color": true
              }
            }
          }
        }
      }
    },
   },
  "supportsOnObjectFormatting": true,
  "enablePointerEventsFormatMode": true,

生成格式卡

使用 formattingModel utils生成格式卡。

颜色选择器卡片设置

class ColorSelectorCardSettings extends Card {
    name: string = "colorSelector";
    displayName: string = "Data Colors";
    slices = [];
}

将方法添加到 formatSetting,以便我们可以动态填充 colorSelector 对象的切片(数据点)。

populateColorSelector(dataPoints: BarChartDataPoint[]) {
        let slices: formattingSettings.ColorPicker[] = this.colorSelector.slices;
        if (dataPoints) {
            dataPoints.forEach(dataPoint => {
                slices.push(new formattingSettings.ColorPicker({
                    name: "fill",
                    displayName: dataPoint.category,
                    value: { value: dataPoint.color },
                    selector: dataPoint.selectionId.getSelector(),
                }));
            });
        }
    }

我们在选择器字段中传递特定数据点的选择器。 此选择器是实现 OnObject 的获取 API 时使用的选择器。

直接编辑卡片设置

class DirectEditSettings extends Card {
    displayName = 'Direct Edit';
    name = 'directEdit';
    private minFontSize: number = 8;
    private defaultFontSize: number = 11;
    show = new formattingSettings.ToggleSwitch({
        name: "show",
        displayName: undefined,
        value: true,
    });
    topLevelSlice = this.show;
    textProperty = new formattingSettings.TextInput({
        displayName: "Text Property",
        name: "textProperty",
        value: "What is your quest?",
        placeholder: ""
    });
    position = new formattingSettings.ItemDropdown({
        name: 'position',
        items: [{ displayName: 'Left', value: 'Left' }, { displayName: 'Right', value: 'Right' }],
        value: { displayName: 'Right', value: 'Right' }
    });
    font = new formattingSettings.FontControl({
        name: "font",
        displayName: 'Font',
        fontFamily: new formattingSettings.FontPicker({
            name: "fontFamily",
            displayName: "Font Family",
            value: "Segoe UI, wf_segoe-ui_normal, helvetica, arial, sans-serif"
        }),
        fontSize: new formattingSettings.NumUpDown({
            name: "fontSize",
            displayName: "Font Size",
            value: this.defaultFontSize,
            options: {
                minValue: {
                    type: powerbi.visuals.ValidatorType.Min,
                    value: this.minFontSize,
                }
            }
        }),
        bold: new formattingSettings.ToggleSwitch({
            name: 'bold',
            displayName: "Font Size",
            value: true
        }),
        italic: new formattingSettings.ToggleSwitch({
            name: 'italic',
            displayName: "Font Size",
            value: true
        }),
        underline: new formattingSettings.ToggleSwitch({
            name: 'underline',
            displayName: "Font Size",
            value: true
        })
    });
    fontColor = new formattingSettings.ColorPicker({
        name: "fontColor",
        displayName: "Color",
        value: { value: "#000000" }
    });
    background = new formattingSettings.ColorPicker({
        name: "background",
        displayName: "Color",
        value: { value: "#FFFFFF" }
    });
    slices = [this.show, this.textProperty, this.font, this.fontColor, this.background, this.position];
}

使用子选择帮助程序属性

HTMLSubSelectionHelper 属性添加到对象。 若要查看 HTMLSubSelectionHelper 提供的属性,请查看 对象 utils 文档

  • 对于 directEdit 属性:

    import {
       HtmlSubSelectableClass, HtmlSubSelectionHelper, SubSelectableDirectEdit as SubSelectableDirectEditAttr,
       SubSelectableDisplayNameAttribute, SubSelectableObjectNameAttribute, SubSelectableTypeAttribute 
    } from 'powerbi-visuals-utils-onobjectutils';
    
    const DirectEdit: powerbi.visuals.SubSelectableDirectEdit = {
        reference: {
            objectName: 'directEdit',
            propertyName: 'textProperty'
        },
        style: SubSelectableDirectEditStyle.Outline,
    };
    private visualDirectEditSubSelection = JSON.stringify(DirectEdit);
    
    this.directEditElement
                .classed('direct-edit', true)
                .classed('hidden', !this.formattingSettings.directEditSettings.show.value)
                .classed(HtmlSubSelectableClass, options.formatMode && this.formattingSettings.directEditSettings.show.value)
                .attr(SubSelectableObjectNameAttribute, 'directEdit')
                .attr(SubSelectableDisplayNameAttribute, 'Direct Edit')
                .attr(SubSelectableDirectEditAttr, this.visualDirectEditSubSelection)
    

    HTMLSubSelectionHelper 使用 SubSelectableDirectEditAttr 属性来提供 directEdit 大纲的 directEdit 引用,因此当用户双击元素时,将启动直接编辑。

    显示子选择帮助程序工作原理的屏幕截图。

  • 对于 colorSelector:

    barSelectionMerged
              .attr(SubSelectableObjectNameAttribute, 'colorSelector')
              .attr(SubSelectableDisplayNameAttribute, (dataPoint: BarChartDataPoint) => this.formattingSettings.colorSelector.slices[dataPoint.index].displayName)
              .attr(SubSelectableTypeAttribute, powerbi.visuals.SubSelectionStylesType.Shape)
              .classed(HtmlSubSelectableClass, options.formatMode)
    
    

定义引用

定义以下接口以简化示例:

注意

提供的 cardUid 应与为 getFormattingModel API 提供的 cardUid 相同。 例如,如果使用 powerbi-visuals-utils-formattingmodel,请提供 cardUid 作为 Visual-cardName-card,其中 cardName 是在格式模型设置中分配给此卡片的名称。 否则,请将其作为分配给此卡的 Visual-cardUid 提供。

interface References {
    cardUid?: string;
    groupUid?: string;
    fill?: FormattingId;
    font?: FormattingId;
    fontColor?: FormattingId;
    show?: FormattingId;
    fontFamily?: FormattingId;
    bold?: FormattingId;
    italic?: FormattingId;
    underline?: FormattingId;
    fontSize?: FormattingId;
    position?: FormattingId;
    textProperty?: FormattingId;
}

对于此示例,请为对象名称创建枚举:

const enum BarChartObjectNames {
    ColorSelector = 'colorSelector',
    DirectEdit = 'directEdit'
}
  • directEdit 对象的引用:
const directEditReferences: References = {
    cardUid: 'Visual-directEdit-card',
    groupUid: 'directEdit-group',
    fontFamily: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'fontFamily'
    },
    bold: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'bold'
    },
    italic: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'italic'
    },
    underline: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'underline'
    },
    fontSize: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'fontSize'
    },
    fontColor: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'fontColor'
    },
    show: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'show'
    },
    position: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'position'
    },
    textProperty: {
        objectName: BarChartObjectNames.DirectEdit,
        propertyName: 'textProperty'
    }
};
  • 对于 colorSelector
const colorSelectorReferences: References = {
    cardUid: 'Visual-colorSelector-card',
    groupUid: 'colorSelector-group',
    fill: {
        objectName: BarChartObjectNames.ColorSelector,
        propertyName: 'fill'
    }
};

实现 API

现在,让我们实现 onObject 格式的获取 API,并在 visualOnObjectFormatting 中提供它们:

  1. 在构造函数代码中,提供 visualOnObjectFormatting 中的 get 方法:

    public visualOnObjectFormatting: powerbi.extensibility.visual.VisualOnObjectFormatting;
    constructor(options: VisualConstructorOptions) {
            this.subSelectionHelper = HtmlSubSelectionHelper.createHtmlSubselectionHelper({
                     hostElement: options.element,
                     subSelectionService: options.host.subSelectionService,
                     selectionIdCallback: (e) => this.selectionIdCallback(e),
                });
    
     this.visualOnObjectFormatting = {
                    getSubSelectionStyles: (subSelections) => this.getSubSelectionStyles(subSelections),
                    getSubSelectionShortcuts: (subSelections, filter) => this.getSubSelectionShortcuts(subSelections, filter),
                    getSubSelectables: (filter) => this. getSubSelectables(filter)
                }
       }
    
    private getSubSelectionStyles(subSelections: CustomVisualSubSelection[]): powerbi.visuals.SubSelectionStyles | undefined {
            const visualObject = subSelections[0]?.customVisualObjects[0];
            if (visualObject) {
                switch (visualObject.objectName) {
                    case BarChartObjectNames.ColorSelector:
                        return this.getColorSelectorStyles(subSelections);
                     case BarChartObjectNames.DirectEdit:
                        return this.getDirectEditStyles();
                }
            }
        }
    
    private getSubSelectionShortcuts(subSelections: CustomVisualSubSelection[], filter: SubSelectionShortcutsKey | undefined):    VisualSubSelectionShortcuts | undefined {
            const visualObject = subSelections[0]?.  customVisualObjects[0];
            if (visualObject) {
                switch (visualObject.objectName) {
                    case BarChartObjectNames.ColorSelector:
                        return this.getColorSelectorShortcuts(subSelections);
                    case BarChartObjectNames.DirectEdit:
                        return this.getDirectEditShortcuts();
                }
            }
        }
    
  2. 实现 colorSelector 的 getSubSelection 快捷方式和样式:

    private getColorSelectorShortcuts(subSelections:  CustomVisualSubSelection[]): VisualSubSelectionShortcuts   {
            const selector = subSelections[0].customVisualObjects[0].selectionId?.getSelector();
            return [
                {
                    type: VisualShortcutType.Reset,
                    relatedResetFormattingIds: [{
                        ...colorSelectorReferences.fill,
                        selector
                    }],
                },
                {
                    type: VisualShortcutType.Navigate,
                    destinationInfo: { cardUid: colorSelectorReferences.cardUid },
                    label: 'Color'
                }
            ];
        }
    

    上述快捷方式返回上下文菜单中的相关菜单项,并添加以下功能:

    • VisualShortcutType.Navigate:当用户选择其中一个条形图(数据点),并且格式窗格处于打开状态时,格式窗格将滚动到颜色选择器卡片并打开它
    • VisualShortcutType.Reset:向上下文菜单添加重置快捷方式。 如果填充颜色已更改,则启用它。
    private getColorSelectorStyles(subSelections: CustomVisualSubSelection[]): SubSelectionStyles {
            const selector = subSelections[0].customVisualObjects[0].selectionId?.getSelector();
            return {
                type: SubSelectionStylesType.Shape,
                fill: {
                    label: 'Fill',
                    reference: {
                        ...colorSelectorReferences.fill,
                     selector
                    },
                },
            };
        }
    

当用户右键单击条形图时,将显示以下内容:

当用户右键单击栏时,用户界面的屏幕截图。

更改颜色时:

更改颜色的屏幕截图。

子节快捷方式

若要实现 directEdit 的子Selection 快捷方式和样式,

private getDirectEditShortcuts(): VisualSubSelectionShortcuts {
        return [
            {
                type: VisualShortcutType.Reset,
                relatedResetFormattingIds: [
                    directEditReferences.bold,
                    directEditReferences.fontFamily,
                    directEditReferences.fontSize,
                    directEditReferences.italic,
                    directEditReferences.underline,
                    directEditReferences.fontColor,
                    directEditReferences.textProperty
                ]
            },
            {
                type: VisualShortcutType.Toggle,
                relatedToggledFormattingIds: [{
                    ...directEditReferences.show,
                }],
                ...directEditReferences.show,
                disabledLabel: 'Delete',
            },
            {
                type: VisualShortcutType.Picker,
                ...directEditReferences.position,
                label: 'Position'
            },
            {
                type: VisualShortcutType.Navigate,
                destinationInfo: { cardUid: directEditReferences.cardUid },
                label: 'Direct edit'
            }
        ];
    }

此快捷方式在上下文菜单中添加相关菜单项,并添加以下功能:

  • VisualShortcutType.Reset:当 relatedResetFormattingIds 数组中提供的属性之一发生更改时,将重置添加到上下文菜单中的默认项。
  • VisualShortcutType.Toggle:向上下文菜单添加“删除”选项。 单击后,directEdit 卡的切换开关将关闭。
  • VisualShortcutType.Picker:在上下文菜单中添加一个选项以在向右和左之间进行选取,因为我们在 directEdit的格式卡中添加了位置切片。
  • VisualShortcutType.Navigate:当格式窗格打开并且用户选择 directEdit 元素时,格式窗格将滚动并打开 directEdit 卡。
private getDirectEditStyles(): SubSelectionStyles {
        return {
            type: powerbi.visuals.SubSelectionStylesType.Text,
            fontFamily: {
                reference: {
                    ...directEditReferences.fontFamily
                },
                label: 'font family'
            },
            bold: {
                reference: {
                    ...directEditReferences.bold
                },
                label: 'bold'
            },
            italic: {
                reference: {
                    ...directEditReferences.italic
                },
                label: 'italic'
            },
            underline: {
                reference: {
                    ...directEditReferences.underline
                },
                label: 'underline'
            },
            fontSize: {
                reference: {
                    ...directEditReferences.fontSize
                },
                label: 'font size'
            },
            fontColor: {
                reference: {
                    ...directEditReferences.fontColor
                },
                label: 'font color'
            },
            background: {
                reference: {
                    objectName: 'directEdit',
                    propertyName: 'background'
                },
                label: 'background'
            }
        }
    }

我们在 formattingSettings 中添加相关属性时提供了相关属性。

下图演示了右键单击 directEdit 元素时 UI 的外观:

直接编辑界面的屏幕截图。

地方化

视觉对象应处理本地化并提供本地化字符串。

GitHub 资源

  • 可以在on-object-formatting-api.d.ts中找到对象格式设置接口上的所有内容(在发布 API 后提供的链接)
  • 建议使用 [on object utils],其中包括 [HTMLSubSelectionHelper](发布 API 后提供的链接)
  • 可以找到使用 API 版本 5.8.0 的自定义视觉对象 SampleBarChart 的示例,并实现对对象格式的支持(发布 API 后提供的链接)