Compartilhar via


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

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

Segurança e acesso de envoltórios de propriedade

Um envoltório de propriedade CLR (Common Language Runtime) costuma ser incluído nas implementações de propriedades de dependência de leitura/gravação para simplificar a obtenção ou definição dos valores das propriedades. Caso incluído, o wrapper de propriedade CLR é um método utilitário que implementa as chamadas estáticas GetValue e SetValue que interagem com a propriedade de dependência subjacente. Essencialmente, um wrapper de propriedade CLR expõe uma propriedade de dependência como uma propriedade CLR suportada por uma propriedade de dependência, em vez de um campo privado.

Aplicar mecanismos de segurança e restringir o acesso ao wrapper de propriedade CLR pode impedir o uso do método de conveniência, mas essas medidas não impedirão invocações diretas para GetValue ou SetValue. Em outras palavras, uma propriedade de dependência de leitura/gravação é sempre acessível por meio do sistema de propriedades do WPF. Se você estiver implementando uma propriedade de dependência de leitura e gravação, evite restringir o acesso ao envoltório da propriedade CLR. Em vez disso, declare o wrapper de propriedade CLR como um membro público para que os chamadores estejam cientes do verdadeiro nível de acesso da propriedade de dependência.

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

O sistema de propriedades do WPF fornece acesso a uma propriedade de dependência de leitura e gravação através 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 aspectos do sistema de propriedades retornarão um DependencyProperty 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, você pode substituir o método virtual OnPropertyChanged para receber dados de evento que irão informar o identificador DependencyProperty para propriedades de dependência que tiveram seu valor alterado. Para conscientizar os chamadores sobre o verdadeiro nível de acesso de uma propriedade de dependência de leitura/gravação, declare seu campo de identificador como um membro público.

Nota

Embora declarar um campo 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 em Demand não é um mecanismo de segurança adequado para restringir mudanças no valor das propriedades. Além disso, a invalidação de novos valores imposta por meio de ValidateValueCallback poderá ser suprimida por chamadores mal-intencionados, se esses chamadores estiverem operando dentro do domínio do aplicativo.

Acesso a propriedades de dependência somente de leitura

Para restringir o acesso, registre sua propriedade como uma propriedade de dependência 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 do WPF fornecerá acesso de escrita apenas àqueles que têm uma referência ao DependencyPropertyKey. Para ilustrar esse comportamento, o seguinte código de teste:

  • Cria uma instância de uma classe que implementa as propriedades de dependência de leitura e escrita e de leitura somente.
  • Atribui um modificador de acesso private a cada identificador.
  • Implementa apenas acessores get.
  • Usa o método GetLocalValueEnumerator para acessar as propriedades de dependência subjacentes por meio do sistema de propriedades do WPF.
  • Chama GetValue e SetValue para testar o acesso a cada valor das propriedades 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

Consulte também