Associazione a proprietà statiche


168

Sto facendo fatica a collegare una semplice proprietà di stringa statica a una TextBox.

Ecco la classe con la proprietà statica:

public class VersionManager
{
    private static string filterString;

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

Nel mio xaml, voglio solo associare questa proprietà statica a una TextBox:

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

Tutto viene compilato, ma in fase di esecuzione ottengo la seguente eccezione:

Impossibile convertire il valore nell'attributo 'Origine' in oggetto di tipo 'System.Windows.Markup.StaticExtension'. Errore nell'oggetto "System.Windows.Data.Binding" nel file di markup "BurnDisk; component / selectversionpagefunction.xaml" Riga 57 Posizione 29.

Hai idea di cosa sto facendo di sbagliato?

Risposte:


168

Se l'associazione deve essere bidirezionale, è necessario fornire un percorso. Esiste un trucco per eseguire l'associazione bidirezionale su una proprietà statica, a condizione che la classe non sia statica: dichiarare un'istanza fittizia della classe nelle risorse e utilizzarla come origine dell'associazione.

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

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

Questa risposta è più appropriata al mio caso perché non voglio introdurre DependencyObject alla mia classe di origine. Grazie per il consiglio!
Anthony Brien,

6
Si noti che consentirà alla casella di testo di reinserire il valore nella proprietà statica, ma non aggiornerà la casella di testo quando il valore di origine cambia.
Adam Sills,

1
Va bene, in questo caso avevo solo bisogno del legame dalla casella di testo alla sorgente. Se voglio che l'associazione funzioni nell'altro modo, sono consapevole della necessità di uno di questi metodi: INotifyPropertyChanged, <PropertyName> Modificato evento o proprietà di dipendenza.
Anthony Brien,

1
Nota: questa soluzione non funzionerà in una situazione MVVM, poiché in genere non si ha accesso ai tipi di oggetti a cui si sta vincolando.
Antony Woods,

@thomas Mi piacerebbe farlo funzionare per me, ma non posso. Ho pubblicato il mio dilemma come un'altra domanda qui: stackoverflow.com/questions/34656670/…
Andrew Simpson

107

Non puoi legare a una statica del genere. Non è possibile che l'infrastruttura di binding venga informata degli aggiornamenti poiché non sono coinvolti DependencyObject(o istanze di oggetti che implementano INotifyPropertyChanged).

Se quel valore non cambia, basta abbandonare l'associazione e utilizzare x:Staticdirettamente all'interno della Textproprietà. Definire di appseguito la posizione dello spazio dei nomi (e dell'assembly) della classe VersionManager.

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

Se il valore cambia, suggerirei di creare un singleton per contenere il valore e associarlo a quello.

Un esempio del 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
Veramente? Sono stato in grado di associare Int32.MaxValue statico che è molto simile al mio esempio: <TextBox Text = {Binding Source = {x: Sys statico: Int32.MaxValue}, Mode = OneWay} "/> È quello lavorando perché è un modo?
Anthony Brien,

2
Sì, qualsiasi associazione bidirezionale richiede un valore della proprietà Path sull'associazione. L'origine deve essere un oggetto che contiene la proprietà specificata da Path. Specificando OneWay si rimuove tale restrizione.
Adam Sills,

Inoltre, mi scuso per l'aggiornamento in ritardo, ma ho aggiornato la risposta sopra con un campione.
Adam Sills,

C'è un modo per associare una stringa statica. Ho un mutibinding e uno degli input è una stringa fissa.
Nitin Chaudhari,

39

In .NET 4.5 è possibile associare proprietà statiche, leggi di più

È possibile utilizzare le proprietà statiche come origine di un'associazione dati. Il motore di associazione dei dati riconosce quando il valore della proprietà cambia se viene generato un evento statico. Ad esempio, se la classe SomeClass definisce una proprietà statica chiamata MyProperty, SomeClass può definire un evento statico che viene generato quando cambia il valore di MyProperty. L'evento statico può utilizzare una delle seguenti firme:

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

Si noti che nel primo caso, la classe espone un evento statico denominato PropertyNameChanged che passa EventArgs al gestore eventi. Nel secondo caso, la classe espone un evento statico denominato StaticPropertyChanged che passa PropertyChangedEventArgs al gestore eventi. Una classe che implementa la proprietà statica può scegliere di aumentare le notifiche di modifica della proprietà usando entrambi i metodi.


Ecco il link nel caso in cui qualcuno volesse leggere di più. Microsoft l'ha rimosso, ma è nell'archivio web qui. web.archive.org/web/20131129053934/http://msdn.microsoft.com/…
C. Tewalt,

Questa risposta mi ha indicato la giusta direzione, ma ci è voluto ancora un po 'di tempo per elaborare i dettagli senza un esempio. Ho scritto un esempio basato sul codice originale.
Matt,

13

A partire da WPF 4.5 è possibile associare direttamente a proprietà statiche e fare in modo che l'associazione si aggiorni automaticamente quando si modifica la proprietà. È necessario collegare manualmente un evento di modifica per attivare gli aggiornamenti dell'associazione.

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; };
    }

}

Ora puoi associare la tua proprietà statica come qualsiasi altra:

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

1
La VersionManagerclasse può essere statica e tutto funziona ancora. Nota le parentesi graffe nella definizione del percorso Path=(local:VersionManager.FilterString). Qualcuno sa perché sono effettivamente necessari?
ChviLadislav,

2
Le parentesi graffe nella definizione del percorso sono necessarie perché la proprietà è statica, vedere qui
chviLadislav

11

Potrebbero esserci due modi / sintassi per associare una staticproprietà. Se p è una staticproprietà nella classe MainWindow, allora bindingper textboxsarà:

1.

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

2.

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

9

Puoi usare la ObjectDataProviderclasse e la sua MethodNameproprietà. Può assomigliare a questo:

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

Il fornitore di dati oggetto dichiarato può essere utilizzato in questo modo:

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

8

Se si utilizzano risorse locali, è possibile fare riferimento a queste come di seguito:

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

3

Variante giusta per .NET 4.5 +

Codice 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;
}

Rilegatura XAML (attenzione alle parentesi graffe che sono (), non {})

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

Apportato una leggera modifica al codice per chiamare correttamente EventHandler.
Mark A. Donohoe,

Ho provato molte soluzioni diverse e questa ha funzionato. PropertyChangedEventHandler è ciò che ha funzionato per me. Saluti.
Mgamerz,

2

Guarda il mio progetto CalcBinding , che ti fornisce la scrittura di espressioni complesse nel valore della proprietà Path, tra cui proprietà statiche, proprietà di origine, matematica e altro. Quindi, puoi scrivere questo:

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

In bocca al lupo!


0

Queste risposte sono tutte buone se vuoi seguire buone convenzioni, ma l'OP voleva qualcosa di semplice , che è anche quello che volevo invece di occuparmi dei modelli di progettazione della GUI. Se tutto quello che vuoi fare è avere una stringa in un'app GUI di base che puoi aggiornare ad hoc senza nulla di speciale, puoi semplicemente accedervi direttamente nel tuo sorgente C #.

Supponiamo che tu abbia un'app WPF davvero semplice MainWindow XAML come questa,

<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>

Sarà simile a questo:

inserisci qui la descrizione dell'immagine

Nella fonte XAML di MainWindow, potresti avere qualcosa del genere in cui tutto ciò che facciamo nel modificare il valore direttamente tramite textBlock.Textla funzionalità 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----";
        }
    }
}

Quindi quando attivi quell'evento click facendo clic sul pulsante, voilà! Kilroy appare :)

inserisci qui la descrizione dell'immagine


0

Un'altra soluzione è quella di creare una classe normale che implementa PropertyChanger in questo modo

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

Quindi crea un'istanza statica della classe da qualche parte che non vuoi

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

E ora usalo come proprietà statica

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

Ed ecco l'implementazione di PropertyChanger, se necessario

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

Risposta più snella (.net 4.5 e successive):

    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)}"/>

Non trascurare le parentesi

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.