Integration Signature with Toolbar

8 Mar 202218 minutes to read

The Signature component integrates with the toolbar and the interaction performed using the Changed event of the toolbar. In that, CanUndoAsync, CanRedoAsync and IsEmptyAsync methods were used to enable/disable undo, redo, and clear buttons by checking the undo collection.

@using Syncfusion.Blazor.Inputs
@using Syncfusion.Blazor.DropDowns
@using Syncfusion.Blazor.Buttons
@using Syncfusion.Blazor.Navigations
@using Syncfusion.Blazor.SplitButtons

<div class="control-section">
    <div id="signature-toolbar-control">
        <SfToolbar ID="toolbar">
            <ToolbarItems>
                <ToolbarItem Type="@ItemType.Button" TooltipText="Undo (Ctrl + Z)">
                    <Template>
                        <SfButton Disabled="undoDisabled" @ref="undoButton" IconCss="e-icons e-undo" @onclick="@onUndo" Content="Undo" />
                    </Template>
                </ToolbarItem>
                <ToolbarItem Type="@ItemType.Button" TooltipText="Redo (Ctrl + Y)">
                    <Template>
                        <SfButton Disabled="redoDisabled" @ref="redoButton" IconCss="e-icons e-redo" @onclick="@onRedo" Content="Redo" />
                    </Template>
                </ToolbarItem>
                <ToolbarItem Type="@ItemType.Separator" />
                <ToolbarItem Type="@ItemType.Button" TooltipText="save">
                    <Template>
                        <SfSplitButton Content="Save" IconCss="e-sign-icons e-save" Disabled="saveDisabled">
                            <SplitButtonEvents Clicked="onSaveClicked" ItemSelected="onSaveType" />
                            <DropDownMenuItems>
                                <DropDownMenuItem Text="PNG" />
                                <DropDownMenuItem Text="JPEG" />
                                <DropDownMenuItem Text="SVG" />
                            </DropDownMenuItems>
                        </SfSplitButton>
                    </Template>
                </ToolbarItem>
                <ToolbarItem Type="@ItemType.Separator" />
                <ToolbarItem Type="@ItemType.Button" TooltipText="Stroke Color">
                    <Template>
                        <SfColorPicker Mode="ColorPickerMode.Palette" CssClass="circle-palette" Value="@strokeColor" ShowButtons="false" ModeSwitcher="false" Columns="4" PresetColors="@circlePaletteColors" ValueChange="@onStrokeColor" />
                    </Template>
                </ToolbarItem>
                <ToolbarItem Type="@ItemType.Separator" />
                <ToolbarItem Type="@ItemType.Button" TooltipText="Background Color">
                    <Template>
                        <SfColorPicker CssClass="circle-palette e-bg-color" NoColor="true" Mode="ColorPickerMode.Palette" Value="@backgroundColor" ShowButtons="false" ModeSwitcher="false" Columns="4" PresetColors="@circlePaletteColors1" ValueChange="@onBgColor" />
                    </Template>
                </ToolbarItem>
                <ToolbarItem Type="@ItemType.Separator" />
                <ToolbarItem Type="@ItemType.Input" TooltipText="Stroke Width">
                    <Template>
                        <SfDropDownList TItem="double" TValue="double" Width="60px" Value="@maxStrokeWidth" DataSource="@data">
                            <DropDownListEvents TItem="double" TValue="double" ValueChange="@ValueChangeHandler" />
                        </SfDropDownList>
                    </Template>
                </ToolbarItem>
                <ToolbarItem Type="@ItemType.Separator" />
                <ToolbarItem Type="@ItemType.Button" TooltipText="Clear">
                    <Template>
                        <SfButton Disabled="clearDisabled" @ref="clearButton" IconCss="e-sign-icons e-clear" @onclick="@onClear" Content="Clear" />
                    </Template>
                </ToolbarItem>
                <ToolbarItem Type="ItemType.Input" Align="ItemAlign.Right" TooltipText="Disabled">
                    <Template>
                        <SfCheckBox Label="Disabled" ValueChange="@onDisabled" TChecked="bool" />
                    </Template>
                </ToolbarItem>
            </ToolbarItems>
        </SfToolbar>
        <div id="signature-control">
            <SfSignature id="signature" @ref="signature" style=" width: 100%; height: 100%;" BackgroundColor="@backgroundColor" StrokeColor="@strokeColor"
                         Disabled="@disabled" MaxStrokeWidth="@maxStrokeWidth" Changed="SignChanged" />
        </div>
    </div>
</div>
@code{
    private SfSignature signature;
    private string backgroundColor = "#ffffff";
    private string strokeColor = "#000000";
    private double maxStrokeWidth = 2;
    private bool disabled = false;
    private SfButton clearButton;
    private SfButton undoButton;
    private SfButton redoButton;
    private bool redoDisabled = true;
    private bool undoDisabled = true;
    private bool clearDisabled = true;
    private bool saveDisabled = true;
    private SignatureFileType type = SignatureFileType.Png;
    private Dictionary<string, string[]> circlePaletteColors = new Dictionary<string, string[]>() {
        { "Custom", new string[] {"#000000", "#e91e63", "#9c27b0", "#673ab7", "#2196f3", "#03a9f4", "#00bcd4",
        "#009688", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107" } }
    };
    private Dictionary<string, string[]> circlePaletteColors1 = new Dictionary<string, string[]>() {
        { "Custom", new string[] {"#ffffff", "#f44336", "#e91e63", "#9c27b0", "#673ab7", "#2196f3", "#03a9f4", "#00bcd4",
        "#009688", "#8bc34a", "#cddc39", "#ffeb3b" } }
    };
    public double[] data = new double[] { 1, 2, 3, 4, 5 };
    private async Task SignChanged()
    {
        await updateSaveClear();
        await updateUndoRedo();
    }
    private void ValueChangeHandler(Syncfusion.Blazor.DropDowns.ChangeEventArgs<double, double> args)
    {
        maxStrokeWidth = args.Value;
    }
    private async Task onUndo()
    {
        bool canUndo = await signature.CanUndoAsync();
        if (canUndo)
        {
            await signature.UndoAsync();
            await updateUndoRedo();
            await updateSaveClear();
        }
    }
    private async Task onRedo()
    {
        bool canRedo = await signature.CanRedoAsync();
        if (canRedo)
        {
            await signature.RedoAsync();
            await updateUndoRedo();
            await updateSaveClear();
        }
    }
    private async Task updateUndoRedo()
    {
        bool canUndo = await signature.CanUndoAsync();
        bool canRedo = await signature.CanRedoAsync();
        redoDisabled = !canRedo;
        undoDisabled = !canUndo;
    }
    private async Task updateSaveClear()
    {
        bool isEmpty = await signature.IsEmptyAsync();
        if (isEmpty)
        {
            saveDisabled = true;
            clearDisabled = true;
        }
        else
        {
            saveDisabled = false;
            clearDisabled = false;
        }
    }
    private async Task onClear()
    {
        await signature.ClearAsync();
        await updateSaveClear();
    }
    private void onBgColor(ColorPickerEventArgs args)
    {
        backgroundColor = args.CurrentValue.Hex;
    }
    private void onStrokeColor(ColorPickerEventArgs args)
    {
        strokeColor = args.CurrentValue.Hex;
    }
    private void onDisabled(Syncfusion.Blazor.Buttons.ChangeEventArgs<bool> args)
    {
        disabled = args.Checked;
    }
    private void onSaveClicked(Syncfusion.Blazor.SplitButtons.ClickEventArgs args)
    {
        signature.SaveAsync();
    }
    private void onSaveType(MenuEventArgs args)
    {
        switch (args.Item.Text)
        {
            case "PNG":
                type = SignatureFileType.Png;
                break;
            case "JPEG":
                type = SignatureFileType.Jpeg;
                break;
            case "SVG":
                type = SignatureFileType.Svg;
                break;
        }
        signature.SaveAsync(type, "Signature");
    }
}
<style>
    @@font-face {
        font-family: 'font-icons';
        src: url(data:application/x-font-ttf;charset=utf-8;base64,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) format('truetype');
        font-weight: normal;
        font-style: normal;
    }

    .e-sign-icons {
        font-family: 'font-icons' !important;
        font-size: 55px;
        font-style: normal;
        font-weight: normal;
        font-variant: normal;
        text-transform: none;
        line-height: 1;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    #signature-toolbar-control .e-clear::before {
        content: '\e706';
    }

    #signature-toolbar-control .e-save::before {
        content: '\e701';
    }

    #signature-toolbar-control {
        border: 1px solid lightgray;
    }

    .highcontrast #signature-toolbar-control {
        border: 1px solid white;
    }

    #signature-toolbar-control #toolbar {
        border: none;
        border-bottom: 1px solid lightgray;
        box-sizing: border-box;
    }

    #signature-toolbar-control #toolbar {
        height: 44px !important;
    }

    .e-bigger #signature-toolbar-control #toolbar {
        height: 54px !important;
    }

    .e-bigger .material #signature-toolbar-control div#toolbar,
    .e-bigger .material-dark #signature-toolbar-control div#toolbar {
        height: 57px !important;
    }

    .e-bigger .bootstrap4 #signature-toolbar-control div#toolbar,
    .e-bigger .bootstrap4-dark #signature-toolbar-control div#toolbar {
        height: 65px !important;
    }

    .bootstrap4 #signature-toolbar-control div#toolbar,
    .bootstrap4-dark #signature-toolbar-control div#toolbar,
    .bootstrap-dark #signature-toolbar-control div#toolbar,
    .bootstrap #signature-toolbar-control div#toolba {
        height: 49px !important;
    }

    .bootstrap5 #signature-toolbar-control #toolbar .e-split-btn,
    .bootstrap5 #signature-toolbar-control #toolbar .e-dropdown-btn {
        color: #6c757d
    }

    .bootstrap4 #signature-toolbar-control #toolbar .e-split-btn,
    .bootstrap4 #signature-toolbar-control #toolbar .e-dropdown-btn {
        color: #495057;
    }

    .highcontrast #signature-toolbar-control #toolbar .e-split-btn,
    .highcontrast #signature-toolbar-control #toolbar .e-dropdown-btn {
        color: #fff;
    }

    .fabric #signature-toolbar-control #toolbar .e-split-btn,
    .fabric #signature-toolbar-control #toolbar .e-dropdown-btn,
    .bootstrap5 #signature-toolbar-control #toolbar .e-split-btn,
    .bootstrap5 #signature-toolbar-control #toolbar .e-dropdown-btn,
    .bootstrap4 #signature-toolbar-control #toolbar .e-split-btn,
    .bootstrap4 #signature-toolbar-control #toolbar .e-dropdown-btn {
        background-color: transparent !important;
        border-color: lightgray;
    }

    .fabric-dark #signature-toolbar-control #toolbar .e-dropdown-btn,
    .fabric-dark #signature-toolbar-control #toolbar .e-split-btn,
    .bootstrap5-dark #signature-toolbar-control #toolbar .e-split-btn,
    .bootstrap5-dark #signature-toolbar-control #toolbar .e-dropdown-btn,
    .bootstrap4-dark #signature-toolbar-control #toolbar .e-split-btn,
    .bootstrap4-dark #signature-toolbar-control #toolbar .e-dropdown-btn {
        background-color: transparent !important;
        border-color: gray;
    }

    #signature-toolbar-control .e-btn:disabled {
        opacity: 0.5 !important;
        pointer-events: none;
    }

    #signature-toolbar-control #signature-control {
        height: 300px;
        width: 100%;
        margin: 0;
    }

    #signature-toolbar-control #signature {
        border: none !important;
    }

    .circle-palette .e-container {
        background-color: transparent;
        border-color: transparent;
        width: 160px;
    }

    .circle-palette .e-container .e-custom-palette.e-palette-group {
        height: 182px;
    }

    .circle-palette .e-container .e-palette .e-tile {
        border: 0;
        color: #fff;
        height: 32px;
        font-size: 18px;
        width: 32px;
        line-height: 36px;
        border-radius: 50%;
        margin: 4px;
        font-family: "e-icons";
        font-style: normal;
        font-variant: normal;
        font-weight: normal;
        text-transform: none;
    }

    .circle-palette .e-container .e-palette .e-tile.e-selected::before {
        content: '\e933';
    }

    .circle-palette .e-container .e-palette .e-tile.e-selected {
        outline: none;
    }

    .circle-palette .e-split-colorpicker .e-selected-color .e-split-preview {
        border: 1px solid lightgray;
    }

    .e-container .e-custom-palette .e-palette {
        padding: 0px;
    }

    .e-bg-color .e-circle-palette.e-nocolor-item.e-selected .e-circle-selection {
        background: transparent;
    }

    .e-bg-color .e-circle-palette.e-nocolor-item.e-selected {
        border: 3px solid lightgray;
    }
</style>

Blazor Signature Component