Partilhar via


Segurança de propriedade de dependência (WPF .NET)

O acesso às propriedades de dependência de leitura-gravação por meio do sistema de propriedades do Windows Presentation Foundation (WPF) torna-as efetivamente propriedades públicas. Como resultado, não é possível garantir a segurança dos valores das propriedades de dependência de leitura e gravação. O sistema de propriedades WPF fornece mais segurança para propriedades de dependência somente leitura, para que seja possível restringir o acesso de gravação.

Acesso e segurança dos envoltórios de propriedade

Um wrapper de propriedade de Common Language Runtime (CLR) é geralmente incluído em implementações de propriedades de dependência para leitura e escrita, para simplificar a obtenção ou configuração de valores de propriedade. Se estiver incluído, o wrapper de propriedade CLR é um método de conveniência que implementa as chamadas estáticas GetValue e SetValue que interagem com a propriedade de dependência subjacente. Essencialmente, um invólucro de propriedade CLR expõe uma propriedade de dependência sob a forma de uma propriedade CLR que é suportada por outra propriedade de dependência, em vez de por um campo privado.

A aplicação de mecanismos de segurança e a restrição de acesso ao wrapper de propriedade CLR podem impedir o uso do método de conveniência, mas essas técnicas não impedirão chamadas diretas para GetValue ou SetValue. Em outras palavras, uma propriedade de dependência de leitura-gravação é sempre acessível através do sistema de propriedades do WPF. Se estiver a implementar uma propriedade de dependência de leitura-escrita, evite restringir o acesso ao invólucro da propriedade CLR. Em vez disso, declare o encapsulador da propriedade CLR como um membro público, para que quem fizer chamadas esteja ciente do verdadeiro nível de acesso da propriedade de dependência.

Exposição do sistema de propriedade de propriedades de dependência

O sistema de propriedades WPF fornece acesso a uma propriedade de dependência de leitura-gravação por meio de seu identificador DependencyProperty. O identificador é utilizável em chamadas GetValue e SetValue. Mesmo que o campo identificador estático não seja público, vários aspetos do sistema de propriedades retornarão um DependencyProperty tal como ele existe em uma instância de uma classe ou classe derivada. Por exemplo, o método GetLocalValueEnumerator retorna identificadores para instâncias de propriedade de dependência com um valor definido localmente. Além disso, pode-se substituir o método virtual OnPropertyChanged para receber dados de eventos que indicarão o identificador DependencyProperty para propriedades de dependência que alteraram o valor. Para informar os chamadores sobre o nível de acesso verdadeiro de uma propriedade de dependência de leitura e escrita, declare o seu campo de identificador como um membro público.

Observação

Embora declarar um campo de identificador de propriedade de dependência como private reduza o número de maneiras pelas quais uma propriedade de dependência de leitura-gravação é acessível, a propriedade não será privada de acordo com a definição de linguagem CLR.

Segurança de validação

Aplicar um Demand a um ValidateValueCallback e esperar que a validação falhe em caso de falha Demand não é um mecanismo de segurança adequado para restringir as alterações de valor de propriedade. Além disso, a nova invalidação de valor imposta por meio de ValidateValueCallback pode ser suprimida por chamadores mal-intencionados, se esses chamadores estiverem operando dentro do domínio do aplicativo.

Acesso a propriedades de dependência somente leitura

Para restringir o acesso, registe a sua propriedade como uma propriedade dependente somente leitura chamando o método RegisterReadOnly. O método RegisterReadOnly retorna um DependencyPropertyKey, que você pode atribuir a um campo de classe não público. Para propriedades de dependência somente leitura, o sistema de propriedades WPF apenas fornecerá acesso de escrita para aqueles que têm uma referência ao DependencyPropertyKey. Para ilustrar esse comportamento, o seguinte código de teste:

  • Instancia uma classe que implementa propriedades de dependência de leitura-gravação e somente leitura.
  • Atribui um modificador de acesso private a cada identificador.
  • Implementa apenas os acessores get.
  • Usa o método GetLocalValueEnumerator para acessar as propriedades de dependência subjacentes por meio do sistema de propriedades WPF.
  • Chama GetValue e SetValue para testar o acesso a cada valor de propriedade de dependência.
    /// <summary>
    ///  Test get/set access to dependency properties exposed through the WPF property system.
    /// </summary>
    public static void DependencyPropertyAccessTests()
    {
        // Instantiate a class that implements read-write and read-only dependency properties.
        Aquarium _aquarium = new();
        // Access each dependency property using the LocalValueEnumerator method.
        LocalValueEnumerator localValueEnumerator = _aquarium.GetLocalValueEnumerator();
        while (localValueEnumerator.MoveNext())
        {
            DependencyProperty dp = localValueEnumerator.Current.Property;
            string dpType = dp.ReadOnly ? "read-only" : "read-write";
            // Test read access.
            Debug.WriteLine($"Attempting to get a {dpType} dependency property value...");
            Debug.WriteLine($"Value ({dpType}): {(int)_aquarium.GetValue(dp)}");
            // Test write access.
            try
            {
                Debug.WriteLine($"Attempting to set a {dpType} dependency property value to 2...");
                _aquarium.SetValue(dp, 2);
            }
            catch (InvalidOperationException e)
            {
                Debug.WriteLine(e.Message);
            }
            finally
            {
                Debug.WriteLine($"Value ({dpType}): {(int)_aquarium.GetValue(dp)}");
            }
        }

        // Test output:

        // Attempting to get a read-write dependency property value...
        // Value (read-write): 1
        // Attempting to set a read-write dependency property value to 2...
        // Value (read-write): 2

        // Attempting to get a read-only dependency property value...
        // Value (read-only): 1
        // Attempting to set a read-only dependency property value to 2...
        // 'FishCountReadOnly' property was registered as read-only
        // and cannot be modified without an authorization key.
        // Value (read-only): 1
    }
}

public class Aquarium : DependencyObject
{
    public Aquarium()
    {
        // Assign locally-set values.
        SetValue(FishCountProperty, 1);
        SetValue(FishCountReadOnlyPropertyKey, 1);
    }

    // Failed attempt to restrict write-access by assigning the
    // DependencyProperty identifier to a non-public field.
    private static readonly DependencyProperty FishCountProperty =
        DependencyProperty.Register(
          name: "FishCount",
          propertyType: typeof(int),
          ownerType: typeof(Aquarium),
          typeMetadata: new PropertyMetadata());

    // Successful attempt to restrict write-access by assigning the
    // DependencyPropertyKey to a non-public field.
    private static readonly DependencyPropertyKey FishCountReadOnlyPropertyKey =
        DependencyProperty.RegisterReadOnly(
          name: "FishCountReadOnly",
          propertyType: typeof(int),
          ownerType: typeof(Aquarium),
          typeMetadata: new PropertyMetadata());

    // Declare public get accessors.
    public int FishCount => (int)GetValue(FishCountProperty);
    public int FishCountReadOnly => (int)GetValue(FishCountReadOnlyPropertyKey.DependencyProperty);
}
    ''' <summary>
    ''' ' Test get/set access to dependency properties exposed through the WPF property system.
    ''' </summary>
    Public Shared Sub DependencyPropertyAccessTests()
        ' Instantiate a class that implements read-write and read-only dependency properties.
        Dim _aquarium As New Aquarium()
        ' Access each dependency property using the LocalValueEnumerator method.
        Dim localValueEnumerator As LocalValueEnumerator = _aquarium.GetLocalValueEnumerator()
        While localValueEnumerator.MoveNext()
            Dim dp As DependencyProperty = localValueEnumerator.Current.[Property]
            Dim dpType As String = If(dp.[ReadOnly], "read-only", "read-write")
            ' Test read access.
            Debug.WriteLine($"Attempting to get a {dpType} dependency property value...")
            Debug.WriteLine($"Value ({dpType}): {CInt(_aquarium.GetValue(dp))}")
            ' Test write access.
            Try
                Debug.WriteLine($"Attempting to set a {dpType} dependency property value to 2...")
                _aquarium.SetValue(dp, 2)
            Catch e As InvalidOperationException
                Debug.WriteLine(e.Message)
            Finally
                Debug.WriteLine($"Value ({dpType}): {CInt(_aquarium.GetValue(dp))}")
            End Try
        End While

        ' Test output

        ' Attempting to get a read-write dependency property value...
        ' Value (read-write): 1
        ' Attempting to set a read-write dependency property value to 2...
        ' Value (read-write): 2

        ' Attempting to get a read-only dependency property value...
        ' Value (read-only): 1
        ' Attempting to set a read-only dependency property value to 2...
        ' 'FishCountReadOnly' property was registered as read-only
        ' and cannot be modified without an authorization key.
        ' Value (read-only): 1
    End Sub

End Class

Public Class Aquarium
    Inherits DependencyObject

    Public Sub New()
        ' Assign locally-set values.
        SetValue(FishCountProperty, 1)
        SetValue(FishCountReadOnlyPropertyKey, 1)
    End Sub

    ' Failed attempt to restrict write-access by assigning the
    ' DependencyProperty identifier to a non-public field.
    Private Shared ReadOnly FishCountProperty As DependencyProperty =
        DependencyProperty.Register(
            name:="FishCount",
            propertyType:=GetType(Integer),
            ownerType:=GetType(Aquarium),
            typeMetadata:=New PropertyMetadata())

    ' Successful attempt to restrict write-access by assigning the
    ' DependencyPropertyKey to a non-public field.
    Private Shared ReadOnly FishCountReadOnlyPropertyKey As DependencyPropertyKey =
        DependencyProperty.RegisterReadOnly(
            name:="FishCountReadOnly",
            propertyType:=GetType(Integer),
            ownerType:=GetType(Aquarium),
            typeMetadata:=New PropertyMetadata())

    ' Declare public get accessors.
    Public ReadOnly Property FishCount As Integer
        Get
            Return GetValue(FishCountProperty)
        End Get
    End Property

    Public ReadOnly Property FishCountReadOnly As Integer
        Get
            Return GetValue(FishCountReadOnlyPropertyKey.DependencyProperty)
        End Get
    End Property

End Class

Ver também