Liaison à une propriété statique

168

J'ai du mal à lier une simple propriété de chaîne statique à un TextBox.

Voici la classe avec la propriété static:

public class VersionManager
{
    private static string filterString;

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

Dans mon xaml, je veux juste lier cette propriété statique à un TextBox:

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

Tout se compile, mais au moment de l'exécution, j'obtiens l'exception suivante:

Impossible de convertir la valeur de l'attribut «Source» en objet de type «System.Windows.Markup.StaticExtension». Erreur à l'objet 'System.Windows.Data.Binding' dans le fichier de balisage 'BurnDisk; component / selectversionpagefunction.xaml' Ligne 57 Position 29.

Une idée de ce que je fais mal?

Anthony Brien
la source

Réponses:

169

Si la liaison doit être bidirectionnelle, vous devez fournir un chemin.

Il existe une astuce pour effectuer une liaison bidirectionnelle sur une propriété statique, à condition que la classe ne soit pas statique: déclarez une instance factice de la classe dans les ressources et utilisez-la comme source de la liaison.

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

<TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>
Thomas Levesque
la source
Cette réponse est plus appropriée à mon cas car je ne souhaite pas introduire DependencyObject dans ma classe source. Merci pour le conseil!
Anthony Brien
6
Notez que cela permettra à votre zone de texte de repousser la valeur dans la propriété statique, mais ne mettra pas à jour la zone de texte lorsque la valeur source change.
Adam Sills le
1
C'est bien, j'avais juste besoin de la liaison de la zone de texte à la source dans ce cas. Si je veux que la liaison fonctionne dans l'autre sens, je suis conscient de la nécessité de l'une de ces méthodes: INotifyPropertyChanged, <PropertyName> Événement modifié ou propriété de dépendance.
Anthony Brien
1
Remarque: cette solution ne fonctionnera pas dans une situation MVVM, car vous n'avez généralement pas accès aux types d'objets auxquels vous vous liez.
Antony Woods
@thomas J'adorerais que cela fonctionne pour moi mais je ne peux pas. J'ai posté mon dilemme comme une autre question ici: stackoverflow.com/questions/34656670/…
Andrew Simpson
107

Vous ne pouvez pas vous lier à une statique comme celle-là. Il n'y a aucun moyen pour l'infrastructure de liaison d'être notifiée des mises à jour, car aucune DependencyObjectinstance (ou instance d'objet implémentée INotifyPropertyChanged) n'est impliquée.

Si cette valeur ne change pas, abandonnez simplement la liaison et utilisez x:Staticdirectement à l'intérieur de la Textpropriété. Définissez appci-dessous l'emplacement de l'espace de noms (et de l'assembly) de la classe VersionManager.

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

Si la valeur change, je suggère de créer un singleton pour contenir la valeur et s'y lier.

Un exemple du 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
la source
5
Vraiment? J'ai pu me lier à la statique Int32.MaxValue qui est très similaire à mon exemple: <TextBox Text = {Binding Source = {x: Static sys: Int32.MaxValue}, Mode = OneWay} "/> Est-ce que travailler parce que c'est à sens unique?
Anthony Brien
2
Ouais, toute liaison bidirectionnelle nécessite une valeur de propriété Path sur la liaison. La source doit être un objet contenant la propriété spécifiée par Path. La spécification de OneWay supprime cette restriction.
Adam Sills
Aussi, désolé pour la mise à jour tardive, mais j'ai mis à jour la réponse ci-dessus avec un échantillon.
Adam Sills
Existe-t-il un moyen de lier une chaîne statique. J'ai un mutibinding et l'une des entrées est une chaîne fixe.
Nitin Chaudhari
39

Dans .NET 4.5, il est possible de se lier à des propriétés statiques, en savoir plus

Vous pouvez utiliser des propriétés statiques comme source d'une liaison de données. Le moteur de liaison de données reconnaît quand la valeur de la propriété change si un événement statique est déclenché. Par exemple, si la classe SomeClass définit une propriété statique appelée MyProperty, SomeClass peut définir un événement statique qui est déclenché lorsque la valeur de MyProperty change. L'événement statique peut utiliser l'une des signatures suivantes:

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

Notez que dans le premier cas, la classe expose un événement statique nommé PropertyNameChanged qui transmet EventArgs au gestionnaire d'événements. Dans le second cas, la classe expose un événement statique nommé StaticPropertyChanged qui transmet PropertyChangedEventArgs au gestionnaire d'événements. Une classe qui implémente la propriété statique peut choisir de déclencher des notifications de modification de propriété à l'aide de l'une ou l'autre méthode.

Jowen
la source
Voici le lien au cas où quelqu'un voudrait en savoir plus. Microsoft l'a retiré, mais c'est sur l'archive Web ici. web.archive.org/web/20131129053934/http
//msdn.microsoft.com/...
Cette réponse m'a orienté dans la bonne direction, mais il a encore fallu un certain temps pour élaborer les détails sans exemple. J'ai écrit un exemple basé sur le code d'origine.
Matt
13

À partir de WPF 4.5, vous pouvez vous lier directement aux propriétés statiques et mettre à jour automatiquement la liaison lorsque votre propriété est modifiée. Vous devez câbler manuellement un événement de modification pour déclencher les mises à jour de liaison.

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

}

Vous pouvez maintenant lier votre propriété statique comme n'importe quelle autre:

<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
Mat
la source
1
La VersionManagerclasse peut être statique et tout fonctionne toujours. Notez les accolades dans la définition du chemin Path=(local:VersionManager.FilterString). Est-ce que quelqu'un sait pourquoi ils sont réellement nécessaires?
chviLadislav
2
Les accolades dans la définition du chemin sont nécessaires car la propriété est statique, voir ici
chviLadislav
11

Il peut y avoir deux manières / syntaxe de lier une staticpropriété. Si p est une staticpropriété de la classe MainWindow, alors bindingfor textboxsera:

1.

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

2.

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

Vous pouvez utiliser la ObjectDataProviderclasse et sa MethodNamepropriété. Cela peut ressembler à ceci:

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

Le fournisseur de données d'objets déclaré peut être utilisé comme ceci:

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

Si vous utilisez des ressources locales, vous pouvez vous y référer comme ci-dessous:

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

Variante appropriée pour .NET 4.5 +

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

Liaison XAML (attention aux accolades qu'ils sont (), pas {})

<TextBox Text="{Binding Path=(yournamespace:VersionManager.FilterString)}" />
Alexei Shcherbakov
la source
A apporté une légère modification à votre code pour appeler correctement le EventHandler.
Mark A. Donohoe
J'ai essayé beaucoup de solutions différentes et celle-ci a fonctionné. Le PropertyChangedEventHandler est ce qui a fonctionné pour moi. À votre santé.
Mgamerz
2

Regardez mon projet CalcBinding , qui vous permet d'écrire des expressions complexes dans la valeur de la propriété Path, y compris les propriétés statiques, les propriétés source, Math et autres. Donc, vous pouvez écrire ceci:

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

Bonne chance!

Alex141
la source
0

Ces réponses sont toutes bonnes si vous voulez suivre de bonnes conventions mais que l'OP voulait quelque chose de simple , ce que je voulais aussi au lieu de traiter des modèles de conception d'interface graphique. Si tout ce que vous voulez faire est d'avoir une chaîne dans une application graphique de base, vous pouvez mettre à jour ad hoc sans rien de compliqué, vous pouvez simplement y accéder directement dans votre source C #.

Disons que vous avez une application WPF vraiment basique MainWindow XAML comme celle-ci,

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

Cela ressemblera à quelque chose comme ceci:

entrez la description de l'image ici

Dans la source de votre MainWindow XAML, vous pourriez avoir quelque chose comme ceci où tout ce que nous faisons pour changer la valeur directement via textBlock.Text's get/ setfeature:

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

Ensuite, lorsque vous déclenchez cet événement de clic en cliquant sur le bouton, le tour est joué! Kilroy apparaît :)

entrez la description de l'image ici

kayleeFrye_onDeck
la source
0

Une autre solution consiste à créer une classe normale qui implémente PropertyChanger comme ceci

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

Ensuite, créez une instance statique de la classe quelque part que vous ne voudrez pas

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

Et maintenant, utilisez-le comme propriété statique

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

Et voici l'implémentation de PropertyChanger si nécessaire

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));
    }
}
néosonne
la source
-1

Réponse la plus simple (.net 4.5 et versions ultérieures):

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

et XAML:

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

Ne négligez pas les parenthèses

Sean
la source