Bindung an statische Eigenschaft

168

Es fällt mir schwer, eine einfache statische Zeichenfolge an eine TextBox zu binden.

Hier ist die Klasse mit der statischen Eigenschaft:

public class VersionManager
{
    private static string filterString;

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

In meinem xaml möchte ich diese statische Eigenschaft nur an eine TextBox binden:

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

Alles wird kompiliert, aber zur Laufzeit erhalte ich die folgende Ausnahme:

Der Wert im Attribut 'Source' kann nicht in ein Objekt vom Typ 'System.Windows.Markup.StaticExtension' konvertiert werden. Fehler am Objekt 'System.Windows.Data.Binding' in der Markup-Datei 'BurnDisk; component / selectversionpagefunction.xaml' Zeile 57 Position 29.

Irgendeine Idee, was ich falsch mache?

Anthony Brien
quelle

Antworten:

168

Wenn die Bindung in beide Richtungen erfolgen muss, müssen Sie einen Pfad angeben. Es gibt einen Trick, eine statische Eigenschaft in beide Richtungen zu binden, vorausgesetzt, die Klasse ist nicht statisch: Deklarieren Sie eine Dummy-Instanz der Klasse in den Ressourcen und verwenden Sie sie als Quelle für die Bindung.

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

<TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>
Thomas Levesque
quelle
Diese Antwort passt besser zu meinem Fall, da ich DependencyObject nicht in meine Quellklasse einführen möchte. Danke für den Tipp!
Anthony Brien
6
Beachten Sie, dass Ihr Textfeld den Wert wieder in die statische Eigenschaft verschieben kann, das Textfeld jedoch nicht aktualisiert wird, wenn sich der Quellwert ändert.
Adam Sills
1
Das ist in Ordnung, ich brauchte in diesem Fall nur die Bindung vom Textfeld zur Quelle. Wenn die Bindung in die andere Richtung funktionieren soll, ist mir eine der folgenden Methoden erforderlich: INotifyPropertyChanged, <PropertyName> Geändertes Ereignis oder Abhängigkeitseigenschaft.
Anthony Brien
1
Hinweis: Diese Lösung funktioniert in einer MVVM-Situation nicht, da Sie im Allgemeinen keinen Zugriff auf die Typen der Objekte haben, an die Sie binden.
Antony Woods
@thomas Ich würde das gerne für mich arbeiten lassen, kann es aber nicht. Ich habe mein Dilemma als eine weitere Frage hier gepostet: stackoverflow.com/questions/34656670/…
Andrew Simpson
107

Sie können sich nicht an eine solche Statik binden. Es gibt keine Möglichkeit für die Bindungsinfrastruktur, über Aktualisierungen benachrichtigt zu werden, da keine DependencyObject(oder implementierte Objektinstanz INotifyPropertyChanged) beteiligt ist.

Wenn sich dieser Wert nicht ändert, lassen Sie die Bindung einfach fallen und verwenden Sie sie x:Staticdirekt in der TextEigenschaft. Definieren Sie appunten den Namespace- (und Assembly-) Speicherort der VersionManager-Klasse.

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

Wenn sich der Wert ändert, würde ich vorschlagen, einen Singleton zu erstellen, der den Wert enthält, und an diesen zu binden.

Ein Beispiel für den 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}"/>
Adam Sills
quelle
5
"Ja wirklich?" Ich konnte eine Bindung an den statischen Int32.MaxValue herstellen, der meinem Beispiel sehr ähnlich ist: <TextBox Text = {Bindungsquelle = {x: Statisches System: Int32.MaxValue}, Modus = OneWay} "/> Ist das so? Arbeiten, weil es eine Möglichkeit ist?
Anthony Brien
2
Ja, für jede bidirektionale Bindung ist ein Pfad-Eigenschaftswert für die Bindung erforderlich. Die Quelle muss ein Objekt sein, das die durch Pfad angegebene Eigenschaft enthält. Durch die Angabe von OneWay wird diese Einschränkung aufgehoben.
Adam Sills
Es tut mir auch leid für das späte Update, aber ich habe die obige Antwort mit einem Beispiel aktualisiert.
Adam Sills
Gibt es eine Möglichkeit, eine statische Zeichenfolge zu binden? Ich habe eine Mutibinding und eine der Eingaben ist eine feste Zeichenfolge.
Nitin Chaudhari
39

In .NET 4.5 ist es möglich, an statische Eigenschaften zu binden. Lesen Sie mehr

Sie können statische Eigenschaften als Quelle für eine Datenbindung verwenden. Die Datenbindungs-Engine erkennt, wenn sich der Wert der Eigenschaft ändert, wenn ein statisches Ereignis ausgelöst wird. Wenn die Klasse SomeClass beispielsweise eine statische Eigenschaft namens MyProperty definiert, kann SomeClass ein statisches Ereignis definieren, das ausgelöst wird, wenn sich der Wert von MyProperty ändert. Das statische Ereignis kann eine der folgenden Signaturen verwenden:

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

Beachten Sie, dass die Klasse im ersten Fall ein statisches Ereignis mit dem Namen PropertyNameChanged verfügbar macht, das EventArgs an den Ereignishandler übergibt. Im zweiten Fall macht die Klasse ein statisches Ereignis mit dem Namen StaticPropertyChanged verfügbar, das PropertyChangedEventArgs an den Ereignishandler übergibt. Eine Klasse, die die statische Eigenschaft implementiert, kann mithilfe beider Methoden Benachrichtigungen über Eigenschaftsänderungen auslösen.

Jowen
quelle
Hier ist der Link für den Fall, dass jemand mehr lesen möchte. Microsoft hat es entfernt, aber es ist hier im Webarchiv. web.archive.org/web/20131129053934/http://msdn.microsoft.com/…
C. Tewalt
Diese Antwort wies mich in die richtige Richtung, aber es dauerte noch eine Weile, bis ich die Details ohne Beispiel herausgearbeitet hatte. Ich habe ein Beispiel basierend auf dem Originalcode geschrieben.
Matt
13

Ab WPF 4.5 können Sie direkt an statische Eigenschaften binden und die Bindung automatisch aktualisieren lassen, wenn Ihre Eigenschaft geändert wird. Sie müssen ein Änderungsereignis manuell verkabeln, um die Bindungsaktualisierungen auszulösen.

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

}

Sie können Ihre statische Eigenschaft jetzt wie jede andere binden:

<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
Matt
quelle
1
Die VersionManagerKlasse kann statisch sein und alles funktioniert noch. Beachten Sie die geschweiften Klammern in der Pfaddefinition Path=(local:VersionManager.FilterString). Weiß jemand, warum sie tatsächlich gebraucht werden?
chviLadislav
2
Die geschweiften Klammern in der
Pfaddefinition
11

Es gibt zwei Möglichkeiten / Syntax, um eine staticEigenschaft zu binden . Wenn p eine staticEigenschaft in der Klasse ist MainWindow, lautet bindingfor textbox:

1.

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

2.

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

Sie können ObjectDataProviderclass und seine MethodNameEigenschaft verwenden. Es kann so aussehen:

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

Der deklarierte Objektdatenanbieter kann folgendermaßen verwendet werden:

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

Wenn Sie lokale Ressourcen verwenden, können Sie diese wie folgt verwenden:

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

Richtige Variante für .NET 4.5 +

C # -Code

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

XAML-Bindung (Aufmerksamkeit auf geschweifte Klammern (), nicht {})

<TextBox Text="{Binding Path=(yournamespace:VersionManager.FilterString)}" />
Alexei Shcherbakov
quelle
Nehmen Sie eine geringfügige Änderung an Ihrem Code vor, um den EventHandler ordnungsgemäß aufzurufen.
Mark A. Donohoe
Versuchte viele verschiedene Lösungen und diese funktionierte. Der PropertyChangedEventHandler hat bei mir funktioniert. Prost.
Mgamerz
2

Schauen Sie sich mein Projekt CalcBinding an , mit dem Sie komplexe Ausdrücke in den Eigenschaftenwert Path schreiben können, einschließlich statischer Eigenschaften, Quelleneigenschaften, Mathematik und anderer. Sie können also Folgendes schreiben:

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

Viel Glück!

Alex141
quelle
0

Diese Antworten sind alle gut, wenn Sie gute Konventionen befolgen möchten, aber das OP wollte etwas Einfaches , was ich auch wollte, anstatt mich mit GUI-Entwurfsmustern zu befassen. Wenn Sie lediglich eine Zeichenfolge in einer einfachen GUI-App haben möchten, die Sie ad-hoc aktualisieren können, ohne etwas Besonderes zu tun, können Sie direkt in Ihrer C # -Quelle darauf zugreifen.

Angenommen, Sie haben eine wirklich einfache WPF-App wie MainWindow XAML.

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

Das wird ungefähr so ​​aussehen:

Geben Sie hier die Bildbeschreibung ein

In der Quelle Ihres MainWindow XAML könnten Sie so etwas haben, wo wir alles tun, um den Wert direkt über textBlock.Text's get/ setFunktionalität zu ändern :

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

Wenn Sie dann dieses Klickereignis durch Klicken auf die Schaltfläche auslösen, wird voila! Kilroy erscheint :)

Geben Sie hier die Bildbeschreibung ein

kayleeFrye_onDeck
quelle
0

Eine andere Lösung besteht darin, eine normale Klasse zu erstellen, die PropertyChanger wie folgt implementiert

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

Erstellen Sie dann eine statische Instanz der Klasse an einer Stelle, die Sie nicht möchten

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

Und jetzt verwenden Sie es als statische Eigenschaft

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

Und hier ist bei Bedarf die Implementierung von PropertyChanger

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));
    }
}
Neosonne
quelle
-1

Schlankste Antwort (.net 4.5 und höher):

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

und XAML:

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

Vernachlässigen Sie nicht die Klammern

Sean
quelle