Ir para o conteúdo
ASP.NET Grade de dados AJAX: estados de caixa de seleção habilitados e desabilitados

ASP.NET Grade de dados AJAX: estados de caixa de seleção habilitados e desabilitados

Está verificado ou não? As caixas de seleção de vários estados estão no menu hoje. O que, é claro, levanta a questão "O WebDataGrid já não suporta colunas de caixa de seleção fora da caixa?".

10min read

Está verificado ou não? As caixas de seleção de vários estados estão no menu hoje. O que, é claro, levanta a questão "O WebDataGrid já não suporta colunas de caixa de seleção fora da caixa?".

Sim, ele suporta um campo de caixa de seleção de três estados, mas há casos em que você pode precisar de um pouco mais de flexibilidade. Como fomos lembrados em nossa plataforma de feedback de ideias de produtos, ter mais estados de caixa de seleção com variações ativadas e desativadas é algo que os próprios usuários podem se beneficiar diretamente. Isso ocorre em parte porque o comportamento de edição do Grid trata os campos de caixas de seleção em massa (como em toda a coluna) e talvez até porque não há nenhuma indicação visual: as caixas de seleção na coluna não são editáveis. Portanto, se você quiser mais do que as caixas de seleção padrão de três estados que podem ser configuradas para cada linha - continue lendo. Com base no feedback que recebemos, a equipe preparou uma solução e eu adicionei meu próprio giro também e quero compartilhá-lo com você.

Configuração

Há algumas coisas assumidas aqui – você deseja mostrar uma grade de dados com coluna de caixa de seleção acoplada ou não acoplada, mas também deseja usar o comportamento Edição e controlar se a caixa de seleção de cada linha é editável com base em uma condição ou em suas outras colunas de dados. Dessa forma, de três estados, em combinação com habilitado e desabilitado, vamos para seis e isso é algo que talvez você precise armazenar em mais de uma coluna. Dependendo dos seus dados e caso essa coluna extra possa ser usada como sinalizador, o tempo ou não, a caixa de seleção deve estar ativa ou todo o estado. A implementação de algo assim pode ser muito diferente, portanto, pode ser necessário um pouco de adaptação, mas tentarei apontar os fundamentos que são reutilizáveis e marcar recursos opcionais no código de demonstração.

Basics

A ideia por trás da adição de um estado desativado é bastante inteligente - substitua a imagem da caixa de seleção pela respectiva imagem inativa e execute a verificação de tempo de execução dessas imagens para evitar a edição. Dessa forma, você obtém controle sobre a indicação visual, mas também sobre os detalhes da imagem que pode usar para detecção. Portanto, você precisa de 3 imagens, juntamente com um objeto de configurações para o lado do cliente com caminhos e valores para elas e mapeamento entre a caixa de seleção e suas células de estado ocultas.

Eventos de cliente a serem manipulados:

  • Inicializar– passe pelas caixas de seleção que têm estado extra e aplique a aparência ativada/desativada
  • Manejar Edição 's Mudança de Valor de Célula para evitar a edição desativada. Lembre-se também de que o evento é acionado ao usar a API, portanto, se você ainda quiser alterar os valores programaticamente no lado do cliente, talvez seja necessário usar o comando"_set_value_internal" em vez disso.
  • Se você estiver usando caixas de seleção não acopladas com uma caixa de cabeçalho, também deverá manipular o HeaderCheckBoxClicked para definir o estado adequado e talvez o valor.

Você pode usar campos vinculados de seus dados atuais, adicionar novos para fazer isso se não estiverem disponíveis, usar campos não vinculados com o único propósito de manter sinalizadores ou até mesmo habilitar e desabilitar caixas de seleção com base na lógica executada no cliente.

External Flag Approach

Isso usa a funcionalidade de caixa de seleção padrão junto com a aplicação de estados editáveis com base em um sinalizador – que pode ser outra coluna de dados, predeterminada pela lógica do servidor e transportada como campo (des)associado ou completamente com base na lógica do lado do cliente. A implementação depende realmente de você, usarei outra coluna bool como parte dos dados - e como tenho duas colunas de caixa de seleção, os dois últimos campos carregam sinalizadores desabilitados para eles:

<ig:WebDataGrid ID="WebDataGrid1" runat="server" Height="350px" Width="100%" AutoGenerateColumns="False" DataKeyFields="ID" StyleSetName="IG">
    <Columns>
        <ig:BoundDataField DataFieldName="ID" Key="ID">
            <Header Text="ID" />
        </ig:BoundDataField>
        <ig:BoundDataField DataFieldName="CheckBoxState" Key="CheckBoxState">
            <Header Text="CheckBox State" />
        </ig:BoundDataField>                
        <ig:BoundCheckBoxField DataFieldName="BoundCheckBox" Key="BoundCheckBox" DataType="System.Boolean">
            <Header Text="Bound CheckBox" />
        </ig:BoundCheckBoxField>
        <ig:BoundCheckBoxField Key="DisableBoundCheckBox">
            <Header Text="Check to disable corresponding BoundCheckBox" />
        </ig:BoundCheckBoxField>                
        <ig:UnboundCheckBoxField Key="UnboundCheckBox" HeaderCheckBoxMode="TriState">
            <Header Text="UnboundCheckBox"/>
        </ig:UnboundCheckBoxField>   
        <ig:UnboundCheckBoxField Key="DisableUnboundCheckBox" HeaderCheckBoxMode="TriState">
            <Header Text="Check to disable corresponding UnboundCheckBox" />
        </ig:UnboundCheckBoxField>
        <%-- Hidden columns to store the states for the CheckBoxFields --%>   
        <ig:BoundDataField Key="BoundCheckBoxDisabled" Hidden="true" DataType="System.Boolean">
            <Header Text="Bound CheckBox Disabled?" />
        </ig:BoundDataField>
        <ig:BoundDataField Key="UnboundCheckBoxDisabled" Hidden="true" DataType="System.Boolean">
            <Header Text="Unbound CheckBox Disabled?" />
        </ig:BoundDataField>                      
    </Columns>
    <ClientEvents Initialize="WebDataGrid_Grid_Initialize" HeaderCheckBoxClicked="WebDataGrid_Grid_HeaderCheckboxClicked" />
    <Behaviors>
        <ig:EditingCore>
            <EditingClientEvents CellValueChanging="WebDataGrid_Editing_CellValueChanging" CellValueChanged="WebDataGrid_Editing_CellValueChanged" />
            <Behaviors>
                <ig:CellEditing>
                        <ColumnSettings>
                            <ig:EditingColumnSetting ColumnKey="ID" ReadOnly="True" />
                        </ColumnSettings>
                        <EditModeActions MouseClick="Single" EnableOnKeyPress="true"/>
                </ig:CellEditing>
            </Behaviors>
        </ig:EditingCore>
    </Behaviors>
</ig:WebDataGrid>

O estado inicial com base nos valores desses é estabelecido e as imagens da caixa de seleção desativadas substituem os padrões quando necessário:

var styleName = "<%= this.WebDataGrid1.StyleSetName %>" || "Default";
var clientSettings = {
    "true": { src: "images/" + styleName + "/ig_checkbox_disabled_on.gif", value: true, chkState: 1 },
    "false": {src: "images/" + styleName + "/ig_checkbox_disabled_off.gif", value: false, chkState: 0 },
    "null": { src: "images/" + styleName + "/ig_checkbox_disabled_partial.gif", value: null, chkState: 2 },
    "columns": {2: 6, 4: 7}           
};
 
function WebDataGrid_Grid_Initialize(sender, eventArgs) {
    // Set initial enabled / disabled state for all checkboxes            
    try {
        var rows = sender.get_rows();
        var rowCount = rows.get_length();
        for (var i = 0; i < rowCount; i++) {
            var row = rows.get_row(i);
            for (columnIndex in clientSettings["columns"]){
                // Set the correct enabled/disabled states:
                var checkBoxCell = row.get_cell(columnIndex);
                var hiddenStateCell = row.get_cell(clientSettings["columns"][columnIndex]);
                var isDisabled = hiddenStateCell.get_value();
                SetCheckState(checkBoxCell, isDisabled, hiddenStateCell);
            }
        }
    }
    catch (ex) { }
}
 
function SetCheckState(cell, isDisabled, checkStateCell) {
    // Set Enabled or disabled state to a cell (adjust image and title/alt) and store in secondary cell.
    try {
        if (isDisabled === isCellDisabled(cell)) return;
        var checkBoxElem = cell.get_element().getElementsByTagName("img").item(0); //get checkbox image
        var chkStates = clientSettings[String(cell.get_value())]; //get state info
        if (!chkStates) return;
                
        if (isDisabled === true) {
            // Set the disabled values of src and title for the checkbox
            checkBoxElem.src = chkStates.src;
            checkBoxElem.title = "Disabled " + checkBoxElem.title;
            checkBoxElem.alt = "Disabled " + checkBoxElem.alt;
        }
        else {
            // re-set value to restore state
            checkBoxElem.src = "";
            //use _internal to force setting the same value
            cell._set_value_internal(chkStates.value, chkStates.chkState);
        }
        checkStateCell.set_value(isDisabled); // Store the new checkbox state value in the corresponding hidden checkbox
    }
    catch (ex) { }
}
 
function isCellDisabled(cell) {
    var checkBoxElem = cell.get_element().getElementsByTagName("img").item(0);
    return checkBoxElem && checkBoxElem.src.toLowerCase().indexOf("disabled") >= 0;
}

Impedir edições em caixas de seleção desativadas e para campos não vinculados preserva o estado quando a caixa de cabeçalho é usada:

function WebDataGrid_Editing_CellValueChanging(sender, eventArgs) {
    // Prevent edit actions on disabled checkboxes
    try {
        var currCell = eventArgs.get_cell();
        if (isCellDisabled(currCell))
            eventArgs.set_cancel(true); // cancel event to prevent value change
    }
    catch (ex) { }
}
 
function WebDataGrid_Grid_HeaderCheckboxClicked(sender, eventArgs) {
    var columnIndex = eventArgs.get_column().get_index();
    var rows = sender.get_rows();
    var rowCount = rows.get_length();
 
    // When the header of the "UnboundCheckBox" column is clicked,
    // set the check states for all while maintaining enabled/disabled states
    if (clientSettings["columns"][columnIndex]) {
        for (var i = 0; i < rowCount; i++) {
            var row = rows.get_row(i);
            var unboundCheckBoxCell = row.get_cell(columnIndex);
            var hiddenUnboundCheckBoxCell = unboundCheckBoxCell.get_row().get_cell(clientSettings["columns"][columnIndex]);
            var unboundCheckBoxCellVal = hiddenUnboundCheckBoxCell.get_value();
            SetCheckState(unboundCheckBoxCell, unboundCheckBoxCellVal, hiddenUnboundCheckBoxCell);
        }
    }
}

Obviamente, manter o estado dos campos não vinculados é tão opcional quanto a própria caixa de cabeçalho – é uma questão do que você pode precisar. Neste ponto, você pode usar a função SetCheckState para alternar o estado e fornecer ao usuário várias maneiras de fazer isso, se necessário (consulte o código de demonstração que manipula alterações de valor de campos de caixa de seleção padrão vizinhos).

Estados de caixa de seleção habilitados e desabilitados com base na coluna secundária 'sinalizar' (dois últimos) que ficariam ocultos do usuário.

P.S. Se você quiser uma melhor legibilidade para pares de colunas nas configurações do cliente, você pode armazenar chaves e usar o método "get_cellByColumnKey" onde eu fui para índices - porque você conhece ambos no servidor, e o método de chave irá usá-lo para encontrar o índice de qualquer maneira.

Abordagem tudo-em-um

Este é o único se você precisar manter tudo em uma única célula de dados. A abordagem tudo-em-um vai para uma única coluna de dados que contém o estado completo na caixa de seleção como uma string, assim como você vê nas capturas de tela - "EnabledOn", "EnabledOff" e assim por diante. Portanto, você precisa mais uma vez de duas colunas, uma caixa de seleção e outra oculta que carrega o estado real. Indique um conjunto semelhante de manipuladores de eventos para fornecer aparência e comportamento do lado do cliente e você terá uma solução igualmente funcional.

A marcação de grade, com variações para campos vinculados e não vinculados obrigatórios, é essencialmente a mesma, assim como a 'desativação' das caixas de seleção, verificando sua fonte de imagem e cancelando a edição e a inicialização do tratamento de eventos. A principal diferença vem do fato de que a configuração do estado lida e salva os valores completos da string:

// Sets the enabled/disabled state on the bound/unbound checkbox in the given cell
function SetCheckState(cell, chkState, checkStateCell) {
    try {
        var checkState = chkState? chkState.replace("Enabled", "Disabled"): "";
        var checkBoxElem = cell.get_element().getElementsByTagName("img").item(0);
        var chkStates = checkStates[checkState];
        if (!chkStates) return;
 
        // Get the default values of src and title for checkbox in its enabled state
        var enabledSrc = null, enabledTitle = null;
        if (chkState && chkState.indexOf("Enabled") >= 0) {
            enabledSrc = "ig_res/Default/" + chkStates.src.replace("disabled_", "");
            enabledTitle = chkState.replace("Enabled", "").replace("On", "Checked").replace("Off", "Unchecked");
        }
 
        cell.set_value(chkStates.value, chkStates.chkState);
        checkBoxElem.src = enabledSrc? enabledSrc : chkStates.src;
        checkBoxElem.title = enabledTitle ? enabledTitle : chkStates.title;
        checkBoxElem.alt = enabledTitle ? enabledTitle : chkStates.alt;
        checkStateCell.set_value(chkState); // Store the new checkbox state value in the corresponding hidden checkbox
    }
    catch (ex) { }
}
Estados de caixa de seleção habilitados e desabilitados com base em uma coluna oculta contendo valores de string e lógica aplicando-os às caixas de seleção

Bonus round

É claro que, quando menciono que você precisa de duas colunas para essa abordagem novamente, quero dizer que nem sempre será eficaz vincular uma coluna de caixa de seleção a valores de cadeia de caracteres como "DisabledOn". Isso não significa que você não possa fazer isso, no entanto. Usando a interface IBooleanConverter, você pode vincular o campo da caixa de seleção (isso naturalmente exclui opções não vinculadas) e, na verdade, facilita o código do cliente necessário também, porque você não precisará da coluna oculta extra.

public class CheckboxConverter : IBooleanConverter {
  public object DefaultFalseValue {
    get {
      return "EnabledOff";
    }
  }

  public object DefaultTrueValue {
    get {
      return "EnabledOn";
    }
  }

  public bool IsFalse(object value) {
    return value.ToString().Contains("Off");
  }

  public bool IsTrue(object value) {
    return value.ToString().Contains("On");
  }
}

Um bônus do manuseio do conversor é que, quando as caixas de seleção editáveis definem os valores padrão verdadeiro/falso quando o usuário interage com elas e usa as regras para corresponder a marcados ou desmarcados e quando nada corresponde (leia-se: "EnabledPartial"), ele também definirá a caixa de seleção para o estado parcial. Quão conveniente!

Options, Options, Options

Eu acho que é exatamente isso que este blog pode lhe dar - 3 maneiras de obter a funcionalidade de caixa de seleção de estado ativado e desativado múltiplo.  Se você deseja duas colunas booleanas que fornecem um estado combinado ou uma abordagem de coluna única com par de colunas ocultas e de apresentação ou campos de ligação por meio de um conversor - todas as versões produzem um comportamento semelhante com diferentes quantidades de esforço e limitações. Cabe a você decidir qual é o mais adequado para suas necessidades e reutilizar o máximo possível do código de demonstração!

Falando em código, aqui estão os sites de demonstração ASP.NET:

Abra como Site do Visual Studio ou WebMatrix e você precisará Infragistics ASP.NET instalado (pode precisar de ajustes de versão de referência) que você pode pegar aqui.

Eu adoraria ouvir seus pensamentos, então deixe um comentário abaixo ou me envie um ping @DamyanPetev.

E como sempre, você pode nos seguir no Twitter@ Infragisticse manter contato noFacebook,Google+eLinkedIn!

Solicite uma demonstração