Vinculação à propriedade estática


168

Estou tendo dificuldade para vincular uma propriedade de cadeia estática simples a um TextBox.

Aqui está a classe com a propriedade estática:

public class VersionManager
{
    private static string filterString;

    public static string FilterString
    {
        get { return filterString; }
        set { filterString = value; }
    }
}

No meu xaml, eu só quero vincular essa propriedade estática a um TextBox:

<TextBox>
    <TextBox.Text>
        <Binding Source="{x:Static local:VersionManager.FilterString}"/>
    </TextBox.Text>
</TextBox>

Tudo é compilado, mas em tempo de execução, recebo a seguinte exceção:

Não é possível converter o valor no atributo 'Origem' para o objeto do tipo 'System.Windows.Markup.StaticExtension'. Erro no objeto 'System.Windows.Data.Binding' no arquivo de marcação 'BurnDisk; component / selectversionpagefunction.xaml' Linha 57 Posição 29.

Alguma idéia do que estou fazendo de errado?

Respostas:


168

Se a ligação precisar ser bidirecional, você deverá fornecer um caminho. Há um truque para fazer a ligação bidirecional em uma propriedade estática, desde que a classe não seja estática: declare uma instância fictícia da classe nos recursos e use-a como a fonte da ligação.

<Window.Resources>
    <local:VersionManager x:Key="versionManager"/>
</Window.Resources>
...

<TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>

Esta resposta é mais apropriada ao meu caso, porque não quero introduzir DependencyObject na minha classe de origem. Obrigado pela dica!
— Anthony Brien

6
Observe que permitirá que sua caixa de texto envie o valor de volta à propriedade estática, mas não atualizará a caixa de texto quando o valor de origem for alterado.
— Adam Sills

1
Tudo bem, eu só precisava da ligação da caixa de texto para a Fonte neste caso. Se quiser que a ligação funcione de outra maneira, conheço a necessidade de um destes métodos: INotifyPropertyChanged, <PropertyName> Evento alterado ou propriedade de dependência.
— Anthony Brien

1
Nota: Esta solução não funcionará em uma situação MVVM, pois geralmente você não tem acesso aos tipos de objetos aos quais está vinculando.
— Antony Woods

@ Thomas Eu adoraria fazer isso funcionar para mim, mas não posso. Eu postei o meu dilema como outra pergunta aqui: stackoverflow.com/questions/34656670/...
— Andrew Simpson

107

Você não pode se ligar a uma estática assim. Não há como a infraestrutura de ligação ser notificada de atualizações, pois não há DependencyObject(ou instância de objeto que implemente INotifyPropertyChanged) envolvida.

Se esse valor não mudar, apenas abandone a ligação e use x:Staticdiretamente dentro da Textpropriedade. Defina appabaixo como o local do espaço para nome (e montagem) da classe VersionManager.

<TextBox Text="{x:Static app:VersionManager.FilterString}" />

Se o valor mudar, sugiro criar um singleton para conter o valor e vincular a ele.

Um exemplo do singleton:

public class VersionManager : DependencyObject {
    public static readonly DependencyProperty FilterStringProperty =
        DependencyProperty.Register( "FilterString", typeof( string ),
        typeof( VersionManager ), new UIPropertyMetadata( "no version!" ) );
    public string FilterString {
        get { return (string) GetValue( FilterStringProperty ); }
        set { SetValue( FilterStringProperty, value ); }
    }

    public static VersionManager Instance { get; private set; }

    static VersionManager() {
        Instance = new VersionManager();
    }
}
<TextBox Text="{Binding Source={x:Static local:VersionManager.Instance},
                        Path=FilterString}"/>

5
Realmente? Consegui vincular o Int32.MaxValue estático, que é muito semelhante ao meu exemplo: <TextBox Text = {Binding Source = {x: Static sys: Int32.MaxValue}, Mode = OneWay} "/> trabalhar porque é um caminho?
— Anthony Brien

2
Sim, qualquer ligação bidirecional requer um valor de propriedade Path na ligação. A origem precisa ser um objeto que contenha a propriedade especificada por Path. A especificação do OneWay remove essa restrição.
— 236 Adam Sills

Sinto muito pela atualização tardia, mas atualizei a resposta acima com uma amostra.
— Adam Sills

Existe uma maneira de vincular uma sequência estática. Eu tenho uma ligação múltipla e uma das entradas é uma string fixa.
— Nitin Chaudhari

39

No .NET 4.5, é possível vincular a propriedades estáticas, leia mais

Você pode usar propriedades estáticas como a fonte de uma ligação de dados. O mecanismo de ligação de dados reconhece quando o valor da propriedade é alterado se um evento estático for gerado. Por exemplo, se a classe SomeClass definir uma propriedade estática chamada MyProperty, SomeClass poderá definir um evento estático que será gerado quando o valor de MyProperty for alterado. O evento estático pode usar uma das seguintes assinaturas:

public static event EventHandler MyPropertyChanged; 
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged; 

Observe que, no primeiro caso, a classe expõe um evento estático chamado PropertyNameChanged que passa EventArgs para o manipulador de eventos. No segundo caso, a classe expõe um evento estático chamado StaticPropertyChanged que passa PropertyChangedEventArgs ao manipulador de eventos. Uma classe que implementa a propriedade estática pode optar por gerar notificações de alteração de propriedade usando um dos métodos.


Aqui está o link, caso alguém queira ler mais. A Microsoft retirou o arquivo, mas está no arquivo da web aqui. web.archive.org/web/20131129053934/http://msdn.microsoft.com/…
— C. Tewalt

Essa resposta me apontou na direção certa, mas ainda demorou um pouco para descobrir os detalhes sem um exemplo. Eu escrevi um exemplo com base no código original.
— Matt

13

No WPF 4.5, é possível vincular diretamente às propriedades estáticas e atualizar automaticamente quando a propriedade é alterada. Você precisa conectar manualmente um evento de alteração para acionar as atualizações de ligação.

public class VersionManager
{
    private static String _filterString;        

    /// <summary>
    /// A static property which you'd like to bind to
    /// </summary>
    public static String FilterString
    {
        get
        {
            return _filterString;
        }

        set
        {
            _filterString = value;

            // Raise a change event
            OnFilterStringChanged(EventArgs.Empty);
        }
    }

    // Declare a static event representing changes to your static property
    public static event EventHandler FilterStringChanged;

    // Raise the change event through this static method
    protected static void OnFilterStringChanged(EventArgs e)
    {
        EventHandler handler = FilterStringChanged;

        if (handler != null)
        {
            handler(null, e);
        }
    }

    static VersionManager()
    {
        // Set up an empty event handler
        FilterStringChanged += (sender, e) => { return; };
    }

}

Agora você pode vincular sua propriedade estática como qualquer outra:

<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>

1
A VersionManagerclasse pode ser estática e tudo ainda funciona. Observe as chaves na definição de caminho Path=(local:VersionManager.FilterString). Alguém sabe por que eles são realmente necessários?
— chviLadislav 26/09/19

2
Os chavetas na definição do caminho são necessários porque a propriedade é estática, veja aqui
— chviLadislav

11

Pode haver duas maneiras / sintaxe para vincular uma staticpropriedade. Se p for uma staticpropriedade na classe MainWindow, bindingfor textboxserá:

1

<TextBox Text="{x:Static local:MainWindow.p}" />

2)

<TextBox Text="{Binding Source={x:Static local:MainWindow.p},Mode=OneTime}" />

9

Você pode usar ObjectDataProviderclasse e sua MethodNamepropriedade. Pode ficar assim:

<Window.Resources>
   <ObjectDataProvider x:Key="versionManager" ObjectType="{x:Type VersionManager}" MethodName="get_FilterString"></ObjectDataProvider>
</Window.Resources>

O provedor de dados do objeto declarado pode ser usado assim:

<TextBox Text="{Binding Source={StaticResource versionManager}}" />

8

Se você estiver usando recursos locais, pode consultá-los como abaixo:

<TextBlock Text="{Binding Source={x:Static prop:Resources.PerUnitOfMeasure}}" TextWrapping="Wrap" TextAlignment="Center"/>

3

Variante correta para .NET 4.5 +

Código c #

public class VersionManager
{
    private static string filterString;

    public static string FilterString
    {
        get => filterString;
        set
        {
            if (filterString == value)
                return;

            filterString = value;

            StaticPropertyChanged?.Invoke(null, FilterStringPropertyEventArgs);
        }
    }

    private static readonly PropertyChangedEventArgs FilterStringPropertyEventArgs = new PropertyChangedEventArgs (nameof(FilterString));
    public static event PropertyChangedEventHandler StaticPropertyChanged;
}

Ligação XAML (atenção aos chavetas são (), não {})

<TextBox Text="{Binding Path=(yournamespace:VersionManager.FilterString)}" />

Fez uma pequena alteração no seu código para chamar corretamente o EventHandler.
— Mark A. Donohoe 8/18

Tentei muitas soluções diferentes e esta funcionou. O PropertyChangedEventHandler é o que funcionou para mim. Felicidades.
— Mgamerz 24/09/19

2

Veja o meu projeto CalcBinding , que fornece a você expressões complexas no valor da propriedade Path, incluindo propriedades estáticas, propriedades de origem, Math e outras. Então, você pode escrever isso:

<TextBox Text="{c:Binding local:VersionManager.FilterString}"/>

Boa sorte!


0

Essas respostas são boas se você deseja seguir boas convenções, mas o OP queria algo simples , que é o que eu também queria, em vez de lidar com os padrões de design da GUI. Se tudo o que você quer é ter uma string em um aplicativo GUI básico, você pode atualizar ad-hoc sem nada sofisticado, basta acessá-lo diretamente na sua fonte C #.

Digamos que você tenha um aplicativo WPF realmente básico, o MainWindow XAML assim,

<Window x:Class="MyWPFApp.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:MyWPFApp"
            mc:Ignorable="d"
            Title="MainWindow"
            Height="200"
            Width="400"
            Background="White" >
    <Grid>
        <TextBlock x:Name="textBlock"                   
                       Text=".."
                       HorizontalAlignment="Center"
                       VerticalAlignment="Top"
                       FontWeight="Bold"
                       FontFamily="Helvetica"
                       FontSize="16"
                       Foreground="Blue" Margin="0,10,0,0"
             />
        <Button x:Name="Find_Kilroy"
                    Content="Poke Kilroy"
                    Click="Button_Click_Poke_Kilroy"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    FontFamily="Helvetica"
                    FontWeight="Bold"
                    FontSize="14"
                    Width="280"
            />
    </Grid>
</Window>

Isso será algo como isto:

insira a descrição da imagem aqui

Na fonte do MainWindow XAML, você pode ter algo parecido com isto, onde tudo o que estamos fazendo é alterar o valor diretamente por meio textBlock.Textda funcionalidade get/ set:

using System.Windows;

namespace MyWPFApp
{
    public partial class MainWindow : Window
    {
        public MainWindow() { InitializeComponent(); }

        private void Button_Click_Poke_Kilroy(object sender, RoutedEventArgs e)
        {
            textBlock.Text = "              \\|||/\r\n" +
                             "              (o o) \r\n" +
                             "----ooO- (_) -Ooo----";
        }
    }
}

Então, quando você acionar o evento de clique clicando no botão, pronto! Kilroy aparece :)

insira a descrição da imagem aqui


0

Outra solução é criar uma classe normal que implemente o PropertyChanger assim

public class ViewProps : PropertyChanger
{
    private string _MyValue = string.Empty;
    public string MyValue
    {
        get { 
            return _MyValue
        }
        set
        {
            if (_MyValue == value)
            {
                return;
            }
            SetProperty(ref _MyValue, value);
        }
    }
}

Em seguida, crie uma instância estática da classe em algum lugar que você não

public class MyClass
{
    private static ViewProps _ViewProps = null;
    public static ViewProps ViewProps
    {
        get
        {
            if (_ViewProps == null)
            {
                _ViewProps = new ViewProps();
            }
            return _ViewProps;
        }
    }
}

E agora use-o como propriedade estática

<TextBlock  Text="{x:Bind local:MyClass.ViewProps.MyValue, Mode=OneWay}"  />

E aqui está a implementação do PropertyChanger, se necessário

public abstract class PropertyChanger : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (object.Equals(storage, value)) return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

-1

Resposta mais simples (.net 4.5 e posterior):

    static public event EventHandler FilterStringChanged;
    static string _filterString;
    static public string FilterString
    {
        get { return _filterString; }
        set
        {
            _filterString= value;
            FilterStringChanged?.Invoke(null, EventArgs.Empty);
        }
    }

e XAML:

    <TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>

Não negligencie os colchetes

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.